├── weatherapp-ng ├── src │ ├── app │ │ ├── components │ │ │ ├── profile │ │ │ │ ├── profile.component.css │ │ │ │ ├── profile.component.html │ │ │ │ ├── profile.component.ts │ │ │ │ └── profile.component.spec.ts │ │ │ ├── register │ │ │ │ ├── register.component.css │ │ │ │ ├── register.component.html │ │ │ │ ├── register.component.spec.ts │ │ │ │ └── register.component.ts │ │ │ ├── login │ │ │ │ ├── login.component.css │ │ │ │ ├── login.component.html │ │ │ │ ├── login.component.ts │ │ │ │ └── login.component.spec.ts │ │ │ ├── subnav │ │ │ │ ├── subnav.component.css │ │ │ │ ├── subnav.component.html │ │ │ │ ├── subnav.component.ts │ │ │ │ └── subnav.component.spec.ts │ │ │ ├── home │ │ │ │ ├── home.component.css │ │ │ │ ├── home.component.ts │ │ │ │ ├── home.component.html │ │ │ │ └── home.component.spec.ts │ │ │ ├── search │ │ │ │ ├── search.component.html │ │ │ │ ├── search.component.css │ │ │ │ ├── search.component.ts │ │ │ │ └── search.component.spec.ts │ │ │ ├── navbar │ │ │ │ ├── navbar.component.css │ │ │ │ ├── navbar.component.ts │ │ │ │ ├── navbar.component.html │ │ │ │ └── navbar.component.spec.ts │ │ │ ├── dashboard │ │ │ │ ├── dashboard.component.css │ │ │ │ ├── dashboard.component.spec.ts │ │ │ │ ├── dashboard.component.html │ │ │ │ └── dashboard.component.ts │ │ │ └── weatherdetails │ │ │ │ ├── weatherdetails.component.spec.ts │ │ │ │ ├── weatherdetails.component.html │ │ │ │ ├── weatherdetails.component.css │ │ │ │ └── weatherdetails.component.ts │ │ ├── app.component.html │ │ ├── app.component.css │ │ ├── app.component.ts │ │ ├── services │ │ │ ├── date.service.spec.ts │ │ │ ├── auth.service.spec.ts │ │ │ ├── weather.service.spec.ts │ │ │ ├── validate.service.spec.ts │ │ │ ├── savedlocations.service.spec.ts │ │ │ ├── validate.service.ts │ │ │ ├── date.service.ts │ │ │ ├── weather.service.ts │ │ │ ├── savedlocations.service.ts │ │ │ └── auth.service.ts │ │ ├── guards │ │ │ └── auth.guard.ts │ │ ├── app.component.spec.ts │ │ └── app.module.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── assets │ │ ├── x.png │ │ ├── mg.png │ │ ├── sun.png │ │ ├── clouds.jpg │ │ ├── globe1.png │ │ ├── globe1a.png │ │ ├── forecasticons.png │ │ ├── weathericons.jpg │ │ └── weathericons.png │ ├── favicon.ico │ ├── typings.d.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ ├── main.ts │ ├── index.html │ ├── test.ts │ ├── polyfills.ts │ └── styles.css ├── e2e │ ├── app.po.ts │ ├── tsconfig.e2e.json │ └── app.e2e-spec.ts ├── tsconfig.json ├── .gitignore ├── protractor.conf.js ├── karma.conf.js ├── README.md ├── .angular-cli.json ├── package.json └── tslint.json ├── public ├── assets │ ├── x.png │ ├── sun.png │ ├── clouds.jpg │ ├── globe1.png │ ├── globe1a.png │ ├── weathericons.jpg │ └── weathericons_ORIG.jpg ├── favicon.ico ├── clouds.57e9f1b424ed9c108c3c.jpg ├── weathericons.fb5d64949dd59dff391e.jpg ├── glyphicons-halflings-regular.e18bbf611f2a2e43afc0.ttf ├── glyphicons-halflings-regular.f4769f9bdb7466be6508.eot ├── glyphicons-halflings-regular.448c34a56d699c29117a.woff2 ├── glyphicons-halflings-regular.fa2772327f55d8198301.woff ├── index.html ├── inline.bundle.js.map ├── inline.bundle.js └── main.bundle.js.map ├── config ├── database.js └── passport.js ├── test ├── appTest.js └── usersTest.js ├── package.json ├── README.md ├── app.js ├── models └── user.js └── routes └── users.js /weatherapp-ng/src/app/components/profile/profile.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/register/register.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/x.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/favicon.ico -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/login/login.component.css: -------------------------------------------------------------------------------- 1 | form { 2 | margin-top: 25px; 3 | } -------------------------------------------------------------------------------- /public/assets/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/sun.png -------------------------------------------------------------------------------- /public/assets/clouds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/clouds.jpg -------------------------------------------------------------------------------- /public/assets/globe1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/globe1.png -------------------------------------------------------------------------------- /public/assets/globe1a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/globe1a.png -------------------------------------------------------------------------------- /weatherapp-ng/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /public/assets/weathericons.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/weathericons.jpg -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/x.png -------------------------------------------------------------------------------- /weatherapp-ng/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/favicon.ico -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/mg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/mg.png -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/sun.png -------------------------------------------------------------------------------- /public/assets/weathericons_ORIG.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/assets/weathericons_ORIG.jpg -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/clouds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/clouds.jpg -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/globe1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/globe1.png -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/globe1a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/globe1a.png -------------------------------------------------------------------------------- /public/clouds.57e9f1b424ed9c108c3c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/clouds.57e9f1b424ed9c108c3c.jpg -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/forecasticons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/forecasticons.png -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/weathericons.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/weathericons.jpg -------------------------------------------------------------------------------- /weatherapp-ng/src/assets/weathericons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/weatherapp-ng/src/assets/weathericons.png -------------------------------------------------------------------------------- /public/weathericons.fb5d64949dd59dff391e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/weathericons.fb5d64949dd59dff391e.jpg -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/subnav/subnav.component.css: -------------------------------------------------------------------------------- 1 | nav { 2 | margin-top: 15px; 3 | } 4 | 5 | nav a { 6 | margin-left: 7px; 7 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /public/glyphicons-halflings-regular.e18bbf611f2a2e43afc0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/glyphicons-halflings-regular.e18bbf611f2a2e43afc0.ttf -------------------------------------------------------------------------------- /public/glyphicons-halflings-regular.f4769f9bdb7466be6508.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/glyphicons-halflings-regular.f4769f9bdb7466be6508.eot -------------------------------------------------------------------------------- /public/glyphicons-halflings-regular.448c34a56d699c29117a.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/glyphicons-halflings-regular.448c34a56d699c29117a.woff2 -------------------------------------------------------------------------------- /public/glyphicons-halflings-regular.fa2772327f55d8198301.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/weatherapp/master/public/glyphicons-halflings-regular.fa2772327f55d8198301.woff -------------------------------------------------------------------------------- /weatherapp-ng/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 | 9 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | database: 'mongodb://localhost:27017/weatherapp', 3 | testdatabase: 'mongodb://localhost:27017/weatherapptest', 4 | secret: 'molokova' 5 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/subnav/subnav.component.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | #appbar { 2 | height: 63px; 3 | width: 100%; 4 | padding: 12px 0 0 16px; 5 | color: white; 6 | background-color: #333; 7 | } 8 | 9 | #appbar h1 { 10 | margin-top: 0 !important; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /weatherapp-ng/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'Weather App'; 10 | } 11 | -------------------------------------------------------------------------------- /weatherapp-ng/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /weatherapp-ng/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/profile/profile.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 8 |
9 | -------------------------------------------------------------------------------- /weatherapp-ng/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('weather-app App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /weatherapp-ng/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts" 15 | ], 16 | "include": [ 17 | "**/*.spec.ts", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /weatherapp-ng/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /weatherapp-ng/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /weatherapp-ng/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types" 13 | ], 14 | "lib": [ 15 | "es2017", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/date.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { DateService } from './date.service'; 4 | 5 | describe('DateService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [DateService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([DateService], (service: DateService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/subnav/subnav.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from '../../services/auth.service'; 3 | 4 | @Component({ 5 | selector: 'app-subnav', 6 | templateUrl: './subnav.component.html', 7 | styleUrls: ['./subnav.component.css'] 8 | }) 9 | export class SubnavComponent implements OnInit { 10 | 11 | constructor(private authService: AuthService) { } 12 | 13 | ngOnInit() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/home/home.component.css: -------------------------------------------------------------------------------- 1 | .jumbotron { 2 | opacity: .8; 3 | border-radius: 8px; 4 | padding-top: 38px; 5 | padding-bottom: 38px; 6 | } 7 | 8 | .jumbotron .h1, .jumbotron h1 { 9 | font-size: 63px !important; 10 | color: inherit !important; 11 | } 12 | 13 | .jumbotron p { 14 | font-size: 21px; 15 | font-weight: 200; 16 | } 17 | 18 | p.tagline { 19 | margin-bottom: 25px; 20 | } 21 | 22 | div.main-cta { 23 | padding: 5px; 24 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/search/search.component.html: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | import { HttpModule } from '@angular/http'; 5 | 6 | describe('AuthService', () => { 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | imports: [ 10 | HttpModule 11 | ], 12 | providers: [AuthService] 13 | }); 14 | }); 15 | 16 | it('should be created', inject([AuthService], (service: AuthService) => { 17 | expect(service).toBeTruthy(); 18 | })); 19 | }); 20 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Router, CanActivate } from '@angular/router'; 3 | import { AuthService } from '../services/auth.service'; 4 | 5 | @Injectable() 6 | export class AuthGuard implements CanActivate{ 7 | constructor(private authService:AuthService, private router:Router){ 8 | 9 | } 10 | 11 | canActivate(){ 12 | if(this.authService.loggedIn()){ 13 | return true; 14 | } else { 15 | this.router.navigate(['/login']); 16 | return false; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/weather.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { WeatherService } from './weather.service'; 4 | 5 | import { HttpModule } from '@angular/http'; 6 | 7 | describe('WeatherService', () => { 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | imports: [ 11 | HttpModule 12 | ], 13 | providers: [WeatherService] 14 | }); 15 | }); 16 | 17 | it('should be created', inject([WeatherService], (service: WeatherService) => { 18 | expect(service).toBeTruthy(); 19 | })); 20 | }); 21 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/validate.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { ValidateService } from './validate.service'; 4 | 5 | import { HttpModule } from '@angular/http'; 6 | 7 | describe('ValidateService', () => { 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | imports: [ 11 | HttpModule 12 | ], 13 | providers: [ValidateService] 14 | }); 15 | }); 16 | 17 | it('should be created', inject([ValidateService], (service: ValidateService) => { 18 | expect(service).toBeTruthy(); 19 | })); 20 | }); 21 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/savedlocations.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { SavedlocationsService } from './savedlocations.service'; 4 | 5 | import { HttpModule } from '@angular/http'; 6 | 7 | describe('SavedlocationsService', () => { 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | imports: [ 11 | HttpModule 12 | ], 13 | providers: [SavedlocationsService] 14 | }); 15 | }); 16 | 17 | it('should be created', inject([SavedlocationsService], (service: SavedlocationsService) => { 18 | expect(service).toBeTruthy(); 19 | })); 20 | }); 21 | -------------------------------------------------------------------------------- /test/appTest.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | 3 | let chai = require('chai'); 4 | let chaiHttp = require('chai-http'); 5 | let should = chai.should(); 6 | 7 | chai.use(chaiHttp); 8 | 9 | describe('App', function() { 10 | describe('/GET any page', () => { 11 | it('it should return the public index.html page', (done) => { 12 | chai.request('http://localhost:3000') 13 | .get('') 14 | .end((err, res) => { 15 | res.should.have.status(200); 16 | //res.body.length.should.be.eql(0); 17 | done(); 18 | }); 19 | }); 20 | }); 21 | 22 | }); -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/validate.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class ValidateService { 5 | 6 | constructor() { } 7 | 8 | validateRegister(user){ 9 | if(user.name == undefined || user.email == undefined || user.username == undefined || user.password == undefined){ 10 | return false; 11 | } else { 12 | return true; 13 | } 14 | } 15 | 16 | validateEmail(email){ 17 | const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 18 | return regex.test(email); 19 | } 20 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weatherapp", 3 | "version": "1.0.0", 4 | "description": "weather app", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "start": "node app" 9 | }, 10 | "dependencies": { 11 | "express": "*", 12 | "mongoose": "*", 13 | "bcryptjs": "*", 14 | "cors": "*", 15 | "jsonwebtoken": "*", 16 | "body-parser": "*", 17 | "passport": "*", 18 | "passport-jwt": "*" 19 | }, 20 | "author": "Jonathan Spruance", 21 | "license": "ISC", 22 | "devDependencies": { 23 | "@angular/cli": "^1.4.3", 24 | "chai": "^4.1.2", 25 | "chai-http": "^3.0.0", 26 | "mocha": "^3.5.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # weatherapp 2 | A MEAN-stack weather application built with MongoDB, Express, Angular 4 and NodeJS and Open Weather Map REST services. 3 | 4 | # installation 5 | 1) Clone repository 6 | 'git clone https://github.com/jspruance/weatherapp.git' 7 | 2) Switch to 'develop' branch 8 | 'git checkout develop' 9 | 3) CD into 'weatherapp' root folder 10 | 4) Run 'npm install' 11 | 5) CD into Angular app 'weatherapp/weatherapp-ng' 12 | 6) Run 'npm install' 13 | 7) Run 'ng build' (prerequisite: Angular CLI tool) 14 | 8) Run 'npm start' in root folder ('weatherapp') 15 | 9) View applicatin in browser at 'localhost:3000' 16 | 17 | # production 18 | Production URL: http://www.weatherapp.xyz 19 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/login/login.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Don't have an account? Register now

5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | -------------------------------------------------------------------------------- /weatherapp-ng/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /config/passport.js: -------------------------------------------------------------------------------- 1 | const JwtStrategy = require('passport-jwt').Strategy; 2 | const ExtractJwt = require('passport-jwt').ExtractJwt; 3 | const User = require('../models/user'); 4 | const config = require('../config/database'); 5 | 6 | module.exports = function(passport) { 7 | let opts = {}; 8 | opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt'); 9 | opts.secretOrKey = config.secret; 10 | passport.use(new JwtStrategy(opts, (jwt_payload, done) => { 11 | User.getUserById(jwt_payload.data._id, (err, user) => { 12 | if (err) { 13 | return done(err, false); 14 | } 15 | if (user) { 16 | return done(null, user); 17 | } else { 18 | return done(null, false); 19 | } 20 | }); 21 | })); 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/profile/profile.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from '../../services/auth.service'; 3 | import {Router} from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-profile', 7 | templateUrl: './profile.component.html', 8 | styleUrls: ['./profile.component.css'] 9 | }) 10 | export class ProfileComponent implements OnInit { 11 | 12 | user: Object; 13 | 14 | constructor(private authService: AuthService, 15 | private router: Router 16 | ) { } 17 | 18 | ngOnInit() { 19 | this.authService.getProfile().subscribe(profile => { 20 | this.user = profile.user; 21 | }), 22 | err => { 23 | console.log(err); 24 | return false; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Weather App 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /weatherapp-ng/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | Weather App 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/navbar/navbar.component.css: -------------------------------------------------------------------------------- 1 | /* bootstrap overrides */ 2 | nav.navbar { 3 | background-color: #333; 4 | border-radius: 0; 5 | 6 | position: relative; 7 | max-height: 60px; 8 | margin-bottom: 20px; 9 | border: 1px solid transparent; 10 | } 11 | 12 | nav.navbar .greeting { 13 | color: white; 14 | } 15 | 16 | .navbar-brand { 17 | float: left; 18 | height: 50px; 19 | padding: 15px 15px; 20 | font-size: 36px; 21 | line-height: 20px; 22 | } 23 | 24 | nav ul.navbar-nav { 25 | float: right; 26 | } 27 | 28 | nav ul.navbar-nav li { 29 | margin-right: 20px; 30 | } 31 | 32 | .navbar-collapse.collapse { 33 | display: block!important; 34 | height: auto!important; 35 | padding-bottom: 0; 36 | overflow: visible!important; 37 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/search/search.component.css: -------------------------------------------------------------------------------- 1 | h3 { 2 | margin-top: 0px; 3 | color: #555; 4 | } 5 | 6 | form#searchform input { 7 | width: 400px; 8 | padding-left: 70px; 9 | background-image: url('/assets/mg.png'); 10 | background-repeat: no-repeat; 11 | background-position: left 10px top 50%; 12 | } 13 | 14 | .form-control-lg, .input-group-lg>.form-control, .input-group-lg>.input-group-addon, .input-group-lg>.input-group-btn>.btn { 15 | padding: .75rem 1.5rem; 16 | font-size: 1.25rem; 17 | border-radius: .3rem; 18 | } 19 | 20 | form#searchform button { 21 | line-height: 1.8em; 22 | margin-left: 15px; 23 | cursor: pointer; 24 | } 25 | 26 | #search { 27 | margin:0 auto; 28 | width: 50%; 29 | margin-top: 30px; 30 | margin-bottom: 30px; 31 | } -------------------------------------------------------------------------------- /weatherapp-ng/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/register/register.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/subnav/subnav.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SubnavComponent } from './subnav.component'; 4 | import { AuthService } from '../../services/auth.service'; 5 | import { HttpModule } from '@angular/http'; 6 | 7 | describe('SubnavComponent', () => { 8 | let component: SubnavComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | declarations: [ SubnavComponent ], 14 | providers:[AuthService], 15 | imports: [ 16 | HttpModule 17 | ] 18 | }) 19 | .compileComponents(); 20 | })); 21 | 22 | beforeEach(() => { 23 | fixture = TestBed.createComponent(SubnavComponent); 24 | component = fixture.componentInstance; 25 | fixture.detectChanges(); 26 | }); 27 | 28 | it('should create', () => { 29 | expect(component).toBeTruthy(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /weatherapp-ng/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/profile/profile.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProfileComponent } from './profile.component'; 4 | import { AuthService } from '../../services/auth.service'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { HttpModule } from '@angular/http'; 7 | 8 | describe('ProfileComponent', () => { 9 | let component: ProfileComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ ProfileComponent ], 15 | imports: [ 16 | HttpModule, 17 | RouterTestingModule 18 | ], 19 | providers: [AuthService] 20 | }) 21 | .compileComponents(); 22 | })); 23 | 24 | beforeEach(() => { 25 | fixture = TestBed.createComponent(ProfileComponent); 26 | component = fixture.componentInstance; 27 | fixture.detectChanges(); 28 | }); 29 | 30 | it('should create', () => { 31 | expect(component).toBeTruthy(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NotificationsService } from 'angular2-notifications'; 3 | import { AuthService } from '../../services/auth.service'; 4 | 5 | @Component({ 6 | selector: 'app-home', 7 | templateUrl: './home.component.html', 8 | styleUrls: ['./home.component.css'] 9 | }) 10 | export class HomeComponent implements OnInit { 11 | 12 | // notification defaults 13 | public options = { 14 | position: ["top", "right"], 15 | timeOut: 3000, 16 | pauseOnHover: true, 17 | lastOnBottom: true 18 | } 19 | 20 | constructor(private notificationsService: NotificationsService, 21 | private authService: AuthService 22 | ) { } 23 | 24 | ngOnInit() { 25 | 26 | // set logout notification when necessary 27 | if (sessionStorage.userlogout) { 28 | sessionStorage.removeItem('userlogout'); 29 | let ns = this.notificationsService; 30 | setTimeout(function() { 31 | ns.success("Success", "You are now logged out.", this.options); 32 | }, 30); 33 | } 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/date.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class DateService { 5 | 6 | constructor() { } 7 | 8 | d = new Date(); 9 | 10 | weekArry = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 11 | monthArry = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 12 | hourArry = ["12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]; 13 | 14 | today = this.weekArry[this.d.getDay()]; 15 | month = this.monthArry[this.d.getMonth()]; 16 | date = this.d.getDate(); 17 | hourMil = this.d.getHours(); 18 | hour = this.hourArry[this.d.getHours()]; 19 | minutesNum = this.d.getMinutes(); 20 | minutes = (this.minutesNum < 10) ? "0" + this.minutesNum : this.minutesNum; 21 | ampm = (this.hourMil > 11) ? "pm" : "am"; 22 | 23 | 24 | getDayAndDate() { 25 | return `${this.today}, ${this.month} ${this.date}`; 26 | } 27 | 28 | getDayAndTime() { 29 | return `${this.today} ${this.hour}:${this.minutes}${this.ampm}`; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /weatherapp-ng/README.md: -------------------------------------------------------------------------------- 1 | # WeatherApp 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.4.2. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | Before running the tests make sure you are serving the app via `ng serve`. 25 | 26 | ## Further help 27 | 28 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 29 | -------------------------------------------------------------------------------- /weatherapp-ng/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/weather.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Http } from '@angular/http'; 3 | import 'rxjs/add/operator/map'; 4 | 5 | @Injectable() 6 | export class WeatherService { 7 | 8 | appid:string = "&appid=fd61eab401147e78b825c2f71cdea941"; 9 | weatherurl:string = "http://api.openweathermap.org/data/2.5/weather"; 10 | forecasturl:string = "http://api.openweathermap.org/data/2.5/forecast"; 11 | iconclass:string; 12 | 13 | constructor(public http:Http) { 14 | console.log('weather service connected...'); 15 | } 16 | 17 | setQueryPrefix(location) { 18 | let queryPrefix = "?q="; 19 | let isnum = /^\d+$/.test(location); 20 | if(isnum === true) { 21 | queryPrefix = "?zip="; 22 | } 23 | return queryPrefix; 24 | } 25 | 26 | getWeather(location) { 27 | let queryPrefix = this.setQueryPrefix(location); 28 | let url = this.weatherurl + queryPrefix + location + this.appid; 29 | return this.http.get(url).map(res => res.json()); 30 | } 31 | 32 | getForecast(location) { 33 | let queryPrefix = this.setQueryPrefix(location); 34 | let url = this.forecasturl + queryPrefix + location + this.appid; 35 | return this.http.get(url).map(res => res.json()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | h3 { 2 | margin-top: 0px; 3 | } 4 | 5 | .edit-link a { 6 | font-size: 20px; 7 | margin-top: 12px; 8 | } 9 | 10 | .componenttitle { 11 | margin: 20px 0 0 0; 12 | } 13 | 14 | .nolocations { 15 | float: left; 16 | width: 100%; 17 | border: 1px solid #ddd; 18 | border-radius: 8px; 19 | margin-top: 25px; 20 | padding: 18px; 21 | font-size: 16px; 22 | color: #555; 23 | background-color: white; 24 | } 25 | 26 | .delete { 27 | cursor: pointer; 28 | } 29 | 30 | #saved-tile { 31 | width: 25%; 32 | float: left; 33 | /* border: 1px solid #ddd; */ 34 | border-radius: 8px; 35 | margin: 20px 25px 25px 25px; 36 | padding: 18px; 37 | font-size: 16px; 38 | height: 312px; 39 | background-color: white; 40 | cursor: pointer; 41 | } 42 | 43 | #saved-tile:hover { 44 | background-color: #FFFFD4; 45 | } 46 | 47 | #saved-tile div { 48 | margin-bottom: 15px; 49 | } 50 | 51 | #saved-tile #description { 52 | margin-bottom: 0px; 53 | } 54 | 55 | #saved-tile #description, #saved-tile #temperature, .componenttitle { 56 | color: #555; 57 | } 58 | 59 | #saved-tile .result.temp { 60 | font-size: 24px; 61 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | import { NavbarComponent } from './components/navbar/navbar.component'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { SimpleNotificationsModule } from 'angular2-notifications'; 7 | import { AuthService } from './services/auth.service'; 8 | import { HttpModule } from '@angular/http'; 9 | 10 | describe('AppComponent', () => { 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | declarations: [ 14 | AppComponent, NavbarComponent 15 | ], 16 | imports: [ 17 | HttpModule, 18 | RouterTestingModule, 19 | SimpleNotificationsModule.forRoot() 20 | ], 21 | providers: [AuthService] 22 | }).compileComponents(); 23 | })); 24 | it('should create the app', async(() => { 25 | const fixture = TestBed.createComponent(AppComponent); 26 | const app = fixture.debugElement.componentInstance; 27 | expect(app).toBeTruthy(); 28 | })); 29 | it(`should have a title of 'Weather App'`, async(() => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | const app = fixture.debugElement.componentInstance; 32 | expect(app.title).toEqual('Weather App'); 33 | })); 34 | }); 35 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/home/home.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |

Weather App

6 |

Accurate weather reporting from around the globe

7 | 8 | 9 | 10 |
11 |
12 | Register Login 13 |
14 |
15 | Dashboard Search weather 16 |
17 |
18 | 19 |
20 | 21 |
22 |
23 |

Real-time weather reports

24 |

Get the latest weather reports from anywhere around the globe.

25 |
26 |
27 |

Customizable dashboard

28 |

View the weather from your favorite locations in your own personalized dashboard.

29 |
30 |
31 |

Secure login

32 |

Our app features secure authentication and password encryption so your data is always safe.

33 |
34 |
35 | 36 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | import { SimpleNotificationsModule } from 'angular2-notifications'; 6 | import { SearchComponent } from '../../components/search/search.component'; 7 | import { RouterTestingModule } from '@angular/router/testing'; 8 | import { AuthService } from '../../services/auth.service'; 9 | import { WeatherService } from '../../services/weather.service'; 10 | import { HttpModule } from '@angular/http'; 11 | 12 | describe('HomeComponent', () => { 13 | let component: HomeComponent; 14 | let fixture: ComponentFixture; 15 | 16 | beforeEach(async(() => { 17 | TestBed.configureTestingModule({ 18 | declarations: [ HomeComponent, SearchComponent ], 19 | imports: [ 20 | HttpModule, 21 | RouterTestingModule, 22 | SimpleNotificationsModule.forRoot() 23 | ], 24 | providers: [AuthService, WeatherService] 25 | }) 26 | .compileComponents(); 27 | })); 28 | 29 | beforeEach(() => { 30 | fixture = TestBed.createComponent(HomeComponent); 31 | component = fixture.componentInstance; 32 | fixture.detectChanges(); 33 | }); 34 | 35 | it('should create', () => { 36 | expect(component).toBeTruthy(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/register/register.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RegisterComponent } from './register.component'; 4 | import { SimpleNotificationsModule } from 'angular2-notifications'; 5 | import { ValidateService } from '../../services/validate.service'; 6 | import { RouterTestingModule } from '@angular/router/testing'; 7 | import { AuthService } from '../../services/auth.service'; 8 | import { WeatherService } from '../../services/weather.service'; 9 | import { FormsModule } from '@angular/forms'; 10 | import { HttpModule } from '@angular/http'; 11 | 12 | describe('RegisterComponent', () => { 13 | let component: RegisterComponent; 14 | let fixture: ComponentFixture; 15 | 16 | beforeEach(async(() => { 17 | TestBed.configureTestingModule({ 18 | declarations: [ RegisterComponent ], 19 | imports: [ 20 | HttpModule, 21 | FormsModule, 22 | RouterTestingModule, 23 | SimpleNotificationsModule.forRoot() 24 | ], 25 | providers:[AuthService,ValidateService,WeatherService] 26 | }) 27 | .compileComponents(); 28 | })); 29 | 30 | beforeEach(() => { 31 | fixture = TestBed.createComponent(RegisterComponent); 32 | component = fixture.componentInstance; 33 | fixture.detectChanges(); 34 | }); 35 | 36 | it('should create', () => { 37 | expect(component).toBeTruthy(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NotificationsService } from 'angular2-notifications'; 3 | import { AuthService } from '../../services/auth.service'; 4 | import {Router} from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-navbar', 8 | templateUrl: './navbar.component.html', 9 | styleUrls: ['./navbar.component.css'] 10 | }) 11 | export class NavbarComponent implements OnInit { 12 | 13 | username: String; 14 | 15 | // notification defaults 16 | public options = { 17 | position: ["top", "right"], 18 | timeOut: 3000, 19 | pauseOnHover: true, 20 | lastOnBottom: true 21 | } 22 | 23 | constructor(private notificationsService: NotificationsService, 24 | private authService: AuthService, 25 | private router: Router 26 | ) { 27 | authService.login$.subscribe(user => this.onUserLogin(user)); 28 | } 29 | 30 | onUserLogin(user) { 31 | // update username in top nav when user logs in 32 | this.username = user.username; 33 | } 34 | 35 | ngOnInit() { 36 | if(localStorage.getItem("user") !== null) { 37 | let user = JSON.parse(localStorage.getItem("user")); 38 | this.username = user.name; 39 | } 40 | } 41 | 42 | onLogoutClick() { 43 | this.authService.logout(); 44 | sessionStorage.setItem("userlogout", "true"); 45 | this.router.navigate(['']); 46 | return false; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DashboardComponent } from './dashboard.component'; 4 | 5 | import { SimpleNotificationsModule } from 'angular2-notifications'; 6 | import { RouterTestingModule } from '@angular/router/testing'; 7 | import { SubnavComponent } from '../../components/subnav/subnav.component'; 8 | import { WeatherService } from '../../services/weather.service'; 9 | import { SavedlocationsService } from '../../services/savedlocations.service'; 10 | import { AuthService } from '../../services/auth.service'; 11 | import { HttpModule } from '@angular/http'; 12 | 13 | describe('DashboardComponent', () => { 14 | let component: DashboardComponent; 15 | let fixture: ComponentFixture; 16 | 17 | beforeEach(async(() => { 18 | TestBed.configureTestingModule({ 19 | declarations: [ DashboardComponent, SubnavComponent ], 20 | imports: [ 21 | HttpModule, 22 | RouterTestingModule, 23 | SimpleNotificationsModule.forRoot() 24 | ], 25 | providers: [WeatherService,AuthService,SavedlocationsService] 26 | }) 27 | .compileComponents(); 28 | })); 29 | 30 | beforeEach(() => { 31 | fixture = TestBed.createComponent(DashboardComponent); 32 | component = fixture.componentInstance; 33 | fixture.detectChanges(); 34 | }); 35 | 36 | it('should create', () => { 37 | expect(component).toBeTruthy(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /weatherapp-ng/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "weather-app" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "../public", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css" 23 | ], 24 | "scripts": [ 25 | "../node_modules/jquery/dist/jquery.min.js", 26 | "../node_modules/bootstrap/dist/js/bootstrap.min.js" 27 | ], 28 | "environmentSource": "environments/environment.ts", 29 | "environments": { 30 | "dev": "environments/environment.ts", 31 | "prod": "environments/environment.prod.ts" 32 | } 33 | } 34 | ], 35 | "e2e": { 36 | "protractor": { 37 | "config": "./protractor.conf.js" 38 | } 39 | }, 40 | "lint": [ 41 | { 42 | "project": "src/tsconfig.app.json" 43 | }, 44 | { 45 | "project": "src/tsconfig.spec.json" 46 | }, 47 | { 48 | "project": "e2e/tsconfig.e2e.json" 49 | } 50 | ], 51 | "test": { 52 | "karma": { 53 | "config": "./karma.conf.js" 54 | } 55 | }, 56 | "defaults": { 57 | "styleExt": "css", 58 | "component": {} 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |

My saved locations

7 |
8 | 9 | 13 | 14 |
15 | 16 |
17 | 18 |
19 | delete 20 |
21 | 22 |

{{report.name}}

23 | 24 |
Current temp: 25 | {{convertTemp(report.main.temp)}} 26 | 27 |
28 | 29 |
Current conditions: 30 | {{report.weather[0].description}} 31 |
32 | 33 |
34 | 35 | 36 | 37 |
38 | 39 |
40 | 41 |
42 |

You have no saved locations. Add one now.

43 |
44 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/weatherdetails/weatherdetails.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WeatherdetailsComponent } from './weatherdetails.component'; 4 | 5 | import { SimpleNotificationsModule } from 'angular2-notifications'; 6 | import { SearchComponent } from '../../components/search/search.component'; 7 | import { RouterTestingModule } from '@angular/router/testing'; 8 | import { WeatherService } from '../../services/weather.service'; 9 | import { DateService } from '../../services/date.service'; 10 | import { SavedlocationsService } from '../../services/savedlocations.service'; 11 | import { HttpModule } from '@angular/http'; 12 | 13 | describe('WeatherdetailsComponent', () => { 14 | let component: WeatherdetailsComponent; 15 | let fixture: ComponentFixture; 16 | 17 | beforeEach(async(() => { 18 | TestBed.configureTestingModule({ 19 | declarations: [ WeatherdetailsComponent, SearchComponent ], 20 | imports: [ 21 | HttpModule, 22 | RouterTestingModule, 23 | SimpleNotificationsModule.forRoot() 24 | ], 25 | providers:[WeatherService, SavedlocationsService, DateService] 26 | }) 27 | .compileComponents(); 28 | })); 29 | 30 | beforeEach(() => { 31 | fixture = TestBed.createComponent(WeatherdetailsComponent); 32 | component = fixture.componentInstance; 33 | fixture.detectChanges(); 34 | }); 35 | 36 | it('should create', () => { 37 | expect(component).toBeTruthy(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/savedlocations.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import {Http, Headers} from '@angular/http'; 3 | 4 | @Injectable() 5 | export class SavedlocationsService { 6 | 7 | constructor(private http:Http) { } 8 | 9 | getSavedLocations() { 10 | let locations = []; 11 | if (localStorage.user) { 12 | let user = JSON.parse(localStorage.user); 13 | return user.locations; 14 | }else{ 15 | return []; 16 | } 17 | } 18 | 19 | addSavedLocation(location, userid) { 20 | let user = { 21 | "location": location, 22 | "userid": userid 23 | } 24 | let headers = new Headers(); 25 | headers.append('Content-Type','application/json'); 26 | return this.http.post('http://localhost:3000/users/addlocation', user, {headers: headers}) 27 | .map(res => res.json()); 28 | } 29 | 30 | deleteSavedLocation(location, userid) { 31 | let user = { 32 | "location": location, 33 | "userid": userid 34 | } 35 | let headers = new Headers(); 36 | headers.append('Content-Type','application/json'); 37 | return this.http.post('http://localhost:3000/users/removelocation', user, {headers: headers}) 38 | .map(res => res.json()); 39 | } 40 | 41 | deleteAllSavedLocations(userid) { 42 | let user = {"userid": userid} 43 | let headers = new Headers(); 44 | headers.append('Content-Type','application/json'); 45 | return this.http.post('http://localhost:3000/users/removealllocations', user, {headers: headers}) 46 | .map(res => res.json()); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /weatherapp-ng/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather-app", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^4.2.4", 16 | "@angular/common": "^4.2.4", 17 | "@angular/compiler": "^4.2.4", 18 | "@angular/core": "^4.2.4", 19 | "@angular/forms": "^4.2.4", 20 | "@angular/http": "^4.2.4", 21 | "@angular/platform-browser": "^4.2.4", 22 | "@angular/platform-browser-dynamic": "^4.2.4", 23 | "@angular/router": "^4.2.4", 24 | "angular2-jwt": "^0.2.3", 25 | "angular2-notifications": "^0.7.7", 26 | "bootstrap": "^3.3.7", 27 | "core-js": "^2.4.1", 28 | "jquery": "^3.2.1", 29 | "rxjs": "^5.4.2", 30 | "zone.js": "^0.8.14" 31 | }, 32 | "devDependencies": { 33 | "@angular/cli": "1.4.2", 34 | "@angular/compiler-cli": "^4.2.4", 35 | "@angular/language-service": "^4.2.4", 36 | "@types/jasmine": "~2.5.53", 37 | "@types/jasminewd2": "~2.0.2", 38 | "@types/node": "~6.0.60", 39 | "codelyzer": "~3.1.1", 40 | "jasmine-core": "~2.6.2", 41 | "jasmine-spec-reporter": "~4.1.0", 42 | "karma": "~1.7.0", 43 | "karma-chrome-launcher": "~2.1.1", 44 | "karma-cli": "~1.0.1", 45 | "karma-coverage-istanbul-reporter": "^1.2.1", 46 | "karma-jasmine": "~1.1.0", 47 | "karma-jasmine-html-reporter": "^0.2.2", 48 | "protractor": "~5.1.2", 49 | "ts-node": "~3.2.0", 50 | "tslint": "~5.3.2", 51 | "typescript": "~2.3.3" 52 | } 53 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const bodyParser = require('body-parser'); 4 | const cors = require('cors'); 5 | const passport = require('passport'); 6 | const mongoose = require('mongoose'); 7 | const config = require('./config/database'); 8 | 9 | // set database 10 | let db = config.database; 11 | db = (process.env.NODE_ENV === 'test') ? config.testdatabase : config.database; 12 | 13 | // connect to database 14 | mongoose.connect(db); 15 | 16 | // on connection 17 | mongoose.connection.on('connected', () => { 18 | console.log('connected to database ' + db); 19 | }); 20 | 21 | // on error 22 | mongoose.connection.on('error', (err) => { 23 | console.log('database error ' + err); 24 | }); 25 | 26 | const app = express(); 27 | 28 | const users = require('./routes/users'); 29 | 30 | // Port number 31 | const port = process.env.PORT || 3000; 32 | 33 | // CORS middleware 34 | app.use(cors()); 35 | 36 | // set static folder 37 | app.use(express.static(path.join(__dirname, 'public'))); 38 | 39 | // Body parser middleware 40 | app.use(bodyParser.json()); 41 | 42 | // Passport middleware 43 | app.use(passport.initialize()); 44 | app.use(passport.session()); 45 | 46 | require('./config/passport')(passport); 47 | 48 | app.use('/users', users); 49 | 50 | // Index route, link to angular app 51 | app.get('/', (req, res) => { 52 | res.send("Invalid endpoint"); 53 | }); 54 | 55 | app.get('*', (req, res) => { 56 | res.sendFile(path.join(__dirname, 'public/index.html')); 57 | }); 58 | 59 | // Start server 60 | app.listen(port, () => { 61 | console.log("Server started on port " + port); 62 | }); 63 | 64 | 65 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NotificationsService } from 'angular2-notifications'; 3 | import { AuthService } from '../../services/auth.service'; 4 | import {Router} from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-login', 8 | templateUrl: './login.component.html', 9 | styleUrls: ['./login.component.css'] 10 | }) 11 | export class LoginComponent implements OnInit { 12 | 13 | title: string = "Login"; 14 | 15 | username: String; 16 | password: String; 17 | loggedIn: string; 18 | 19 | // notification defaults 20 | public options = { 21 | position: ["top", "right"], 22 | timeOut: 4000, 23 | pauseOnHover: true, 24 | lastOnBottom: true 25 | } 26 | 27 | constructor(private notificationsService: NotificationsService, 28 | private authService: AuthService, 29 | private router: Router 30 | ) { } 31 | 32 | ngOnInit() { 33 | 34 | if (sessionStorage.newregistereduser) { 35 | sessionStorage.removeItem('newregistereduser'); 36 | let ns = this.notificationsService; 37 | setTimeout(function() { 38 | ns.success("Success", "You are now registered!", this.options); 39 | }, 30); 40 | } 41 | 42 | } 43 | 44 | onLoginSubmit() { 45 | const user = { 46 | username: this.username, 47 | password: this.password 48 | } 49 | 50 | this.authService.authenticateUser(user).subscribe(data => { 51 | if(data.success){ 52 | this.authService.storeUserData(data.token, data.user); 53 | this.loggedIn = 'true'; 54 | sessionStorage.setItem('userloggedin', this.loggedIn); 55 | this.router.navigate(['/dashboard']); 56 | }else { 57 | this.notificationsService.error("Error", data.msg, this.options); 58 | } 59 | }); 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/services/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, EventEmitter } from '@angular/core'; 2 | import {Http, Headers} from '@angular/http'; 3 | import 'rxjs/add/operator/map'; 4 | import { tokenNotExpired } from 'angular2-jwt'; 5 | 6 | @Injectable() 7 | export class AuthService { 8 | authToken: any; 9 | user: any; 10 | public login$: EventEmitter; 11 | 12 | constructor(private http:Http) { 13 | this.login$ = new EventEmitter(); 14 | } 15 | 16 | registerUser(user){ 17 | let headers = new Headers(); 18 | headers.append('Content-Type','application/json'); 19 | return this.http.post('http://localhost:3000/users/register', user,{headers: headers}) 20 | .map(res => res.json()); 21 | } 22 | 23 | authenticateUser(user){ 24 | let headers = new Headers(); 25 | headers.append('Content-Type','application/json'); 26 | return this.http.post('http://localhost:3000/users/authenticate', user,{headers: headers}) 27 | .map(res => res.json()); 28 | 29 | } 30 | 31 | getProfile(){ 32 | let headers = new Headers(); 33 | this.loadToken(); 34 | headers.append('Authorization', this.authToken); 35 | headers.append('Content-Type','application/json'); 36 | return this.http.get('http://localhost:3000/users/profile', {headers: headers}) 37 | .map(res => res.json()); 38 | } 39 | 40 | storeUserData(token, user) { 41 | localStorage.setItem('id_token', token); 42 | localStorage.setItem('user', JSON.stringify(user)); 43 | this.authToken = token; 44 | this.user = user; 45 | this.login$.emit(user); 46 | } 47 | 48 | loadToken() { 49 | const token = localStorage.getItem('id_token'); 50 | this.authToken = token; 51 | } 52 | 53 | loggedIn() { 54 | return tokenNotExpired('id_token'); 55 | } 56 | 57 | logout() { 58 | this.authToken = null; 59 | this.user = null; 60 | localStorage.removeItem('id_token'); 61 | localStorage.removeItem('user'); 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/weatherdetails/weatherdetails.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 |

{{report.name}}

9 |
10 | Add to dashboard 11 |
12 |
13 | 14 |
15 |
16 | 17 |
{{ date }}
18 |
Current conditions: 19 | {{report.weather[0].description}} 20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 | {{convertTemp(report.main.temp, "F")}} 28 | High: {{convertTemp(report.main.temp_max, "F")}} Low: {{convertTemp(report.main.temp_min, "F")}} 29 |
30 |
31 | 32 | Wind: {{report.wind.speed}} mph 33 | Humidity: {{report.main.humidity}}% 34 |
35 |
36 |
37 | 38 |
39 | 40 |
41 |
42 |
{{forecast.day}}
43 |
{{convertTemp(forecast.hi, "F", true)}} / {{convertTemp(forecast.lo, "F", true)}}
44 |
45 |

{{forecast.description}}

46 |
47 |
-------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/register/register.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {ValidateService} from '../../services/validate.service' 3 | import {AuthService} from '../../services/auth.service' 4 | import { NotificationsService } from 'angular2-notifications'; 5 | import {Router} from '@angular/router'; 6 | 7 | @Component({ 8 | selector: 'app-register', 9 | templateUrl: './register.component.html', 10 | styleUrls: ['./register.component.css'] 11 | }) 12 | export class RegisterComponent implements OnInit { 13 | name: String; 14 | username: String; 15 | email: String; 16 | password: String; 17 | 18 | // notification defaults 19 | public options = { 20 | position: ["top", "right"], 21 | timeOut: 5000, 22 | pauseOnHover: true, 23 | lastOnBottom: true 24 | } 25 | 26 | constructor( 27 | private validateService: ValidateService, 28 | private notificationsService: NotificationsService, 29 | private authService:AuthService, 30 | private router: Router 31 | ) { } 32 | 33 | ngOnInit() { 34 | 35 | } 36 | 37 | onRegisterSubmit(){ 38 | const user = { 39 | name: this.name, 40 | email: this.email, 41 | username: this.username, 42 | password: this.password, 43 | locations: [] 44 | } 45 | 46 | // Required Fields 47 | if(!this.validateService.validateRegister(user)){ 48 | this.notificationsService.error("Error", "Please fill out all fields", this.options); 49 | return false; 50 | } 51 | 52 | // Validate Email 53 | if(!this.validateService.validateEmail(user.email)){ 54 | this.notificationsService.error("Error", "Please enter a valid email address", this.options); 55 | return false; 56 | } 57 | 58 | // Register user 59 | this.authService.registerUser(user).subscribe(data => { 60 | if(data.success){ 61 | sessionStorage.setItem('newregistereduser', 'true'); 62 | this.router.navigate(['/login']); 63 | } else { 64 | this.notificationsService.error("Error", "Registration error", this.options); 65 | this.router.navigate(['/register']); 66 | } 67 | }); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Output, EventEmitter } from '@angular/core'; 2 | import { WeatherService } from '../../services/weather.service'; 3 | import {Router} from '@angular/router'; 4 | import { ActivatedRoute } from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-search', 8 | templateUrl: './search.component.html', 9 | styleUrls: ['./search.component.css'] 10 | }) 11 | export class SearchComponent implements OnInit { 12 | 13 | location:string; 14 | weatherRetrieved: Boolean = false; 15 | private sub: any; 16 | onWeatherPg: Boolean = false; 17 | 18 | @Output() 19 | changeWeather: EventEmitter = new EventEmitter(); 20 | 21 | @Output() 22 | changeForecast: EventEmitter = new EventEmitter(); 23 | 24 | constructor(private weatherService:WeatherService, 25 | private router: Router, 26 | private route: ActivatedRoute 27 | ) { } 28 | 29 | ngOnInit() { 30 | this.onWeatherPg = window.location.href.indexOf("/weather") > -1; 31 | 32 | // if the current url containes a location parameter, run the search using it 33 | this.sub = this.route.params.subscribe(params => { 34 | this.location = params['location']; 35 | if (this.location) { 36 | this.getWeather(this.location, true, this.onWeatherPg); 37 | } 38 | }); 39 | } 40 | 41 | ngOnDestroy() { 42 | this.sub.unsubscribe(); 43 | } 44 | 45 | getWeather(locationinput, forecast, onWeatherPg) { 46 | this.location = locationinput; 47 | 48 | // if search is run from the weather page 49 | if(onWeatherPg) { 50 | 51 | // get current weather 52 | this.weatherService.getWeather(this.location).subscribe((data) => { 53 | this.weatherRetrieved = true; // for testing 54 | this.changeWeather.emit(data); 55 | }); 56 | 57 | if(forecast === true) { 58 | // get extended forecast 59 | this.weatherService.getForecast(this.location).subscribe((data) => { 60 | this.changeForecast.emit(data); 61 | }); 62 | } 63 | 64 | // if search is run from another page 65 | } else { 66 | this.router.navigate(['/weather', this.location]); 67 | } 68 | 69 | return false; 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/login/login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; 2 | import { DebugElement } from '@angular/core'; 3 | import { By } from '@angular/platform-browser'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import { LoginComponent } from './login.component'; 6 | import { SimpleNotificationsModule } from 'angular2-notifications'; 7 | import { RouterTestingModule } from '@angular/router/testing'; 8 | import { AuthService } from '../../services/auth.service'; 9 | import { HttpModule } from '@angular/http'; 10 | import { FormsModule } from '@angular/forms'; 11 | 12 | describe('LoginComponent', () => { 13 | let component: LoginComponent; 14 | let fixture: ComponentFixture; 15 | let de: DebugElement; 16 | let el: HTMLElement; 17 | 18 | let authService: AuthService; 19 | 20 | let authServiceStub = { 21 | "success":true, 22 | "token":"abc123", 23 | "user":{ 24 | "id":"123", 25 | "name":"test", 26 | "username":"test", 27 | "email":"test@t.com", 28 | "locations":[] 29 | } 30 | }; 31 | 32 | beforeEach(async(() => { 33 | TestBed.configureTestingModule({ 34 | declarations: [ LoginComponent ], 35 | imports: [ 36 | HttpModule, 37 | FormsModule, 38 | RouterTestingModule, 39 | SimpleNotificationsModule.forRoot() 40 | ], 41 | providers:[{provide: AuthService, useValue: authServiceStub }] 42 | }) 43 | .compileComponents(); 44 | })); 45 | 46 | beforeEach(() => { 47 | fixture = TestBed.createComponent(LoginComponent); 48 | component = fixture.componentInstance; 49 | authService = TestBed.get(AuthService); 50 | 51 | // query for the title

by CSS element selector 52 | de = fixture.debugElement.query(By.css('h2')); 53 | el = de.nativeElement; 54 | }); 55 | 56 | it('should create', () => { 57 | expect(component).toBeTruthy(); 58 | }); 59 | 60 | it('no title in the DOM until manually call `detectChanges`', () => { 61 | expect(el.textContent).toEqual(''); 62 | }); 63 | 64 | it('should display original title', () => { 65 | fixture.detectChanges(); 66 | expect(el.textContent).toContain(component.title); 67 | }); 68 | 69 | it('component "loggedIn" property should be set to "true"', () => { 70 | fixture.detectChanges(); 71 | component.loggedIn === 'true'; 72 | }); 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/navbar/navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing'; 3 | import { Router } from '@angular/router'; 4 | import { By } from '@angular/platform-browser'; 5 | import { Location, CommonModule } from '@angular/common'; 6 | import { NavbarComponent } from './navbar.component'; 7 | import { RouterTestingModule } from '@angular/router/testing'; 8 | import { SimpleNotificationsModule } from 'angular2-notifications'; 9 | import { AuthService } from '../../services/auth.service'; 10 | import { HttpModule } from '@angular/http'; 11 | 12 | @Component({ 13 | template: '' 14 | }) 15 | class DummyComponent { 16 | } 17 | 18 | describe('NavbarComponent', () => { 19 | let component: NavbarComponent; 20 | let fixture: ComponentFixture; 21 | 22 | beforeEach(async(() => { 23 | TestBed.configureTestingModule({ 24 | declarations: [ NavbarComponent, DummyComponent ], 25 | imports: [ 26 | HttpModule, 27 | RouterTestingModule.withRoutes([ 28 | { path: 'home', component: DummyComponent }, 29 | { path: 'weather', component: DummyComponent } 30 | ]), 31 | SimpleNotificationsModule.forRoot() 32 | ], 33 | providers:[AuthService] 34 | }) 35 | .compileComponents(); 36 | })); 37 | 38 | beforeEach(() => { 39 | fixture = TestBed.createComponent(NavbarComponent); 40 | component = fixture.componentInstance; 41 | fixture.detectChanges(); 42 | }); 43 | 44 | it('should create', () => { 45 | expect(component).toBeTruthy(); 46 | }); 47 | 48 | it('click on "home" link should go to home page', 49 | async(inject([Router, Location], (router: Router, location: Location) => { 50 | 51 | fixture.detectChanges(); 52 | 53 | fixture.debugElement.query(By.css('.home-link')).nativeElement.click(); 54 | fixture.whenStable().then(() => { 55 | expect(location.path()).toEqual('/'); 56 | console.log('after expect'); 57 | }); 58 | }))); 59 | 60 | it('click on "weather" link should go to weather page', 61 | async(inject([Router, Location], (router: Router, location: Location) => { 62 | 63 | fixture.detectChanges(); 64 | 65 | fixture.debugElement.query(By.css('.weather-link')).nativeElement.click(); 66 | fixture.whenStable().then(() => { 67 | expect(location.path()).toEqual('/weather'); 68 | console.log('after expect'); 69 | }); 70 | }))); 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcryptjs'); 3 | const config = require('../config/database'); 4 | 5 | // user schema 6 | const UserSchema = mongoose.Schema({ 7 | name: { 8 | type: String 9 | }, 10 | email: { 11 | type: String, 12 | required: true 13 | }, 14 | username: { 15 | type: String, 16 | required: true 17 | }, 18 | password: { 19 | type: String, 20 | required: true 21 | }, 22 | locations: { 23 | type: [String] 24 | } 25 | }); 26 | 27 | const User = module.exports = mongoose.model('User', UserSchema); 28 | 29 | module.exports.getUserById = function(id, callback) { 30 | User.findById(id, callback); 31 | } 32 | 33 | module.exports.getUserByUsername = function(username, callback) { 34 | const query = {username: username} 35 | User.findOne(query, callback); 36 | } 37 | 38 | module.exports.addUser = function (newUser, callback) { 39 | bcrypt.genSalt(10, (err, salt) => { 40 | bcrypt.hash(newUser.password, salt, (err, hash) => { 41 | if (err) throw err; 42 | newUser.password = hash; 43 | newUser.save(callback); 44 | }); 45 | }); 46 | }; 47 | 48 | /* 49 | * DELETE all users from collection 50 | */ 51 | module.exports.deleteUsers = function (callback) { 52 | User.remove({}, function(err) { 53 | callback(); 54 | }); 55 | } 56 | 57 | 58 | module.exports.addLocation = function (userid, newlocation, callback) { 59 | User.findByIdAndUpdate( 60 | userid, 61 | { $push: {"locations": newlocation}}, 62 | { new: true, upsert: true}, 63 | function(err, model) { 64 | if(err) throw err; 65 | callback(null, model); 66 | } 67 | ); 68 | }; 69 | 70 | module.exports.removeLocation = function (userid, location, callback) { 71 | 72 | User.findByIdAndUpdate( 73 | userid, 74 | { $pull: {"locations": location}}, 75 | { new: true, upsert: true}, 76 | function(err, model) { 77 | if(err) throw err; 78 | callback(null, model); 79 | } 80 | ); 81 | }; 82 | 83 | module.exports.removeAllLocations = function (userid, callback) { 84 | User.update( 85 | { _id: userid }, 86 | { $set: { locations: [] }}, 87 | function(err, model) { 88 | if(err) throw err; 89 | callback(null, model); 90 | } 91 | ); 92 | }; 93 | 94 | module.exports.comparePassword = function (candidatePassword, hash, callback) { 95 | bcrypt.compare(candidatePassword, hash, (err, isMatch) => { 96 | if(err) throw err; 97 | callback(null, isMatch); 98 | }); 99 | }; -------------------------------------------------------------------------------- /weatherapp-ng/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /weatherapp-ng/src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 3 | font-size: 14px; 4 | color: #333; 5 | font-size: 14px; 6 | line-height: 1.43; 7 | background-image:url("assets/clouds.jpg"); 8 | background-repeat: repeat-y; 9 | } 10 | 11 | simple-notifications { 12 | z-index: 9999; 13 | margin-top: 200px; 14 | } 15 | 16 | #appcontainer { 17 | padding: 10px 20px 20px 20px; 18 | } 19 | 20 | h1 { 21 | margin: 10px 0 7px 0 !important; 22 | color: #3C8ED2; 23 | } 24 | 25 | h3 { 26 | font-size: 24px !important; 27 | } 28 | 29 | .clear { 30 | clear: both; 31 | } 32 | 33 | .pull-left { 34 | float: left; 35 | } 36 | 37 | .pull-right { 38 | float: right; 39 | } 40 | 41 | label { 42 | display: block; 43 | font-size: 14px; 44 | } 45 | 46 | #location { 47 | display: block; 48 | margin: 15px; 49 | } 50 | 51 | .error { 52 | color: red; 53 | display: none; 54 | } 55 | 56 | .result { color: #05E177; } 57 | 58 | .ex { 59 | color: #aaa; 60 | font-style: italic; 61 | } 62 | 63 | .weather-icon { 64 | width: 150px; 65 | height: 150px; 66 | background-image: url("assets/weathericons.png"); 67 | background-repeat: no-repeat; 68 | } 69 | 70 | .weather-icon.clearsky { 71 | background-position: -300px -300px; 72 | } 73 | 74 | .weather-icon.snow, .weather-icon.lightrainandsnow { 75 | background-position: -300px -150px; 76 | } 77 | 78 | .weather-icon.scatteredclouds, .weather-icon.brok-cloud { 79 | background-position: -300px 0px; 80 | } 81 | 82 | .weather-icon.lightrain, 83 | .weather-icon.moderaterain, 84 | .weather-icon.lightintensityshowerrain, 85 | .weather-icon[class*="rain"] 86 | { 87 | background-position: 0px -150px; 88 | } 89 | 90 | .weather-icon.showerrain { 91 | background-position: -150px -150px; 92 | } 93 | 94 | .weather-icon.fog, .weather-icon.haze, .weather-icon.mist { 95 | background-position: -150px -450px; 96 | } 97 | 98 | .weather-icon.overcast clouds, .weather-icon.fewclouds { 99 | background-position: -300px -0px; 100 | } 101 | .weather-icon.thunderstorm { 102 | background-position: -450px -0px; 103 | } 104 | .weather-icon.noicon { 105 | background-image: none; 106 | } 107 | 108 | /* Global bootstrap overrides */ 109 | .navbar { 110 | margin-bottom: 0 !important; 111 | } 112 | 113 | .fixed-top { 114 | z-index: 1000; 115 | } 116 | 117 | .page-header { 118 | border: 0 !important; 119 | padding-bottom: 9px; 120 | margin: 40px 0 20px; 121 | border-bottom: 1px solid #eee; 122 | } 123 | 124 | /* notifications style overrides */ 125 | .simple-notification.error { 126 | display: block !important; 127 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpModule } from '@angular/http'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { RouterModule, Routes } from '@angular/router'; 6 | import { SimpleNotificationsModule } from 'angular2-notifications'; 7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 8 | 9 | import { AppComponent } from './app.component'; 10 | import { SearchComponent } from './components/search/search.component'; 11 | import { DashboardComponent } from './components/dashboard/dashboard.component'; 12 | import { NavbarComponent } from './components/navbar/navbar.component'; 13 | import { LoginComponent } from './components/login/login.component'; 14 | import { RegisterComponent } from './components/register/register.component'; 15 | import { HomeComponent } from './components/home/home.component'; 16 | import { ProfileComponent } from './components/profile/profile.component'; 17 | import { SubnavComponent } from './components/subnav/subnav.component'; 18 | 19 | import { WeatherService } from './services/weather.service'; 20 | import { SavedlocationsService } from './services/savedlocations.service'; 21 | import { ValidateService } from './services/validate.service'; 22 | import { AuthService } from './services/auth.service'; 23 | import { DateService } from './services/date.service'; 24 | import { AuthGuard } from './guards/auth.guard'; 25 | import { WeatherdetailsComponent } from './components/weatherdetails/weatherdetails.component'; 26 | 27 | 28 | const appRoutes: Routes = [ 29 | { 30 | path: '', 31 | component: HomeComponent 32 | }, 33 | { 34 | path: 'home', 35 | component: HomeComponent 36 | }, 37 | { 38 | path: 'register', 39 | component: RegisterComponent 40 | }, 41 | { 42 | path: 'login', 43 | component: LoginComponent 44 | }, 45 | { 46 | path: 'dashboard', 47 | component: DashboardComponent, 48 | canActivate:[AuthGuard] 49 | }, 50 | { 51 | path: 'profile', 52 | component: ProfileComponent, 53 | canActivate:[AuthGuard] 54 | }, 55 | { 56 | path: 'weather', 57 | component: WeatherdetailsComponent 58 | }, 59 | { 60 | path: 'weather/:location', 61 | component: WeatherdetailsComponent 62 | } 63 | ]; 64 | 65 | @NgModule({ 66 | declarations: [ 67 | AppComponent, 68 | SearchComponent, 69 | DashboardComponent, 70 | NavbarComponent, 71 | LoginComponent, 72 | RegisterComponent, 73 | HomeComponent, 74 | ProfileComponent, 75 | SubnavComponent, 76 | WeatherdetailsComponent 77 | ], 78 | imports: [ 79 | BrowserModule, 80 | HttpModule, 81 | FormsModule, 82 | BrowserAnimationsModule, 83 | SimpleNotificationsModule.forRoot(), 84 | RouterModule.forRoot(appRoutes) 85 | ], 86 | providers: [WeatherService,SavedlocationsService,ValidateService,AuthService,AuthGuard,DateService], 87 | bootstrap: [AppComponent] 88 | }) 89 | 90 | export class AppModule { } -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const User = require('../models/user'); 4 | const passport = require('passport'); 5 | const jwt = require('jsonwebtoken'); 6 | const config = require('../config/database'); 7 | 8 | // register 9 | router.post('/register', (req, res, next) => { 10 | let newUser = new User({ 11 | name: req.body.name, 12 | email: req.body.email, 13 | username: req.body.username, 14 | password: req.body.password, 15 | locations: [] 16 | }); 17 | 18 | User.addUser(newUser, (err, user) => { 19 | if (err) { 20 | res.json({success: false, msg: 'Failed to register user'}); 21 | } else { 22 | res.json({success: true, msg: 'User registered'}); 23 | } 24 | }); 25 | }); 26 | 27 | // authenticate 28 | router.post('/authenticate', (req, res, next) => { 29 | const username = req.body.username; 30 | const password = req.body.password; 31 | 32 | User.getUserByUsername(username, (err, user) => { 33 | if (err) throw err; 34 | if(!user) { 35 | return res.json({success: false, msg:'User not found'}); 36 | } 37 | 38 | User.comparePassword(password, user.password, (err, isMatch) => { 39 | if(err) throw err; 40 | if(isMatch) { 41 | const token = jwt.sign({data: user}, config.secret, { 42 | expiresIn: 604800 // 1 week 43 | }); 44 | 45 | res.json({ 46 | success: true, 47 | token: 'JWT ' + token, 48 | user: { 49 | id: user._id, 50 | name: user.name, 51 | username: user.username, 52 | email: user.email, 53 | locations: user.locations 54 | } 55 | }); 56 | }else { 57 | return res.json({success: false, msg:'Password does not match'}); 58 | } 59 | }) 60 | }); 61 | }); 62 | 63 | // profile 64 | router.get('/profile', passport.authenticate('jwt', {session: false}), (req, res, next) => { 65 | res.json({ user: req.user}); 66 | }); 67 | 68 | // add location 69 | router.post('/addlocation', (req, res, next) => { 70 | 71 | const userid = req.body.userid; 72 | const newlocation = req.body.location; 73 | 74 | User.addLocation(userid, newlocation, (err, model) => { 75 | if (err) { 76 | res.json({success: false, msg: err}); 77 | } else { 78 | res.json({success: true, model: model}); 79 | } 80 | }); 81 | 82 | }); 83 | 84 | // remove location 85 | router.post('/removelocation', (req, res, next) => { 86 | 87 | const userid = req.body.userid; 88 | const location = req.body.location; 89 | 90 | User.removeLocation(userid, location, (err, model) => { 91 | if (err) { 92 | res.json({success: false, msg: err}); 93 | } else { 94 | res.json({success: true, model: model}); 95 | } 96 | }); 97 | 98 | }); 99 | 100 | // remove all locations 101 | router.post('/removealllocations', (req, res, next) => { 102 | 103 | const userid = req.body.userid; 104 | 105 | User.removeAllLocations(userid, (err) => { 106 | if (err) { 107 | res.json({success: false, msg: err}); 108 | } else { 109 | res.json({success: true}); 110 | } 111 | }); 112 | 113 | }); 114 | 115 | router.get('/remove', (req, res, next) => { 116 | User.deleteUsers(() => { 117 | console.log('users removed'); 118 | }); 119 | }); 120 | 121 | module.exports = router; -------------------------------------------------------------------------------- /weatherapp-ng/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "eofline": true, 15 | "forin": true, 16 | "import-blacklist": [ 17 | true, 18 | "rxjs" 19 | ], 20 | "import-spacing": true, 21 | "indent": [ 22 | true, 23 | "spaces" 24 | ], 25 | "interface-over-type-literal": true, 26 | "label-position": true, 27 | "max-line-length": [ 28 | true, 29 | 140 30 | ], 31 | "member-access": false, 32 | "member-ordering": [ 33 | true, 34 | { 35 | "order": [ 36 | "static-field", 37 | "instance-field", 38 | "static-method", 39 | "instance-method" 40 | ] 41 | } 42 | ], 43 | "no-arg": true, 44 | "no-bitwise": true, 45 | "no-console": [ 46 | true, 47 | "debug", 48 | "info", 49 | "time", 50 | "timeEnd", 51 | "trace" 52 | ], 53 | "no-construct": true, 54 | "no-debugger": true, 55 | "no-duplicate-super": true, 56 | "no-empty": false, 57 | "no-empty-interface": true, 58 | "no-eval": true, 59 | "no-inferrable-types": [ 60 | true, 61 | "ignore-params" 62 | ], 63 | "no-misused-new": true, 64 | "no-non-null-assertion": true, 65 | "no-shadowed-variable": true, 66 | "no-string-literal": false, 67 | "no-string-throw": true, 68 | "no-switch-case-fall-through": true, 69 | "no-trailing-whitespace": true, 70 | "no-unnecessary-initializer": true, 71 | "no-unused-expression": true, 72 | "no-use-before-declare": true, 73 | "no-var-keyword": true, 74 | "object-literal-sort-keys": false, 75 | "one-line": [ 76 | true, 77 | "check-open-brace", 78 | "check-catch", 79 | "check-else", 80 | "check-whitespace" 81 | ], 82 | "prefer-const": true, 83 | "quotemark": [ 84 | true, 85 | "single" 86 | ], 87 | "radix": true, 88 | "semicolon": [ 89 | true, 90 | "always" 91 | ], 92 | "triple-equals": [ 93 | true, 94 | "allow-null-check" 95 | ], 96 | "typedef-whitespace": [ 97 | true, 98 | { 99 | "call-signature": "nospace", 100 | "index-signature": "nospace", 101 | "parameter": "nospace", 102 | "property-declaration": "nospace", 103 | "variable-declaration": "nospace" 104 | } 105 | ], 106 | "typeof-compare": true, 107 | "unified-signatures": true, 108 | "variable-name": false, 109 | "whitespace": [ 110 | true, 111 | "check-branch", 112 | "check-decl", 113 | "check-operator", 114 | "check-separator", 115 | "check-type" 116 | ], 117 | "directive-selector": [ 118 | true, 119 | "attribute", 120 | "app", 121 | "camelCase" 122 | ], 123 | "component-selector": [ 124 | true, 125 | "element", 126 | "app", 127 | "kebab-case" 128 | ], 129 | "use-input-property-decorator": true, 130 | "use-output-property-decorator": true, 131 | "use-host-property-decorator": true, 132 | "no-input-rename": true, 133 | "no-output-rename": true, 134 | "use-life-cycle-interface": true, 135 | "use-pipe-transform-interface": true, 136 | "component-class-suffix": true, 137 | "directive-class-suffix": true, 138 | "no-access-missing-member": true, 139 | "templates-use-public": true, 140 | "invoke-injectable": true 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/search/search.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ComponentFixture, TestBed, async, fakeAsync, tick, inject } from '@angular/core/testing'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { DebugElement } from '@angular/core'; 5 | import { By } from '@angular/platform-browser'; 6 | import { SearchComponent } from './search.component'; 7 | import { WeatherService } from '../../services/weather.service'; 8 | import {Router} from '@angular/router'; 9 | import { RouterTestingModule } from '@angular/router/testing'; 10 | import { Location, CommonModule } from '@angular/common'; 11 | import { HttpModule } from '@angular/http'; 12 | 13 | @Component({ 14 | template: '' 15 | }) 16 | class DummyComponent { 17 | } 18 | 19 | describe('SearchComponent', () => { 20 | let component: SearchComponent; 21 | let fixture: ComponentFixture; 22 | let de: DebugElement; 23 | let el: HTMLElement; 24 | 25 | let weatherService: WeatherService; 26 | let testWeather = { 27 | "weather":[ 28 | { 29 | "main":"Rain", 30 | "description":"moderate rain" 31 | } 32 | ], 33 | "main":{ 34 | "temp":284.81, 35 | "pressure":1018, 36 | "humidity":93, 37 | "temp_min":282.15, 38 | "temp_max":287.15 39 | }, 40 | "visibility":16093, 41 | "wind":{ 42 | "speed":1.5, 43 | "deg":170 44 | }, 45 | "clouds":{ 46 | "all":75 47 | }, 48 | "name":"Redmond" 49 | }; 50 | 51 | beforeEach(async(() => { 52 | TestBed.configureTestingModule({ 53 | declarations: [ SearchComponent, DummyComponent ], 54 | imports:[ 55 | HttpModule, 56 | RouterTestingModule.withRoutes([ 57 | { path: 'weather/Redmond', component: DummyComponent } 58 | ]), 59 | ], 60 | providers:[WeatherService] 61 | }) 62 | .compileComponents(); 63 | })); 64 | 65 | beforeEach(() => { 66 | fixture = TestBed.createComponent(SearchComponent); 67 | component = fixture.componentInstance; 68 | 69 | // WeatherService actually injected into the component 70 | weatherService = fixture.debugElement.injector.get(WeatherService); 71 | 72 | // query for the title

by CSS element selector 73 | de = fixture.debugElement.query(By.css('button')); 74 | el = de.nativeElement; 75 | 76 | // Setup spy on the 'getWeather' method 77 | spyOn(weatherService, 'getWeather') 78 | .and.returnValue(Observable.of(testWeather)); 79 | 80 | }); 81 | 82 | it('should create', () => { 83 | expect(component).toBeTruthy(); 84 | }); 85 | 86 | // test 'getWeather' from '/weather' page - async' example 87 | it('set variable after getWeather promise (async)', async(() => { 88 | component.getWeather("Redmond", false, true); 89 | 90 | fixture.detectChanges(); 91 | 92 | fixture.whenStable().then(() => { // wait for async getWeather 93 | fixture.detectChanges(); // update view with weather data 94 | expect(component.weatherRetrieved).toBe(true); 95 | }); 96 | })); 97 | 98 | 99 | // test 'getWeather' from '/weather' page - 'fakeAsync' example 100 | it('set variable after getWeather promise (fakeAsync)', fakeAsync(() => { 101 | component.getWeather("Redmond", false, true); 102 | fixture.detectChanges(); 103 | 104 | tick(3000); 105 | expect(component.weatherRetrieved).toBe(true); 106 | })); 107 | 108 | it('search from home page should redirect to weather page', 109 | async(inject([Router, Location], (router: Router, location: Location) => { 110 | component.getWeather("Redmond", false, false); 111 | 112 | fixture.detectChanges(); 113 | 114 | fixture.whenStable().then(() => { 115 | fixture.detectChanges(); 116 | expect(location.path()).toEqual('/weather/Redmond'); 117 | console.log('after expect'); 118 | }); 119 | }))); 120 | 121 | 122 | }); 123 | -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Output, EventEmitter } from '@angular/core'; 2 | import { WeatherService } from '../../services/weather.service'; 3 | import { SavedlocationsService } from '../../services/savedlocations.service'; 4 | import { NotificationsService } from 'angular2-notifications'; 5 | import {Router} from '@angular/router'; 6 | import { Observable } from 'rxjs'; 7 | 8 | @Component({ 9 | selector: 'app-dashboard', 10 | templateUrl: './dashboard.component.html', 11 | styleUrls: ['./dashboard.component.css'] 12 | }) 13 | export class DashboardComponent implements OnInit { 14 | 15 | locations: string[]; 16 | reports: Report[]; 17 | isEdit: boolean = false; 18 | editText: string = "edit"; 19 | private sub: any; 20 | 21 | @Output() 22 | changeWeather: EventEmitter = new EventEmitter(); 23 | 24 | // notification defaults 25 | public options = { 26 | position: ["top", "right"], 27 | timeOut: 3000, 28 | pauseOnHover: true, 29 | lastOnBottom: true 30 | } 31 | 32 | constructor(private weatherService:WeatherService, 33 | private savedLocationsService:SavedlocationsService, 34 | private notificationsService: NotificationsService, 35 | private router: Router 36 | ) { } 37 | 38 | ngOnInit() { 39 | 40 | if (sessionStorage.userloggedin) { 41 | sessionStorage.removeItem('userloggedin'); 42 | let ns = this.notificationsService; 43 | setTimeout(function() { 44 | ns.success("Success", "You are now logged in!", this.options); 45 | }, 30); 46 | } 47 | 48 | this.getSavedLocations(); 49 | } 50 | 51 | getSavedLocations() { 52 | this.locations = this.savedLocationsService.getSavedLocations(); 53 | this.getWeather(); 54 | } 55 | 56 | getWeather() { 57 | // make one service call for each element in the 'locations' array 58 | Observable.forkJoin(this.locations 59 | .map((element) => this.weatherService.getWeather(element))) 60 | .subscribe( 61 | (data) => this.reports = data 62 | ); 63 | } 64 | 65 | getDetails(location) { 66 | this.router.navigate(['/weather', location]); 67 | } 68 | 69 | toggleEdit() { 70 | this.isEdit = !this.isEdit; 71 | this.editText = (this.isEdit == true) ? "exit edit" : "edit"; 72 | return false; 73 | } 74 | 75 | deleteTile(location) { 76 | if (localStorage.user) { 77 | let user = JSON.parse(localStorage.user); 78 | this.savedLocationsService.deleteSavedLocation(location, user.id).subscribe((data) => { 79 | // set the new locations into session storage 80 | let updatedlocations = data.model.locations; 81 | let updateduser = JSON.parse(localStorage.user); 82 | updateduser.locations = updatedlocations; 83 | localStorage.setItem("user", JSON.stringify(updateduser)); 84 | this.getSavedLocations(); 85 | }); 86 | }else { 87 | console.log('Error removing tile'); 88 | } 89 | return false; 90 | } 91 | 92 | clearAll() { 93 | if (localStorage.user) { 94 | let user = JSON.parse(localStorage.user); 95 | this.savedLocationsService.deleteAllSavedLocations(user.id).subscribe(() => { 96 | this.reports = []; 97 | // clear the locations from session storage 98 | let updateduser = JSON.parse(localStorage.user); 99 | updateduser.locations = []; 100 | localStorage.setItem("user", JSON.stringify(updateduser)); 101 | }); 102 | }else { 103 | console.log('Error clearing locations'); 104 | } 105 | return false; 106 | } 107 | 108 | convertTemp(temp) { 109 | return Math.ceil((9/5*(temp - 273))+32) + String.fromCharCode(176) + "F" + " / " + Math.ceil(temp - 273.15) + String.fromCharCode(176) + "C"; 110 | } 111 | 112 | removeWhitespace(str) { 113 | return str.replace(" ", ""); 114 | } 115 | 116 | } 117 | 118 | interface Report{ 119 | name:string, 120 | temp:number, 121 | conditions:string 122 | } 123 | -------------------------------------------------------------------------------- /test/usersTest.js: -------------------------------------------------------------------------------- 1 | let mongoose = require('mongoose'); 2 | let User = require('../models/user'); 3 | 4 | let chai = require('chai'); 5 | let chaiHttp = require('chai-http'); 6 | let should = chai.should(); 7 | 8 | chai.use(chaiHttp); 9 | 10 | describe('User', function() { 11 | 12 | let testid = null; 13 | 14 | beforeEach((done) => { 15 | //empty the database before each test 16 | /* this isn't working from the test spec for some reason */ 17 | User.remove({}, function(err) { 18 | console.log(err); 19 | done(); 20 | }); 21 | done(); // temp to prevent failure, but causes an 'evergreen' test 22 | 23 | }); 24 | 25 | /* 26 | * Test registering a new user 27 | */ 28 | describe('/POST register new user', () => { 29 | it('it should register a new user and save to the database', (done) => { 30 | const testuser = { 31 | "name": "test user", 32 | "email": "testuser@gmail.com", 33 | "username": "testuser", 34 | "password": "testuser", 35 | "locations": ["Seattle","Miami"] 36 | } 37 | chai.request('http://localhost:3000') 38 | .post('/users/register') 39 | .set('content-type', 'application/json') 40 | .send(testuser) 41 | .end((err, res) => { 42 | res.should.have.status(200); 43 | res.body.should.be.a('object'); 44 | res.body.should.have.property('success'); 45 | res.body.should.have.property('success').eql(true); 46 | res.body.should.have.property('msg'); 47 | done(); 48 | }); 49 | }); 50 | }); 51 | 52 | /* 53 | * Test authenticating a new user 54 | */ 55 | describe('/POST authenticate user', () => { 56 | it('it should successfully authenticate user', (done) => { 57 | const testauthuser = { 58 | "username": "testuser", 59 | "password": "testuser", 60 | } 61 | chai.request('http://localhost:3000') 62 | .post('/users/authenticate') 63 | .set('content-type', 'application/json') 64 | .send(testauthuser) 65 | .end((err, res) => { 66 | res.should.have.status(200); 67 | res.body.should.be.a('object'); 68 | res.body.should.have.property('success'); 69 | res.body.should.have.property('success').eql(true); 70 | res.body.should.have.property('token'); 71 | res.body.should.have.property('user'); 72 | testid = res.body.user.id; // needed for further tests 73 | 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | /* 80 | * Test adding a new location to user's saved locations 81 | */ 82 | describe('/POST add new location user', () => { 83 | it('it should successfully add a new location', (done) => { 84 | const testnewloc = { 85 | "userid": testid, 86 | "location": "Copenhagen" 87 | } 88 | 89 | chai.request('http://localhost:3000') 90 | .post('/users/addlocation') 91 | .set('content-type', 'application/json') 92 | .send(testnewloc) 93 | .end((err, res) => { 94 | res.should.have.status(200); 95 | res.body.should.be.a('object'); 96 | res.body.should.have.property('success'); 97 | res.body.should.have.property('success').eql(true); 98 | res.body.should.have.property('model'); 99 | done(); 100 | }); 101 | }); 102 | }); 103 | 104 | /* 105 | * Test removing a location to user's saved locations 106 | */ 107 | describe('/POST remove location user', () => { 108 | it('it should successfully remove a location', (done) => { 109 | const testremoveloc = { 110 | "userid": testid, 111 | "location": "Copenhagen" 112 | } 113 | 114 | chai.request('http://localhost:3000') 115 | .post('/users/removelocation') 116 | .set('content-type', 'application/json') 117 | .send(testremoveloc) 118 | .end((err, res) => { 119 | res.should.have.status(200); 120 | res.body.should.be.a('object'); 121 | res.body.should.have.property('success'); 122 | res.body.should.have.property('success').eql(true); 123 | res.body.should.have.property('model'); 124 | done(); 125 | }); 126 | }); 127 | }); 128 | 129 | }); -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/weatherdetails/weatherdetails.component.css: -------------------------------------------------------------------------------- 1 | .current-weather-tile { 2 | width: 50%; 3 | float: left; 4 | /* border: 1px solid #ddd; */ 5 | border-radius: 8px; 6 | margin: 15px 20px 20px 10px; 7 | padding: 18px; 8 | font-size: 16px; 9 | height: 312px; 10 | background-color: white; 11 | } 12 | 13 | #extended-forecast { 14 | width: 100%; 15 | float: left; 16 | clear: both; 17 | } 18 | 19 | .forecast { 20 | width: 18%; 21 | float: left; 22 | border-radius: 8px; 23 | margin: 20px 10px; 24 | padding: 18px; 25 | font-size: 16px; 26 | height: 312px; 27 | background-color: white; 28 | } 29 | 30 | .current-weather-tile .tile-title { 31 | width: 100%; 32 | float: left; 33 | } 34 | 35 | 36 | .current-weather-tile #tile-main { 37 | width: 100%; 38 | float: left; 39 | clear: both; 40 | } 41 | 42 | .current-weather-tile .tile-left { 43 | width: 50%; 44 | float: left; 45 | } 46 | 47 | .current-weather-tile .tile-left .weather-icon { 48 | margin: auto; 49 | } 50 | 51 | .current-weather-tile .tile-right { 52 | width: 50%; 53 | float: left; 54 | } 55 | 56 | .current-weather-tile .tile-title h3 { 57 | float: left; 58 | font-size: 36px !important; 59 | } 60 | 61 | .current-weather-tile #description { 62 | margin-bottom: 0px; 63 | } 64 | 65 | .current-weather-tile #temp-main { 66 | margin: 30px auto 10px auto; 67 | width: 75%; 68 | text-align: center; 69 | } 70 | 71 | .current-weather-tile #temp-main .result.temp { 72 | padding: 20px 20px 0px 20px; 73 | font-size: 66px; 74 | } 75 | 76 | .current-weather-tile #hi-lo-temp { 77 | clear: both; 78 | padding-bottom: 30px; 79 | } 80 | 81 | .current-weather-tile #addlinfo { 82 | margin: 20px auto; 83 | text-align: center; 84 | } 85 | 86 | .current-weather-tile #temp-main #hi-lo-temp.result.temp { 87 | font-size: 16px; 88 | padding-top: 0; 89 | margin-top: 20px; 90 | } 91 | 92 | .current-weather-tile #description, 93 | .componenttitle, 94 | .grey { 95 | color: #555; 96 | } 97 | 98 | .current-weather-tile div.addtosaved { 99 | float: right; 100 | margin-bottom: 0px !important; 101 | } 102 | 103 | .current-weather-tile div#date, 104 | .current-weather-tile #description, 105 | .current-weather-tile #temp-main { 106 | margin-bottom: 5px; 107 | } 108 | 109 | .forecast { 110 | text-align: center; 111 | } 112 | 113 | .forecast h5 { 114 | color: #555; 115 | } 116 | 117 | .forecast .lotempcolor { 118 | color: #5FCBE7; 119 | } 120 | 121 | .forecast .grey { 122 | color: #ccc; 123 | } 124 | 125 | .forecast .temp-hi { 126 | font-size: 42px; 127 | margin-top: 10px auto; 128 | padding-top: 10px; 129 | } 130 | 131 | .forecast .forecast-description { 132 | color: #05E177; 133 | } 134 | 135 | /* 5 day forecast icons */ 136 | 137 | .forecast .weather-icon { 138 | width: 120px; 139 | height: 120px; 140 | background-image: url("../../../assets/forecasticons.png"); 141 | background-repeat: no-repeat; 142 | margin: 0px auto 10px auto; 143 | position: relative; 144 | top: -10px; 145 | left: -10px; 146 | } 147 | 148 | .forecast .weather-icon.clearsky { 149 | background-position: -240px -240px; 150 | } 151 | 152 | .forecast .weather-icon.snow, .forecast .weather-icon.lightrainandsnow { 153 | background-position: -240px -120px; 154 | } 155 | 156 | .forecast .weather-icon.scatteredclouds, .forecast .weather-icon.brok-cloud { 157 | background-position: -240px 0px; 158 | } 159 | 160 | .forecast .weather-icon.lightrain, 161 | .forecast .weather-icon.moderaterain, 162 | .forecast .weather-icon.lightintensityshowerrain, 163 | .forecast .weather-icon[class*="rain"] 164 | { 165 | background-position: 0px -120px; 166 | } 167 | 168 | .forecast .weather-icon.showerrain { 169 | background-position: -120px -120px; 170 | } 171 | 172 | .forecast .weather-icon.fog, .forecast .weather-icon.haze, .forecast .weather-icon.mist { 173 | background-position: -120px -360px; 174 | } 175 | 176 | .forecast .weather-icon.overcast clouds, .forecast .weather-icon.fewclouds { 177 | background-position: -240px -0px; 178 | } 179 | .forecast .weather-icon.thunderstorm { 180 | background-position: -360px -0px; 181 | } 182 | .forecast .weather-icon.noicon { 183 | background-image: none; 184 | } -------------------------------------------------------------------------------- /weatherapp-ng/src/app/components/weatherdetails/weatherdetails.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WeatherService } from '../../services/weather.service'; 3 | import { SavedlocationsService } from '../../services/savedlocations.service'; 4 | import { DateService } from '../../services/date.service'; 5 | import { NotificationsService } from 'angular2-notifications'; 6 | 7 | @Component({ 8 | selector: 'app-weatherdetails', 9 | templateUrl: './weatherdetails.component.html', 10 | styleUrls: ['./weatherdetails.component.css'] 11 | }) 12 | export class WeatherdetailsComponent implements OnInit { 13 | 14 | location:string; 15 | report:Report; 16 | forecastReport:ForecastReport; 17 | date: String; 18 | 19 | // notification defaults 20 | public options = { 21 | position: ["top", "right"], 22 | timeOut: 2000, 23 | lastOnBottom: true 24 | } 25 | 26 | constructor(private weatherService:WeatherService, 27 | private savedLocationsService:SavedlocationsService, 28 | private notificationsService: NotificationsService, 29 | private dateService: DateService 30 | ) { } 31 | 32 | ngOnInit() { 33 | this.date = this.dateService.getDayAndTime(); 34 | } 35 | 36 | updateWeather(event) { 37 | this.report = event; 38 | }; 39 | 40 | updateForecast(event) { 41 | 42 | let data = event; 43 | let fiveDayArray = [[],[],[],[],[],[],[]]; 44 | let weekArry = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 45 | let hourArry = ["12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]; 46 | 47 | let dt = new Date(); 48 | let dtoday = dt.getDay(); 49 | 50 | let resultArray = []; 51 | 52 | let putIndex = 0; 53 | let lastDay = dtoday + 1; 54 | 55 | // populte fiveDayArray with reports by day for further processing 56 | data.list.forEach(function(forecast){ 57 | let d = new Date(forecast.dt * 1000); 58 | let day = d.getDay(); 59 | let hour = d.getHours(); 60 | 61 | // filter out any forecasts for today and order the rest by day 62 | if (day != dtoday) { 63 | // convert dt to day value 64 | forecast.day = weekArry[day]; 65 | forecast.hour = hourArry[hour]; 66 | forecast.hourMil = hour; 67 | // add forecasts to the fiveDayArray in the right order 68 | if ((day > lastDay) || ((lastDay===6)&&(day===0))) { 69 | lastDay = day; 70 | putIndex++; 71 | } 72 | fiveDayArray[putIndex].push(forecast); 73 | } 74 | 75 | }); 76 | 77 | fiveDayArray.forEach(function(forecastgroup){ 78 | 79 | let thisForecast = { "day":"", "hi":0, "lo":0,"description":""}; 80 | 81 | let hitemp = -999; 82 | let lotemp = 999; 83 | 84 | if(forecastgroup.length > 0) { 85 | // inner loop - iterate forecasts for a particular day 86 | forecastgroup.forEach(function(forecast) { 87 | 88 | // get description for midday forecast 89 | if(forecast.hourMil > 10 && forecast.hourMil < 14) { 90 | thisForecast.description = forecast.weather[0].description; 91 | thisForecast.day = forecast.day; 92 | } 93 | 94 | // set day's hi temp 95 | if (forecast.main.temp_max > hitemp) { 96 | hitemp = forecast.main.temp_max; 97 | } 98 | 99 | // set day's lo temp 100 | if (forecast.main.temp_min < lotemp) { 101 | lotemp = forecast.main.temp_min; 102 | } 103 | 104 | }); 105 | 106 | thisForecast.hi = hitemp; 107 | thisForecast.lo = lotemp; 108 | 109 | // push the final 'combned' forecast for each day to the results array 110 | resultArray.push(thisForecast); 111 | 112 | } 113 | 114 | }); 115 | 116 | // set the results in the class property for binding 117 | data.list = resultArray; 118 | this.forecastReport = data; 119 | }; 120 | 121 | addToSavedLocations(location) { 122 | if (localStorage.user) { 123 | let user = JSON.parse(localStorage.user); 124 | this.savedLocationsService.addSavedLocation(location, user.id).subscribe((data) => { 125 | // set the new locations into session storage 126 | let updatedlocations = data.model.locations; 127 | let updateduser = JSON.parse(localStorage.user); 128 | updateduser.locations = updatedlocations; 129 | localStorage.setItem("user", JSON.stringify(updateduser)); 130 | // alert user of success 131 | this.notificationsService.success("Success", "Location saved to dashboard", this.options); 132 | }); 133 | 134 | }else { 135 | // alert user of error 136 | this.notificationsService.error("Error", "Please log in or register in order to save a location.", this.options); 137 | } 138 | return false; 139 | } 140 | 141 | convertTemp(temp, scale, nosign) { 142 | if (scale == "C") { 143 | let S = (nosign) ? "" : "C"; 144 | Math.ceil(temp - 273.15) + String.fromCharCode(176) + S; 145 | }else { 146 | let S = (nosign) ? "" : "F"; 147 | return Math.ceil((9/5*(temp - 273))+32) + String.fromCharCode(176) + S; 148 | } 149 | 150 | } 151 | 152 | removeWhitespace(str) { 153 | return str.replace(" ", ""); 154 | } 155 | 156 | } 157 | 158 | interface Report{ 159 | name:string, 160 | temp:number, 161 | conditions:string 162 | } 163 | 164 | interface ForecastReport{ 165 | list:any 166 | } -------------------------------------------------------------------------------- /public/inline.bundle.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack/bootstrap 3ddc0a98b38f73ad0867"],"names":[],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAQ,oBAAoB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAY,2BAA2B;AACvC;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,kDAA0C,WAAW,EAAE;AACvD;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,YAAI;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA,kDAA0C,oBAAoB,WAAW","file":"inline.bundle.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t\"inline\": 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData === 0) {\n \t\t\treturn new Promise(function(resolve) { resolve(); });\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunkData) {\n \t\t\treturn installedChunkData[2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunkData[2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = 'text/javascript';\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"\" + chunkId + \".chunk.js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 3ddc0a98b38f73ad0867"],"sourceRoot":"webpack:///"} -------------------------------------------------------------------------------- /public/inline.bundle.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // install a JSONP callback for chunk loading 3 | /******/ var parentJsonpFunction = window["webpackJsonp"]; 4 | /******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { 5 | /******/ // add "moreModules" to the modules object, 6 | /******/ // then flag all "chunkIds" as loaded and fire callback 7 | /******/ var moduleId, chunkId, i = 0, resolves = [], result; 8 | /******/ for(;i < chunkIds.length; i++) { 9 | /******/ chunkId = chunkIds[i]; 10 | /******/ if(installedChunks[chunkId]) { 11 | /******/ resolves.push(installedChunks[chunkId][0]); 12 | /******/ } 13 | /******/ installedChunks[chunkId] = 0; 14 | /******/ } 15 | /******/ for(moduleId in moreModules) { 16 | /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { 17 | /******/ modules[moduleId] = moreModules[moduleId]; 18 | /******/ } 19 | /******/ } 20 | /******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules); 21 | /******/ while(resolves.length) { 22 | /******/ resolves.shift()(); 23 | /******/ } 24 | /******/ if(executeModules) { 25 | /******/ for(i=0; i < executeModules.length; i++) { 26 | /******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]); 27 | /******/ } 28 | /******/ } 29 | /******/ return result; 30 | /******/ }; 31 | /******/ 32 | /******/ // The module cache 33 | /******/ var installedModules = {}; 34 | /******/ 35 | /******/ // objects to store loaded and loading chunks 36 | /******/ var installedChunks = { 37 | /******/ "inline": 0 38 | /******/ }; 39 | /******/ 40 | /******/ // The require function 41 | /******/ function __webpack_require__(moduleId) { 42 | /******/ 43 | /******/ // Check if module is in cache 44 | /******/ if(installedModules[moduleId]) { 45 | /******/ return installedModules[moduleId].exports; 46 | /******/ } 47 | /******/ // Create a new module (and put it into the cache) 48 | /******/ var module = installedModules[moduleId] = { 49 | /******/ i: moduleId, 50 | /******/ l: false, 51 | /******/ exports: {} 52 | /******/ }; 53 | /******/ 54 | /******/ // Execute the module function 55 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 56 | /******/ 57 | /******/ // Flag the module as loaded 58 | /******/ module.l = true; 59 | /******/ 60 | /******/ // Return the exports of the module 61 | /******/ return module.exports; 62 | /******/ } 63 | /******/ 64 | /******/ // This file contains only the entry chunk. 65 | /******/ // The chunk loading function for additional chunks 66 | /******/ __webpack_require__.e = function requireEnsure(chunkId) { 67 | /******/ var installedChunkData = installedChunks[chunkId]; 68 | /******/ if(installedChunkData === 0) { 69 | /******/ return new Promise(function(resolve) { resolve(); }); 70 | /******/ } 71 | /******/ 72 | /******/ // a Promise means "currently loading". 73 | /******/ if(installedChunkData) { 74 | /******/ return installedChunkData[2]; 75 | /******/ } 76 | /******/ 77 | /******/ // setup Promise in chunk cache 78 | /******/ var promise = new Promise(function(resolve, reject) { 79 | /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject]; 80 | /******/ }); 81 | /******/ installedChunkData[2] = promise; 82 | /******/ 83 | /******/ // start chunk loading 84 | /******/ var head = document.getElementsByTagName('head')[0]; 85 | /******/ var script = document.createElement('script'); 86 | /******/ script.type = 'text/javascript'; 87 | /******/ script.charset = 'utf-8'; 88 | /******/ script.async = true; 89 | /******/ script.timeout = 120000; 90 | /******/ 91 | /******/ if (__webpack_require__.nc) { 92 | /******/ script.setAttribute("nonce", __webpack_require__.nc); 93 | /******/ } 94 | /******/ script.src = __webpack_require__.p + "" + chunkId + ".chunk.js"; 95 | /******/ var timeout = setTimeout(onScriptComplete, 120000); 96 | /******/ script.onerror = script.onload = onScriptComplete; 97 | /******/ function onScriptComplete() { 98 | /******/ // avoid mem leaks in IE. 99 | /******/ script.onerror = script.onload = null; 100 | /******/ clearTimeout(timeout); 101 | /******/ var chunk = installedChunks[chunkId]; 102 | /******/ if(chunk !== 0) { 103 | /******/ if(chunk) { 104 | /******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); 105 | /******/ } 106 | /******/ installedChunks[chunkId] = undefined; 107 | /******/ } 108 | /******/ }; 109 | /******/ head.appendChild(script); 110 | /******/ 111 | /******/ return promise; 112 | /******/ }; 113 | /******/ 114 | /******/ // expose the modules object (__webpack_modules__) 115 | /******/ __webpack_require__.m = modules; 116 | /******/ 117 | /******/ // expose the module cache 118 | /******/ __webpack_require__.c = installedModules; 119 | /******/ 120 | /******/ // define getter function for harmony exports 121 | /******/ __webpack_require__.d = function(exports, name, getter) { 122 | /******/ if(!__webpack_require__.o(exports, name)) { 123 | /******/ Object.defineProperty(exports, name, { 124 | /******/ configurable: false, 125 | /******/ enumerable: true, 126 | /******/ get: getter 127 | /******/ }); 128 | /******/ } 129 | /******/ }; 130 | /******/ 131 | /******/ // getDefaultExport function for compatibility with non-harmony modules 132 | /******/ __webpack_require__.n = function(module) { 133 | /******/ var getter = module && module.__esModule ? 134 | /******/ function getDefault() { return module['default']; } : 135 | /******/ function getModuleExports() { return module; }; 136 | /******/ __webpack_require__.d(getter, 'a', getter); 137 | /******/ return getter; 138 | /******/ }; 139 | /******/ 140 | /******/ // Object.prototype.hasOwnProperty.call 141 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 142 | /******/ 143 | /******/ // __webpack_public_path__ 144 | /******/ __webpack_require__.p = ""; 145 | /******/ 146 | /******/ // on error function for async loading 147 | /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; 148 | /******/ }) 149 | /************************************************************************/ 150 | /******/ ([]); 151 | //# sourceMappingURL=inline.bundle.js.map -------------------------------------------------------------------------------- /public/main.bundle.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["c:/Projects/weatherapp/weatherapp-ng/src/$_gendir lazy","c:/Projects/weatherapp/weatherapp-ng/src/app/app.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/app.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/app.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/app.module.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/dashboard/dashboard.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/dashboard/dashboard.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/dashboard/dashboard.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/home/home.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/home/home.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/home/home.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/login/login.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/login/login.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/login/login.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/navbar/navbar.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/navbar/navbar.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/navbar/navbar.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/profile/profile.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/profile/profile.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/profile/profile.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/register/register.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/register/register.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/register/register.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/search/search.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/search/search.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/search/search.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/components/subnav/subnav.component.css","c:/Projects/weatherapp/weatherapp-ng/src/app/components/subnav/subnav.component.html","c:/Projects/weatherapp/weatherapp-ng/src/app/components/subnav/subnav.component.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/guards/auth.guard.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/services/auth.service.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/services/savedlocations.service.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/services/validate.service.ts","c:/Projects/weatherapp/weatherapp-ng/src/app/services/weather.service.ts","c:/Projects/weatherapp/weatherapp-ng/src/environments/environment.ts","c:/Projects/weatherapp/weatherapp-ng/src/main.ts"],"names":[],"mappings":";;;;;AAAA;AACA;AACA;AACA;AACA;AACA,EAAE;AACF;AACA,4CAA4C,WAAW;AACvD;AACA;AACA,4E;;;;;;;ACVA;AACA;;;AAGA;AACA,kCAAmC,qBAAqB,oBAAoB,+BAA+B,qBAAqB,+BAA+B,KAAK,oBAAoB,+BAA+B,KAAK;;AAE5N;;;AAGA;AACA,2C;;;;;;;ACXA,+I;;;;;;;;;;;;;;;;ACA0C;AAO1C,IAAa,YAAY;IALzB;QAME,UAAK,GAAG,cAAc,CAAC;IACzB,CAAC;IAAD,mBAAC;AAAD,CAAC;AAFY,YAAY;IALxB,gEAAS,CAAC;QACT,QAAQ,EAAE,UAAU;QACpB,0EAAmC;QACnC,yEAAkC;KACnC,CAAC;GACW,YAAY,CAExB;AAFwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPiC;AACjB;AACE;AACE;AACY;AACU;AACY;AAEhC;AACwB;AACS;AACT;AACH;AACS;AACZ;AACS;AAEd;AACc;AACZ;AACR;AACiB;AACvB;AAGhD,IAAM,SAAS,GAAW;IACxB;QACE,IAAI,EAAE,EAAE;QACR,UAAU,EAAE,OAAO;QACnB,SAAS,EAAE,MAAM;KAClB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,uFAAa;KACzB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,mGAAiB;KAC7B;IACD;QACE,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,0FAAc;KAC1B;IACD;QACE,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,qGAAkB;QAC7B,WAAW,EAAC,CAAC,sEAAS,CAAC;KACxB;IACD;QACE,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,gGAAgB;QAC3B,WAAW,EAAC,CAAC,sEAAS,CAAC;KACxB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,SAAS,EAAE,4FAAe;KAC3B;CACF,CAAC;AA0BF,IAAa,SAAS;IAAtB;IAAyB,CAAC;IAAD,gBAAC;AAAD,CAAC;AAAb,SAAS;IAxBrB,+DAAQ,CAAC;QACR,YAAY,EAAE;YACZ,oEAAY;YACZ,4FAAe;YACf,qGAAkB;YAClB,6FAAe;YACf,0FAAc;YACd,mGAAiB;YACjB,uFAAa;YACb,gGAAgB;YAChB,6FAAe;SAChB;QACD,OAAO,EAAE;YACP,wEAAa;YACb,yDAAU;YACV,mEAAW;YACX,qGAAuB;YACvB,iFAAyB,CAAC,OAAO,EAAE;YACnC,qEAAY,CAAC,OAAO,CAAC,SAAS,CAAC;SAChC;QACD,SAAS,EAAE,CAAC,kFAAc,EAAC,gGAAqB,EAAC,oFAAe,EAAC,4EAAW,EAAC,sEAAS,CAAC;QACvF,SAAS,EAAE,CAAC,oEAAY,CAAC;KAC1B,CAAC;GAEW,SAAS,CAAI;AAAJ;;;;;;;;ACnFtB;AACA;;;AAGA;AACA,6BAA8B,wBAAwB,KAAK,sBAAsB,wBAAwB,yBAAyB,KAAK,yBAAyB,2BAA2B,KAAK,sBAAsB,oBAAoB,oBAAoB,+BAA+B,2BAA2B,yBAAyB,sBAAsB,wBAAwB,oBAAoB,gCAAgC,KAAK,iBAAiB,wBAAwB,KAAK,qBAAqB,mBAAmB,oBAAoB,kCAAkC,8BAA8B,oCAAoC,sBAAsB,wBAAwB,0BAA0B,gCAAgC,KAAK,2BAA2B,8BAA8B,KAAK,oCAAoC,6BAA6B,KAAK,6EAA6E,oBAAoB,KAAK,kCAAkC,wBAAwB,KAAK;;AAE5hC;;;AAGA;AACA,2C;;;;;;;ACXA,kVAAkV,UAAU,sXAAsX,aAAa,4FAA4F,+BAA+B,yHAAyH,+BAA+B,6DAA6D,iDAAiD,sL;;;;;;;;;;;;;;;;;;;;;;;;;ACA9iC;AACc;AACc;AAChB;AAC5B;AAOlC,IAAa,kBAAkB;IAe7B,4BAAoB,cAA6B,EAC7B,qBAA2C,EAC3C,oBAA0C;QAF1C,mBAAc,GAAd,cAAc,CAAe;QAC7B,0BAAqB,GAArB,qBAAqB,CAAsB;QAC3C,yBAAoB,GAApB,oBAAoB,CAAsB;QAb9D,WAAM,GAAY,KAAK,CAAC;QACxB,aAAQ,GAAW,MAAM,CAAC;QAE1B,wBAAwB;QACjB,YAAO,GAAG;YACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACnB;IAKa,CAAC;IAEf,qCAAQ,GAAR;QAEE,EAAE,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;YAChC,cAAc,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAC1C,IAAI,IAAE,GAAG,IAAI,CAAC,oBAAoB,CAAC;YACnC,UAAU,CAAC;gBACT,IAAE,CAAC,OAAO,CAAC,SAAS,EAAE,wBAAwB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAChE,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED,8CAAiB,GAAjB;QACE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,CAAC;QAChE,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,uCAAU,GAAV;QAAA,iBAOC;QANC,kEAAkE;QAClE,gDAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS;aAC/B,GAAG,CAAC,UAAC,OAAO,IAAK,YAAI,CAAC,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,EAAvC,CAAuC,CAAC,CAAC;aAC1D,SAAS,CACR,UAAC,IAAI,IAAK,YAAI,CAAC,OAAO,GAAG,IAAI,EAAnB,CAAmB,CAChC,CAAC;IACJ,CAAC;IAED,uCAAU,GAAV;QACE,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,WAAW,GAAG,MAAM,CAAC;QAC7D,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAED,uCAAU,GAAV,UAAW,QAAQ;QAAnB,iBAeC;QAdC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,qBAAqB,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,UAAC,IAAI;gBAC/E,6CAA6C;gBAC7C,IAAI,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC5C,IAAI,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAChD,WAAW,CAAC,SAAS,GAAG,gBAAgB,CAAC;gBACzC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;gBAC1D,KAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,CAAC,CAAC,CAAC;QACL,CAAC;QAAA,IAAI,CAAC,CAAC;YACL,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAED,qCAAQ,GAAR;QAAA,iBAcC;QAbC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,qBAAqB,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;gBACpE,KAAI,CAAC,OAAO,GAAG,EAAE,CAAC;gBAClB,2CAA2C;gBAC3C,IAAI,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAChD,WAAW,CAAC,SAAS,GAAG,EAAE,CAAC;gBAC3B,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;QACL,CAAC;QAAA,IAAI,CAAC,CAAC;YACL,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAED,wCAAW,GAAX,UAAY,IAAI;QACd,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,IAAI,GAAI,GAAG,CAAC,CAAC,GAAC,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAChJ,CAAC;IAED,6CAAgB,GAAhB,UAAiB,GAAG;QAClB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9B,CAAC;IAEH,yBAAC;AAAD,CAAC;AA9FY,kBAAkB;IAL9B,gEAAS,CAAC;QACT,QAAQ,EAAE,eAAe;QACzB,qGAAyC;QACzC,oGAAwC;KACzC,CAAC;yDAgBmC,iFAAc,oBAAd,iFAAc,sDACP,+FAAqB,oBAArB,+FAAqB,sDACrB,4EAAoB,oBAApB,4EAAoB;GAjBnD,kBAAkB,CA8F9B;AA9F8B;;;;;;;;;ACX/B;AACA;;;AAGA;AACA,qCAAsC,oBAAoB,2BAA2B,KAAK;;AAE1F;;;AAGA;AACA,2C;;;;;;;ACXA,gpC;;;;;;;;;;;;;;;;;;;;;;ACAkD;AACY;AACJ;AAO1D,IAAa,aAAa;IAUxB,uBAAoB,oBAA0C,EAC1C,WAAwB;QADxB,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,gBAAW,GAAX,WAAW,CAAa;QAT5C,wBAAwB;QACjB,YAAO,GAAG;YACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACnB;IAIc,CAAC;IAEhB,gCAAQ,GAAR;QAEE,yCAAyC;QACzC,EAAE,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;YAC9B,cAAc,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACxC,IAAI,IAAE,GAAG,IAAI,CAAC,oBAAoB,CAAC;YACnC,UAAU,CAAC;gBACT,IAAE,CAAC,OAAO,CAAC,SAAS,EAAE,yBAAyB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACjE,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC;IAEH,CAAC;IAEH,oBAAC;AAAD,CAAC;AA3BY,aAAa;IALzB,gEAAS,CAAC;QACT,QAAQ,EAAE,UAAU;QACpB,2FAAoC;QACpC,0FAAmC;KACpC,CAAC;yDAW0C,4EAAoB,oBAApB,4EAAoB,sDAC7B,2EAAW,oBAAX,2EAAW;GAXjC,aAAa,CA2BzB;AA3ByB;;;;;;;;;ACT1B;AACA;;;AAGA;AACA,+BAAgC,yBAAyB,KAAK;;AAE9D;;;AAGA;AACA,2C;;;;;;;ACXA,6oB;;;;;;;;;;;;;;;;;;;;;;;ACAkD;AACY;AACJ;AACnB;AAOvC,IAAa,cAAc;IAazB,wBAAoB,oBAA0C,EAC1C,WAAwB,EACxB,MAAc;QAFd,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,gBAAW,GAAX,WAAW,CAAa;QACxB,WAAM,GAAN,MAAM,CAAQ;QAVlC,wBAAwB;QACjB,YAAO,GAAG;YACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACnB;IAKc,CAAC;IAEhB,iCAAQ,GAAR;QAEE,EAAE,CAAC,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACrC,cAAc,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;YAC/C,IAAI,IAAE,GAAG,IAAI,CAAC,oBAAoB,CAAC;YACnC,UAAU,CAAC;gBACT,IAAE,CAAC,OAAO,CAAC,SAAS,EAAE,yBAAyB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACjE,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC;IAEH,CAAC;IAED,sCAAa,GAAb;QAAA,iBAgBC;QAfC,IAAM,IAAI,GAAG;YACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB;QAED,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAI;YACpD,EAAE,EAAC,IAAI,CAAC,OAAO,CAAC,EAAC;gBACf,KAAI,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtD,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;gBAC/C,KAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;YACvC,CAAC;YAAA,IAAI,CAAC,CAAC;gBACL,KAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,KAAI,CAAC,OAAO,CAAC,CAAC;YACnE,CAAC;QACH,CAAC,CAAC,CAAC;IAEL,CAAC;IAEH,qBAAC;AAAD,CAAC;AAhDY,cAAc;IAL1B,gEAAS,CAAC;QACT,QAAQ,EAAE,WAAW;QACrB,6FAAqC;QACrC,4FAAoC;KACrC,CAAC;yDAc0C,4EAAoB,oBAApB,4EAAoB,sDAC7B,2EAAW,oBAAX,2EAAW,sDAChB,+DAAM,oBAAN,+DAAM;GAfvB,cAAc,CAgD1B;AAhD0B;;;;;;;;;ACV3B;AACA;;;AAGA;AACA,kEAAmE,+BAA+B,yBAAyB,KAAK,8BAA8B,qBAAqB,KAAK,2BAA2B,wBAAwB,KAAK,2BAA2B,qBAAqB,KAAK,8BAA8B,2BAA2B,KAAK;;AAEnW;;;AAGA;AACA,2C;;;;;;;ACXA,+sBAA+sB,UAAU,mHAAmH,WAAW,uLAAuL,WAAW,iOAAiO,WAAW,oOAAoO,WAAW,iOAAiO,WAAW,6NAA6N,WAAW,kT;;;;;;;;;;;;;;;;;;;;;;;ACAt5D;AACY;AACJ;AACnB;AAOvC,IAAa,eAAe;IAY1B,yBAAoB,oBAA0C,EAC1C,WAAwB,EACxB,MAAc;QAFd,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,gBAAW,GAAX,WAAW,CAAa;QACxB,WAAM,GAAN,MAAM,CAAQ;QAVlC,wBAAwB;QACjB,YAAO,GAAG;YACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACnB;IAKc,CAAC;IAEhB,kCAAQ,GAAR;QACE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAChC,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YACpD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,uCAAa,GAAb;QACE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QAC1B,cAAc,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAEH,sBAAC;AAAD,CAAC;AA/BY,eAAe;IAL3B,gEAAS,CAAC;QACT,QAAQ,EAAE,YAAY;QACtB,+FAAsC;QACtC,8FAAqC;KACtC,CAAC;yDAa0C,4EAAoB,oBAApB,4EAAoB,sDAC7B,2EAAW,oBAAX,2EAAW,sDAChB,+DAAM,oBAAN,+DAAM;GAdvB,eAAe,CA+B3B;AA/B2B;;;;;;;;;ACV5B;AACA;;;AAGA;AACA;;AAEA;;;AAGA;AACA,2C;;;;;;;ACXA,sEAAsE,WAAW,gFAAgF,WAAW,qDAAqD,eAAe,kDAAkD,YAAY,yB;;;;;;;;;;;;;;;;;;;;;ACA5P;AACQ;AACnB;AAOvC,IAAa,gBAAgB;IAI3B,0BAAoB,WAAwB,EACxB,MAAc;QADd,gBAAW,GAAX,WAAW,CAAa;QACxB,WAAM,GAAN,MAAM,CAAQ;IACnB,CAAC;IAEhB,mCAAQ,GAAR;QAAA,iBAQC;QAPG,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC,iBAAO;YAC/C,KAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC3B,CAAC,CAAC;YACF,aAAG;gBACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACjB,MAAM,CAAC,KAAK,CAAC;YACf,CAAC;IACH,CAAC;IAEH,uBAAC;AAAD,CAAC;AAlBY,gBAAgB;IAL5B,gEAAS,CAAC;QACT,QAAQ,EAAE,aAAa;QACvB,iGAAuC;QACvC,gGAAsC;KACvC,CAAC;yDAKiC,2EAAW,oBAAX,2EAAW,sDAChB,+DAAM,oBAAN,+DAAM;GALvB,gBAAgB,CAkB5B;AAlB4B;;;;;;;;;ACT7B;AACA;;;AAGA;AACA;;AAEA;;;AAGA;AACA,2C;;;;;;;ACXA,k3B;;;;;;;;;;;;;;;;;;;;;;;;ACAkD;AACa;AACR;AACO;AACvB;AAOvC,IAAa,iBAAiB;IAc5B,2BACU,eAAgC,EAChC,oBAA0C,EAC1C,WAAuB,EACvB,MAAc;QAHd,oBAAe,GAAf,eAAe,CAAiB;QAChC,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,gBAAW,GAAX,WAAW,CAAY;QACvB,WAAM,GAAN,MAAM,CAAQ;QAZxB,wBAAwB;QACjB,YAAO,GAAG;YACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACnB;IAOG,CAAC;IAEL,oCAAQ,GAAR;IAEA,CAAC;IAED,4CAAgB,GAAhB;QAAA,iBAgCC;QA/BC,IAAM,IAAI,GAAG;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,EAAE;SACd;QAED,kBAAkB;QAClB,EAAE,EAAC,CAAC,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAC;YAC/C,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,4BAA4B,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACrF,MAAM,CAAC,KAAK,CAAC;QACf,CAAC;QAED,iBAAiB;QACjB,EAAE,EAAC,CAAC,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAC;YAClD,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,oCAAoC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7F,MAAM,CAAC,KAAK,CAAC;QACf,CAAC;QAED,gBAAgB;QAChB,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAI;YAChD,EAAE,EAAC,IAAI,CAAC,OAAO,CAAC,EAAC;gBACf,cAAc,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;gBACpD,KAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnC,CAAC;YAAC,IAAI,CAAC,CAAC;gBACN,KAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAI,CAAC,OAAO,CAAC,CAAC;gBAC7E,KAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;YACtC,CAAC;QACH,CAAC,CAAC,CAAC;IAEL,CAAC;IAEH,wBAAC;AAAD,CAAC;AA3DY,iBAAiB;IAL7B,gEAAS,CAAC;QACT,QAAQ,EAAE,cAAc;QACxB,mGAAwC;QACxC,kGAAuC;KACxC,CAAC;yDAgB2B,mFAAe,oBAAf,mFAAe,sDACV,4EAAoB,oBAApB,4EAAoB,sDAC9B,2EAAW,oBAAX,2EAAW,sDACf,+DAAM,oBAAN,+DAAM;GAlBb,iBAAiB,CA2D7B;AA3D6B;;;;;;;;;ACX9B;AACA;;;AAGA;AACA,6BAA8B,wBAAwB,oBAAoB,KAAK,qBAAqB,qBAAqB,KAAK,yBAAyB,yBAAyB,4BAA4B,KAAK,gCAAgC,yBAAyB,KAAK,sBAAsB,uCAAuC,sCAAsC,oCAAoC,wCAAwC,KAAK;;AAEnc;;;AAGA;AACA,2C;;;;;;;ACXA,myBAAmyB,aAAa,iGAAiG,+BAA+B,6GAA6G,+BAA+B,qDAAqD,iDAAiD,gC;;;;;;;;;;;;;;;;;;;;;;;ACAhnC;AACc;AACc;AAChB;AAO9D,IAAa,eAAe;IAW1B,yBAAoB,cAA6B,EAC7B,qBAA2C,EAC3C,oBAA0C;QAF1C,mBAAc,GAAd,cAAc,CAAe;QAC7B,0BAAqB,GAArB,qBAAqB,CAAsB;QAC3C,yBAAoB,GAApB,oBAAoB,CAAsB;QAT9D,wBAAwB;QACjB,YAAO,GAAG;YACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;YAC1B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI;SACnB;IAKa,CAAC;IAEf,kCAAQ,GAAR;IACA,CAAC;IAED,oCAAU,GAAV,UAAW,aAAa;QAAxB,iBAQC;QAPC,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC;QAE9B,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,UAAC,IAAI;YAC3D,KAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAED,6CAAmB,GAAnB,UAAoB,QAAQ;QAA5B,iBAkBC;QAjBC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,qBAAqB,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,UAAC,IAAI;gBAC5E,6CAA6C;gBAC7C,IAAI,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC5C,IAAI,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAChD,WAAW,CAAC,SAAS,GAAG,gBAAgB,CAAC;gBACzC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;gBAC1D,wBAAwB;gBACxB,KAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,SAAS,EAAE,6BAA6B,EAAE,KAAI,CAAC,OAAO,CAAC,CAAC;YAC5F,CAAC,CAAC,CAAC;QAEL,CAAC;QAAA,IAAI,CAAC,CAAC;YACL,sBAAsB;YACtB,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,wDAAwD,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACnH,CAAC;QACD,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAED,qCAAW,GAAX,UAAY,IAAI;QACd,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,IAAI,GAAI,GAAG,CAAC,CAAC,GAAC,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAChJ,CAAC;IAED,0CAAgB,GAAhB,UAAiB,GAAG;QAClB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9B,CAAC;IAGH,sBAAC;AAAD,CAAC;AA1DY,eAAe;IAL3B,gEAAS,CAAC;QACT,QAAQ,EAAE,YAAY;QACtB,+FAAsC;QACtC,8FAAqC;KACtC,CAAC;yDAYmC,iFAAc,oBAAd,iFAAc,sDACP,+FAAqB,oBAArB,+FAAqB,sDACrB,4EAAoB,oBAApB,4EAAoB;GAbnD,eAAe,CA0D3B;AA1D2B;;;;;;;;;ACV5B;AACA;;;AAGA;AACA,8BAA+B,yBAAyB,KAAK,eAAe,yBAAyB,KAAK;;AAE1G;;;AAGA;AACA,2C;;;;;;;ACXA,8M;;;;;;;;;;;;;;;;;;;;ACAkD;AACQ;AAO1D,IAAa,eAAe;IAE1B,yBAAoB,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAI,CAAC;IAEjD,kCAAQ,GAAR;IACA,CAAC;IAEH,sBAAC;AAAD,CAAC;AAPY,eAAe;IAL3B,gEAAS,CAAC;QACT,QAAQ,EAAE,YAAY;QACtB,+FAAsC;QACtC,8FAAqC;KACtC,CAAC;yDAGiC,2EAAW,oBAAX,2EAAW;GAFjC,eAAe,CAO3B;AAP2B;;;;;;;;;;;;;;;;;;;;;;;ACRe;AACW;AACC;AAGvD,IAAa,SAAS;IACpB,mBAAoB,WAAuB,EAAU,MAAa;QAA9C,gBAAW,GAAX,WAAW,CAAY;QAAU,WAAM,GAAN,MAAM,CAAO;IAElE,CAAC;IAED,+BAAW,GAAX;QACE,EAAE,EAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,EAAC;YAC9B,MAAM,CAAC,IAAI,CAAC;QACd,CAAC;QAAC,IAAI,CAAC,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACH,gBAAC;AAAD,CAAC;AAbY,SAAS;IADrB,iEAAU,EAAE;yDAEqB,2EAAW,oBAAX,2EAAW,sDAAiB,+DAAM,oBAAN,+DAAM;GADvD,SAAS,CAarB;AAbqB;;;;;;;;;;;;;;;;;;;;;;;;;;ACLqB;AACC;AACb;AACgB;AAG/C,IAAa,WAAW;IAItB,qBAAoB,IAAS;QAAT,SAAI,GAAJ,IAAI,CAAK;IAAI,CAAC;IAElC,kCAAY,GAAZ,UAAa,IAAI;QACf,IAAI,OAAO,GAAG,IAAI,sDAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,sCAAsC,EAAE,IAAI,EAAC,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC;aACnF,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAC5B,CAAC;IAED,sCAAgB,GAAhB,UAAiB,IAAI;QACnB,IAAI,OAAO,GAAG,IAAI,sDAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,0CAA0C,EAAE,IAAI,EAAC,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC;aACzF,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAC1B,CAAC;IAED,gCAAU,GAAV;QACE,IAAI,OAAO,GAAG,IAAI,sDAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,CAAC,MAAM,CAAC,cAAc,EAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,qCAAqC,EAAE,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC;aAC9E,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAC1B,CAAC;IAED,mCAAa,GAAb,UAAc,KAAK,EAAE,IAAI;QACvB,YAAY,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACxC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,+BAAS,GAAT;QACE,IAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,8BAAQ,GAAR;QACE,MAAM,CAAC,qEAAe,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC;IAED,4BAAM,GAAN;QACE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACpC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAEH,kBAAC;AAAD,CAAC;AApDY,WAAW;IADvB,iEAAU,EAAE;yDAKc,mDAAI,oBAAJ,mDAAI;GAJlB,WAAW,CAoDvB;AApDuB;;;;;;;;;;;;;;;;;;;;;;ACNmB;AACC;AAG5C,IAAa,qBAAqB;IAEhC,+BAAoB,IAAS;QAAT,SAAI,GAAJ,IAAI,CAAK;IAAI,CAAC;IAElC,iDAAiB,GAAjB;QACE,IAAI,SAAS,GAAG,EAAE,CAAC;QACnB,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;QAAA,IAAI,EAAC;YACJ,MAAM,CAAC,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,gDAAgB,GAAhB,UAAiB,QAAQ,EAAE,MAAM;QAC/B,IAAI,IAAI,GAAG;YACT,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,MAAM;SACjB;QACD,IAAI,OAAO,GAAG,IAAI,sDAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC,EAAE,IAAI,EAAE,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC;aACzF,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAC1B,CAAC;IAED,mDAAmB,GAAnB,UAAoB,QAAQ,EAAE,MAAM;QAClC,IAAI,IAAI,GAAG;YACT,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,MAAM;SACjB;QACD,IAAI,OAAO,GAAG,IAAI,sDAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,4CAA4C,EAAE,IAAI,EAAE,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC;aAC5F,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAC1B,CAAC;IAED,uDAAuB,GAAvB,UAAwB,MAAM;QAC5B,IAAI,IAAI,GAAG,EAAC,QAAQ,EAAE,MAAM,EAAC;QAC7B,IAAI,OAAO,GAAG,IAAI,sDAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gDAAgD,EAAE,IAAI,EAAE,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC;aAChG,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAC1B,CAAC;IAEH,4BAAC;AAAD,CAAC;AA5CY,qBAAqB;IADjC,iEAAU,EAAE;yDAGc,mDAAI,oBAAJ,mDAAI;GAFlB,qBAAqB,CA4CjC;AA5CiC;;;;;;;;;;;;;;;;;;;;;ACJS;AAG3C,IAAa,eAAe;IAE1B;IAAgB,CAAC;IAEjB,0CAAgB,GAAhB,UAAiB,IAAI;QACnB,EAAE,EAAC,IAAI,CAAC,IAAI,IAAI,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,SAAS,IAAI,IAAI,CAAC,QAAQ,IAAI,SAAS,IAAI,IAAI,CAAC,QAAQ,IAAI,SAAS,CAAC,EAAC;YAChH,MAAM,CAAC,KAAK,CAAC;QACf,CAAC;QAAC,IAAI,CAAC,CAAC;YACN,MAAM,CAAC,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,uCAAa,GAAb,UAAc,KAAK;QACjB,IAAM,KAAK,GAAG,wJAAwJ,CAAC;QACvK,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IACH,sBAAC;AAAD,CAAC;AAhBY,eAAe;IAD3B,iEAAU,EAAE;;GACA,eAAe,CAgB3B;AAhB2B;;;;;;;;;;;;;;;;;;;;;;;ACHe;AACN;AACN;AAG/B,IAAa,cAAc;IAMzB,wBAAmB,IAAS;QAAT,SAAI,GAAJ,IAAI,CAAK;QAJ5B,UAAK,GAAU,yCAAyC,CAAC;QACzD,YAAO,GAAU,mDAAmD,CAAC;QAInE,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC9C,CAAC;IAED,mCAAU,GAAV,UAAW,QAAQ;QACjB,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,aAAG,IAAI,UAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IACnD,CAAC;IAEH,qBAAC;AAAD,CAAC;AAfY,cAAc;IAD1B,iEAAU,EAAE;yDAOa,mDAAI,oBAAJ,mDAAI;GANjB,cAAc,CAe1B;AAf0B;;;;;;;;;;ACL3B;AAAA,mFAAmF;AACnF,8FAA8F;AAC9F,yEAAyE;AACzE,gFAAgF;AAEhF,mFALmF;AAK5E,IAAM,WAAW,GAAG;IACzB,UAAU,EAAE,KAAK;CAClB,CAAC;;;;;;;;;;;;;;ACP6C;AAC4B;AAE9B;AACY;AAEzD,EAAE,CAAC,CAAC,8EAAW,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3B,qEAAc,EAAE,CAAC;AACnB,CAAC;AAED,yGAAsB,EAAE,CAAC,eAAe,CAAC,kEAAS,CAAC;KAChD,KAAK,CAAC,aAAG,IAAI,cAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAhB,CAAgB,CAAC,CAAC","file":"main.bundle.js","sourcesContent":["function webpackEmptyAsyncContext(req) {\n\t// Here Promise.resolve().then() is used instead of new Promise() to prevent\n\t// uncatched exception popping up in devtools\n\treturn Promise.resolve().then(function() {\n\t\tthrow new Error(\"Cannot find module '\" + req + \"'.\");\n\t});\n}\nwebpackEmptyAsyncContext.keys = function() { return []; };\nwebpackEmptyAsyncContext.resolve = webpackEmptyAsyncContext;\nmodule.exports = webpackEmptyAsyncContext;\nwebpackEmptyAsyncContext.id = \"../../../../../src/$$_gendir lazy recursive\";\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/$$_gendir lazy\n// module id = ../../../../../src/$$_gendir lazy recursive\n// module chunks = main","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"#appbar {\\r\\n height: 63px;\\r\\n width: 100%;\\r\\n padding: 12px 0 0 16px;\\r\\n color: white;\\r\\n background-color: #333;\\r\\n}\\r\\n\\r\\n#appbar h1 {\\r\\n margin-top: 0 !important;\\r\\n}\\r\\n\\r\\n\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/app.component.css\n// module id = ../../../../../src/app/app.component.css\n// module chunks = main","module.exports = \"
\\n \\n
\\n \\n
\\n
\\n\\n\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/app.component.html\n// module id = ../../../../../src/app/app.component.html\n// module chunks = main","import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n styleUrls: ['./app.component.css']\n})\nexport class AppComponent {\n title = 'Weather App ';\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/app.component.ts","import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpModule } from '@angular/http';\nimport { FormsModule } from '@angular/forms';\nimport { RouterModule, Routes } from '@angular/router';\nimport { SimpleNotificationsModule } from 'angular2-notifications';\nimport { BrowserAnimationsModule } from '@angular/platform-browser/animations';\n\nimport { AppComponent } from './app.component';\nimport { SearchComponent } from './components/search/search.component';\nimport { DashboardComponent } from './components/dashboard/dashboard.component';\nimport { NavbarComponent } from './components/navbar/navbar.component';\nimport { LoginComponent } from './components/login/login.component';\nimport { RegisterComponent } from './components/register/register.component';\nimport { HomeComponent } from './components/home/home.component';\nimport { ProfileComponent } from './components/profile/profile.component';\n\nimport { WeatherService } from './services/weather.service';\nimport { SavedlocationsService } from './services/savedlocations.service';\nimport { ValidateService } from './services/validate.service';\nimport { AuthService } from './services/auth.service';\nimport { SubnavComponent } from './components/subnav/subnav.component';\nimport { AuthGuard } from './guards/auth.guard';\n\n\nconst appRoutes: Routes = [\n {\n path: '',\n redirectTo: '/home',\n pathMatch: 'full'\n },\n {\n path: 'home',\n component: HomeComponent\n },\n {\n path: 'register',\n component: RegisterComponent\n },\n {\n path: 'login',\n component: LoginComponent\n },\n {\n path: 'dashboard',\n component: DashboardComponent, \n canActivate:[AuthGuard]\n },\n {\n path: 'profile',\n component: ProfileComponent, \n canActivate:[AuthGuard]\n },\n {\n path: 'search',\n component: SearchComponent\n }\n];\n\n@NgModule({\n declarations: [\n AppComponent,\n SearchComponent,\n DashboardComponent,\n NavbarComponent,\n LoginComponent,\n RegisterComponent,\n HomeComponent,\n ProfileComponent,\n SubnavComponent\n ],\n imports: [\n BrowserModule,\n HttpModule,\n FormsModule,\n BrowserAnimationsModule,\n SimpleNotificationsModule.forRoot(),\n RouterModule.forRoot(appRoutes)\n ],\n providers: [WeatherService,SavedlocationsService,ValidateService,AuthService,AuthGuard],\n bootstrap: [AppComponent]\n})\n\nexport class AppModule { }\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/app.module.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"h3 {\\r\\n margin-top: 0px;\\r\\n}\\r\\n\\r\\n.edit-link a {\\r\\n font-size: 20px;\\r\\n margin-top: 12px;\\r\\n}\\r\\n\\r\\n.componenttitle {\\r\\n margin: 20px 0 0 0;\\r\\n}\\r\\n\\r\\n.nolocations {\\r\\n float: left;\\r\\n width: 100%;\\r\\n border: 1px solid #ddd;\\r\\n border-radius: 8px;\\r\\n margin-top: 25px;\\r\\n padding: 18px;\\r\\n font-size: 16px;\\r\\n color: #555;\\r\\n background-color: white;\\r\\n}\\r\\n\\r\\n.delete {\\r\\n cursor: pointer;\\r\\n}\\r\\n\\r\\n#saved-tile {\\r\\n width: 25%;\\r\\n float: left;\\r\\n /* border: 1px solid #ddd; */\\r\\n border-radius: 8px;\\r\\n margin: 20px 25px 25px 25px;\\r\\n padding: 18px;\\r\\n font-size: 16px;\\r\\n max-height: 312px;\\r\\n background-color: white;\\r\\n}\\r\\n \\r\\n#saved-tile div {\\r\\n margin-bottom: 15px;\\r\\n}\\r\\n \\r\\n#saved-tile #description {\\r\\n margin-bottom: 0px;\\r\\n}\\r\\n\\r\\n#saved-tile #description, #saved-tile #temperature, .componenttitle {\\r\\n color: #555;\\r\\n}\\r\\n\\r\\n#saved-tile .result.temp {\\r\\n font-size: 24px;\\r\\n}\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/dashboard/dashboard.component.css\n// module id = ../../../../../src/app/components/dashboard/dashboard.component.css\n// module chunks = main","module.exports = \"\\n\\n\\n\\n
\\n

My saved locations

\\n
\\n\\n\\n\\n
\\n\\n
\\n\\n
\\\"delete\\\"
\\n \\n

{{report.name}}

\\n \\n
Temperature: \\n {{convertTemp(report.main.temp)}}\\n \\n
\\n \\n
Weather conditions:\\n {{report.weather[0].description}}\\n
\\n \\n
\\n \\n
\\n\\n
\\n\\n
\\n

You have no saved locations. Add one now.

\\n
\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/dashboard/dashboard.component.html\n// module id = ../../../../../src/app/components/dashboard/dashboard.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { WeatherService } from '../../services/weather.service';\nimport { SavedlocationsService } from '../../services/savedlocations.service';\nimport { NotificationsService } from 'angular2-notifications';\nimport { Observable } from 'rxjs';\n\n@Component({\n selector: 'app-dashboard',\n templateUrl: './dashboard.component.html',\n styleUrls: ['./dashboard.component.css']\n})\nexport class DashboardComponent implements OnInit {\n\n locations: string[];\n reports: Report[];\n isEdit: boolean = false;\n editText: string = \"edit\";\n\n // notification defaults\n public options = {\n position: [\"top\", \"right\"],\n timeOut: 3000,\n pauseOnHover: true,\n lastOnBottom: true\n }\n\n constructor(private weatherService:WeatherService, \n private savedLocationsService:SavedlocationsService,\n private notificationsService: NotificationsService\n ) { }\n\n ngOnInit() {\n\n if (sessionStorage.userloggedin) {\n sessionStorage.removeItem('userloggedin');\n let ns = this.notificationsService;\n setTimeout(function() {\n ns.success(\"Success\", \"You are now logged in!\", this.options);\n }, 30);\n }\n\n this.getSavedLocations();\n }\n\n getSavedLocations() {\n this.locations = this.savedLocationsService.getSavedLocations();\n this.getWeather();\n }\n\n getWeather() {\n // make one service call for each element in the 'locations' array\n Observable.forkJoin(this.locations\n .map((element) => this.weatherService.getWeather(element)))\n .subscribe(\n (data) => this.reports = data\n );\n }\n\n toggleEdit() {\n this.isEdit = !this.isEdit;\n this.editText = (this.isEdit == true) ? \"exit edit\" : \"edit\";\n return false;\n }\n\n deleteTile(location) {\n if (localStorage.user) {\n let user = JSON.parse(localStorage.user);\n this.savedLocationsService.deleteSavedLocation(location, user.id).subscribe((data) => {\n // set the new locations into session storage\n let updatedlocations = data.model.locations;\n let updateduser = JSON.parse(localStorage.user);\n updateduser.locations = updatedlocations;\n localStorage.setItem(\"user\", JSON.stringify(updateduser));\n this.getSavedLocations();\n });\n }else {\n console.log('Error removing tile');\n }\n return false;\n }\n\n clearAll() {\n if (localStorage.user) {\n let user = JSON.parse(localStorage.user);\n this.savedLocationsService.deleteAllSavedLocations(user.id).subscribe(() => {\n this.reports = [];\n // clear the locations from session storage\n let updateduser = JSON.parse(localStorage.user);\n updateduser.locations = [];\n localStorage.setItem(\"user\", JSON.stringify(updateduser));\n });\n }else {\n console.log('Error clearing locations');\n }\n return false;\n }\n\n convertTemp(temp) {\n return Math.ceil((9/5*(temp - 273))+32) + String.fromCharCode(176) + \"F\" + \" / \" + Math.ceil(temp - 273.15) + String.fromCharCode(176) + \"C\";\n }\n\n removeWhitespace(str) {\n return str.replace(\" \", \"\");\n }\n\n}\n\ninterface Report{\n name:string,\n temp:number,\n conditions:string\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/dashboard/dashboard.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \".jumbotron {\\r\\n opacity: .8;\\r\\n border-radius: 8px;\\r\\n}\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/home/home.component.css\n// module id = ../../../../../src/app/components/home/home.component.css\n// module chunks = main","module.exports = \"\\n\\n
\\n

Weather App

\\n

Accurate weather reporting from around the globe

\\n
\\n Register Login\\n
\\n \\n
\\n \\n
\\n
\\n

Real-time weather reports

\\n

Get the latest weather reports from anywhere around the globe.

\\n
\\n
\\n

Customizable dashboard

\\n

View the weather from your favorite locations in your own personalized dashboard.

\\n
\\n
\\n

Secure login

\\n

Our app features secure authentication and password encryption so your data is always safe.

\\n
\\n
\\n\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/home/home.component.html\n// module id = ../../../../../src/app/components/home/home.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { NotificationsService } from 'angular2-notifications';\nimport { AuthService } from '../../services/auth.service';\n\n@Component({\n selector: 'app-home',\n templateUrl: './home.component.html',\n styleUrls: ['./home.component.css']\n})\nexport class HomeComponent implements OnInit {\n\n // notification defaults\n public options = {\n position: [\"top\", \"right\"],\n timeOut: 3000,\n pauseOnHover: true,\n lastOnBottom: true\n }\n\n constructor(private notificationsService: NotificationsService,\n private authService: AuthService\n ) { }\n\n ngOnInit() {\n\n // set logout notification when necessary\n if (sessionStorage.userlogout) {\n sessionStorage.removeItem('userlogout');\n let ns = this.notificationsService;\n setTimeout(function() {\n ns.success(\"Success\", \"You are now logged out.\", this.options);\n }, 30);\n }\n\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/home/home.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"form {\\r\\n margin-top: 25px;\\r\\n}\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/login/login.component.css\n// module id = ../../../../../src/app/components/login/login.component.css\n// module chunks = main","module.exports = \"\\n\\n

Login

\\n

Don't have an account? Register now

\\n
\\n
\\n \\n \\n
\\n
\\n \\n \\n
\\n \\n
\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/login/login.component.html\n// module id = ../../../../../src/app/components/login/login.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { NotificationsService } from 'angular2-notifications';\nimport { AuthService } from '../../services/auth.service';\nimport {Router} from '@angular/router';\n\n@Component({\n selector: 'app-login',\n templateUrl: './login.component.html',\n styleUrls: ['./login.component.css']\n})\nexport class LoginComponent implements OnInit {\n\n username: String;\n password: String;\n\n // notification defaults\n public options = {\n position: [\"top\", \"right\"],\n timeOut: 4000,\n pauseOnHover: true,\n lastOnBottom: true\n }\n\n constructor(private notificationsService: NotificationsService,\n private authService: AuthService,\n private router: Router\n ) { }\n \n ngOnInit() {\n \n if (sessionStorage.newregistereduser) {\n sessionStorage.removeItem('newregistereduser');\n let ns = this.notificationsService;\n setTimeout(function() {\n ns.success(\"Success\", \"You are now registered!\", this.options);\n }, 30);\n }\n\n }\n\n onLoginSubmit() {\n const user = {\n username: this.username,\n password: this.password\n }\n\n this.authService.authenticateUser(user).subscribe(data => {\n if(data.success){\n this.authService.storeUserData(data.token, data.user);\n sessionStorage.setItem('userloggedin', 'true');\n this.router.navigate(['/dashboard']);\n }else {\n this.notificationsService.error(\"Error\", data.msg, this.options);\n }\n });\n\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/login/login.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"/* bootstrap overrides */\\r\\nnav.navbar {\\r\\n background-color: #333;\\r\\n border-radius: 0;\\r\\n}\\r\\n\\r\\nnav.navbar .greeting {\\r\\n color: white;\\r\\n}\\r\\n\\r\\nnav .navbar-brand {\\r\\n font-size: 36px;\\r\\n}\\r\\n\\r\\nnav ul.navbar-nav {\\r\\n float: right;\\r\\n}\\r\\n\\r\\nnav ul.navbar-nav li {\\r\\n margin-right: 20px;\\r\\n}\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/navbar/navbar.component.css\n// module id = ../../../../../src/app/components/navbar/navbar.component.css\n// module chunks = main","module.exports = \"\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/navbar/navbar.component.html\n// module id = ../../../../../src/app/components/navbar/navbar.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { NotificationsService } from 'angular2-notifications';\nimport { AuthService } from '../../services/auth.service';\nimport {Router} from '@angular/router';\n\n@Component({\n selector: 'app-navbar',\n templateUrl: './navbar.component.html',\n styleUrls: ['./navbar.component.css']\n})\nexport class NavbarComponent implements OnInit {\n\n username: String;\n\n // notification defaults\n public options = {\n position: [\"top\", \"right\"],\n timeOut: 3000,\n pauseOnHover: true,\n lastOnBottom: true\n }\n\n constructor(private notificationsService: NotificationsService,\n private authService: AuthService,\n private router: Router\n ) { }\n\n ngOnInit() {\n if (this.authService.loggedIn()) {\n let user = JSON.parse(localStorage.getItem('user'));\n this.username = user.name;\n }\n }\n\n onLogoutClick() {\n this.authService.logout();\n sessionStorage.setItem(\"userlogout\", \"true\");\n this.router.navigate(['/home']);\n return false;\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/navbar/navbar.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/profile/profile.component.css\n// module id = ../../../../../src/app/components/profile/profile.component.css\n// module chunks = main","module.exports = \"
\\n

{{user.name}}

\\n
    \\n
  • Name: {{user.name}}
  • \\n
  • Username: {{user.username}}
  • \\n
  • Email: {{user.email}}
  • \\n
\\n
\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/profile/profile.component.html\n// module id = ../../../../../src/app/components/profile/profile.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { AuthService } from '../../services/auth.service';\nimport {Router} from '@angular/router';\n\n@Component({\n selector: 'app-profile',\n templateUrl: './profile.component.html',\n styleUrls: ['./profile.component.css']\n})\nexport class ProfileComponent implements OnInit {\n\n user: Object;\n\n constructor(private authService: AuthService,\n private router: Router\n ) { }\n\n ngOnInit() {\n this.authService.getProfile().subscribe(profile => {\n this.user = profile.user;\n }),\n err => {\n console.log(err);\n return false;\n }\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/profile/profile.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/register/register.component.css\n// module id = ../../../../../src/app/components/register/register.component.css\n// module chunks = main","module.exports = \"\\n\\n

Register

\\n
\\n
\\n \\n \\n
\\n
\\n \\n \\n
\\n
\\n \\n \\n
\\n
\\n \\n \\n
\\n \\n
\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/register/register.component.html\n// module id = ../../../../../src/app/components/register/register.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport {ValidateService} from '../../services/validate.service'\nimport {AuthService} from '../../services/auth.service'\nimport { NotificationsService } from 'angular2-notifications';\nimport {Router} from '@angular/router';\n\n@Component({\n selector: 'app-register',\n templateUrl: './register.component.html',\n styleUrls: ['./register.component.css']\n})\nexport class RegisterComponent implements OnInit {\n name: String;\n username: String;\n email: String;\n password: String;\n\n // notification defaults\n public options = {\n position: [\"top\", \"right\"],\n timeOut: 5000,\n pauseOnHover: true,\n lastOnBottom: true\n }\n\n constructor(\n private validateService: ValidateService,\n private notificationsService: NotificationsService,\n private authService:AuthService,\n private router: Router\n ) { }\n\n ngOnInit() {\n \n }\n \n onRegisterSubmit(){\n const user = {\n name: this.name,\n email: this.email,\n username: this.username,\n password: this.password,\n locations: []\n }\n\n // Required Fields\n if(!this.validateService.validateRegister(user)){\n this.notificationsService.error(\"Error\", \"Please fill out all fields\", this.options);\n return false;\n }\n\n // Validate Email\n if(!this.validateService.validateEmail(user.email)){\n this.notificationsService.error(\"Error\", \"Please enter a valid email address\", this.options);\n return false;\n }\n\n // Register user\n this.authService.registerUser(user).subscribe(data => {\n if(data.success){\n sessionStorage.setItem('newregistereduser', 'true');\n this.router.navigate(['/login']);\n } else {\n this.notificationsService.error(\"Error\", \"Registration error\", this.options);\n this.router.navigate(['/register']);\n }\n });\n\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/register/register.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"h3 {\\r\\n margin-top: 0px;\\r\\n color: #555;\\r\\n}\\r\\n\\r\\n.addtosaved {\\r\\n float: right;\\r\\n}\\r\\n\\r\\nform#searchform {\\r\\n margin-top: 21px;\\r\\n margin-bottom: 20px;\\r\\n}\\r\\n\\r\\nform#searchform button {\\r\\n margin-top: 20px;\\r\\n}\\r\\n\\r\\n.input-jumbo {\\r\\n padding: .5rem 1rem !important;\\r\\n font-size: 1.25rem !important;\\r\\n line-height: 1.5 !important;\\r\\n border-radius: .3rem !important;\\r\\n}\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/search/search.component.css\n// module id = ../../../../../src/app/components/search/search.component.css\n// module chunks = main","module.exports = \"\\n\\n\\n\\n
\\n \\n
\\n

Enter search location

\\n \\n
\\n
\\n \\n\\n
\\n\\n
\\n\\n \\n \\n
City: \\n {{report.name}}\\n
\\n \\n
Temperature: \\n {{convertTemp(report.main.temp)}}\\n \\n
\\n \\n
Weather conditions:\\n {{report.weather[0].description}}\\n
\\n\\n
\\n \\n
\\n \\n\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/search/search.component.html\n// module id = ../../../../../src/app/components/search/search.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { WeatherService } from '../../services/weather.service';\nimport { SavedlocationsService } from '../../services/savedlocations.service';\nimport { NotificationsService } from 'angular2-notifications';\n\n@Component({\n selector: 'app-search',\n templateUrl: './search.component.html',\n styleUrls: ['./search.component.css']\n})\nexport class SearchComponent implements OnInit {\n\n location:string;\n report:Report;\n // notification defaults\n public options = {\n position: [\"top\", \"right\"],\n timeOut: 2000,\n lastOnBottom: true\n }\n\n constructor(private weatherService:WeatherService,\n private savedLocationsService:SavedlocationsService,\n private notificationsService: NotificationsService\n ) { }\n\n ngOnInit() {\n }\n\n getWeather(locationinput) {\n this.location = locationinput;\n \n this.weatherService.getWeather(this.location).subscribe((data) => {\n this.report = data;\n });\n\n return false;\n }\n\n addToSavedLocations(location) {\n if (localStorage.user) {\n let user = JSON.parse(localStorage.user);\n this.savedLocationsService.addSavedLocation(location, user.id).subscribe((data) => {\n // set the new locations into session storage\n let updatedlocations = data.model.locations;\n let updateduser = JSON.parse(localStorage.user);\n updateduser.locations = updatedlocations;\n localStorage.setItem(\"user\", JSON.stringify(updateduser));\n // alert user of success\n this.notificationsService.success(\"Success\", \"Location saved to dashboard\", this.options);\n });\n \n }else {\n // alert user of error\n this.notificationsService.error(\"Error\", \"Please log in or register in order to save a location.\", this.options);\n }\n return false;\n }\n\n convertTemp(temp) {\n return Math.ceil((9/5*(temp - 273))+32) + String.fromCharCode(176) + \"F\" + \" / \" + Math.ceil(temp - 273.15) + String.fromCharCode(176) + \"C\";\n }\n\n removeWhitespace(str) {\n return str.replace(\" \", \"\");\n }\n\n\n}\n\ninterface Report{\n name:string,\n temp:number,\n conditions:string\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/search/search.component.ts","exports = module.exports = require(\"../../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"nav {\\r\\n margin-top: 15px;\\r\\n}\\r\\n\\r\\nnav a {\\r\\n margin-left: 7px;\\r\\n}\", \"\"]);\n\n// exports\n\n\n/*** EXPORTS FROM exports-loader ***/\nmodule.exports = module.exports.toString();\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/subnav/subnav.component.css\n// module id = ../../../../../src/app/components/subnav/subnav.component.css\n// module chunks = main","module.exports = \"\\n\"\n\n\n//////////////////\n// WEBPACK FOOTER\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/subnav/subnav.component.html\n// module id = ../../../../../src/app/components/subnav/subnav.component.html\n// module chunks = main","import { Component, OnInit } from '@angular/core';\nimport { AuthService } from '../../services/auth.service';\n\n@Component({\n selector: 'app-subnav',\n templateUrl: './subnav.component.html',\n styleUrls: ['./subnav.component.css']\n})\nexport class SubnavComponent implements OnInit {\n\n constructor(private authService: AuthService) { }\n\n ngOnInit() {\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/components/subnav/subnav.component.ts","import { Injectable } from '@angular/core';\r\nimport { Router, CanActivate } from '@angular/router';\r\nimport { AuthService } from '../services/auth.service';\r\n\r\n@Injectable()\r\nexport class AuthGuard implements CanActivate{\r\n constructor(private authService:AuthService, private router:Router){\r\n\r\n }\r\n\r\n canActivate(){\r\n if(this.authService.loggedIn()){\r\n return true;\r\n } else {\r\n this.router.navigate(['/login']);\r\n return false;\r\n }\r\n }\r\n}\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/guards/auth.guard.ts","import { Injectable } from '@angular/core';\nimport {Http, Headers} from '@angular/http';\nimport 'rxjs/add/operator/map';\nimport { tokenNotExpired } from 'angular2-jwt';\n\n@Injectable()\nexport class AuthService {\n authToken: any;\n user: any;\n\n constructor(private http:Http) { }\n\n registerUser(user){\n let headers = new Headers();\n headers.append('Content-Type','application/json');\n return this.http.post('http://localhost:3000/users/register', user,{headers: headers})\n .map(res => res.json());\n }\n\n authenticateUser(user){\n let headers = new Headers();\n headers.append('Content-Type','application/json');\n return this.http.post('http://localhost:3000/users/authenticate', user,{headers: headers})\n .map(res => res.json());\n }\n\n getProfile(){\n let headers = new Headers();\n this.loadToken();\n headers.append('Authorization', this.authToken);\n headers.append('Content-Type','application/json');\n return this.http.get('http://localhost:3000/users/profile', {headers: headers})\n .map(res => res.json());\n }\n\n storeUserData(token, user) {\n localStorage.setItem('id_token', token);\n localStorage.setItem('user', JSON.stringify(user));\n this.authToken = token;\n this.user = user;\n }\n\n loadToken() {\n const token = localStorage.getItem('id_token');\n this.authToken = token;\n }\n\n loggedIn() {\n return tokenNotExpired('id_token');\n }\n\n logout() {\n this.authToken = null;\n this.user = null;\n localStorage.removeItem('id_token');\n localStorage.removeItem('user');\n }\n\n}\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/services/auth.service.ts","import { Injectable } from '@angular/core';\nimport {Http, Headers} from '@angular/http';\n\n@Injectable()\nexport class SavedlocationsService {\n\n constructor(private http:Http) { }\n\n getSavedLocations() {\n let locations = [];\n if (localStorage.user) {\n let user = JSON.parse(localStorage.user);\n return user.locations;\n }else{\n return [];\n }\n }\n\n addSavedLocation(location, userid) {\n let user = {\n \"location\": location,\n \"userid\": userid\n }\n let headers = new Headers();\n headers.append('Content-Type','application/json');\n return this.http.post('http://localhost:3000/users/addlocation', user, {headers: headers})\n .map(res => res.json());\n }\n\n deleteSavedLocation(location, userid) {\n let user = {\n \"location\": location,\n \"userid\": userid\n }\n let headers = new Headers();\n headers.append('Content-Type','application/json');\n return this.http.post('http://localhost:3000/users/removelocation', user, {headers: headers})\n .map(res => res.json());\n }\n\n deleteAllSavedLocations(userid) {\n let user = {\"userid\": userid}\n let headers = new Headers();\n headers.append('Content-Type','application/json');\n return this.http.post('http://localhost:3000/users/removealllocations', user, {headers: headers})\n .map(res => res.json());\n }\n\n}\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/services/savedlocations.service.ts","import { Injectable } from '@angular/core';\n\n@Injectable()\nexport class ValidateService {\n\n constructor() { }\n\n validateRegister(user){\n if(user.name == undefined || user.email == undefined || user.username == undefined || user.password == undefined){\n return false;\n } else {\n return true;\n }\n }\n\n validateEmail(email){\n const regex = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n return regex.test(email);\n }\n}\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/services/validate.service.ts","import { Injectable } from '@angular/core';\nimport { Http } from '@angular/http';\nimport 'rxjs/add/operator/map';\n\n@Injectable()\nexport class WeatherService {\n\n appid:string = \"&appid=fd61eab401147e78b825c2f71cdea941\";\n resturl:string = \"http://api.openweathermap.org/data/2.5/weather?q=\";\n iconclass:string;\n\n constructor(public http:Http) { \n console.log('weather service connected...');\n }\n\n getWeather(location) {\n let url = this.resturl + location + this.appid;\n return this.http.get(url).map(res => res.json());\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/app/services/weather.service.ts","// The file contents for the current environment will overwrite these during build.\n// The build system defaults to the dev environment which uses `environment.ts`, but if you do\n// `ng build --env=prod` then `environment.prod.ts` will be used instead.\n// The list of which env maps to which file can be found in `.angular-cli.json`.\n\nexport const environment = {\n production: false\n};\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/environments/environment.ts","import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app/app.module';\nimport { environment } from './environments/environment';\n\nif (environment.production) {\n enableProdMode();\n}\n\nplatformBrowserDynamic().bootstrapModule(AppModule)\n .catch(err => console.log(err));\n\n\n\n// WEBPACK FOOTER //\n// c:/Projects/weatherapp/weatherapp-ng/src/main.ts"],"sourceRoot":"webpack:///"} --------------------------------------------------------------------------------