├── .gitignore
├── README.md
├── jquery
├── index.html
├── weather.css
└── weather.js
└── react
├── weather-react.001-creacion
├── index.html
├── package.json
├── weather.css
└── weather.js
├── weather-react.002-webpack-js
├── bundle.js
├── index.html
├── package.json
├── weather.css
├── weather.js
└── webpack.config.js
├── weather-react.003-webpack-css
├── bundle.js
├── index.html
├── package.json
├── weather.css
├── weather.js
└── webpack.config.js
├── weather-react.004-react
├── bundle.js
├── index.html
├── package.json
├── weather.css
├── weather.js
└── webpack.config.js
├── weather-react.005-react-modularization
├── bundle.js
├── index.html
├── package.json
├── weather.css
├── weather.js
└── webpack.config.js
├── weather-react.006-react-events
├── bundle.js
├── index.html
├── package.json
├── weather.css
├── weather.js
└── webpack.config.js
└── weather-react.007-fetch
├── bundle.js
├── index.html
├── package.json
├── weather.css
├── weather.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | weather-react
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Taller de React, ES6 y webpack
2 |
3 | **NOTA:** Podéis encontrar las slides de la charla [aquí](https://speakerdeck.com/borillo/iniciacion-a-react-y-es6-con-webpack) y el vídeo que introduce los principales conceptos [aquí](https://www.youtube.com/watch?v=_oARNJqq1yE).
4 |
5 | **NOTA:** El ejemplo ha sido modificado para sólo reponder a búsquedas por "Castellón" o "Valencia", ya que el API de weather de OpenStreetMap es ahora cerrada :/
6 |
7 | ## Puesta en marcha del entorno
8 |
9 | Como requisito antes de comenzar, es necesario tener instalado [NodeJS](https://nodejs.org/en/). Para ello, recomendamos el uso de [NVM](https://github.com/creationix/nvm) (Node Version Manager), con el cual podemos instalar y utilizar varias versiones de [NodeJS](https://nodejs.org/en/) en nuestro entorno.
10 |
11 | En definitiva, si ya tenéis NVM instalado, sólo tenéis que ejecutar:
12 |
13 | $ nvm install v8.4.0
14 |
15 | Con lo que al ejecutar los siguientes comandos, deberíais ver sin problemas las versiones correctas de [NodeJS](https://nodejs.org/en/) y [NPM](https://www.npmjs.com/):
16 |
17 | $ node -v
18 | v8.4.0
19 |
20 | $ npm -v
21 | 5.5.2
22 |
23 | ## Creación del proyecto "weather"
24 |
25 | Nuestro objetivo en este taller va a ser migrar a React una aplicación desarrollada en jQuery que permite consultar el tiempo indicando una localidad o obteniendola de nuestra geolocalización.
26 |
27 | El código original que vamos a migrar lo podéis descargar del directorio `jquery` de este mismo repositorio. Como la idea es partir de la aplicación jQuery e ir haciendo cambios poco a poco, descargad ya el ejemplo para tenerlo preparado.
28 |
29 | Listos para comenzar!! Vamos pues a crear un directorio para el proyecto y le daremos el nombre de weather-react:
30 |
31 | $ mkdir weather-react
32 | $ cd weather-react
33 |
34 | Dentro del directorio del proyecto, inicializamos NPM para poder gestionar sus dependencias:
35 |
36 | $ npm init -y
37 |
38 | Como vamos a ir modificando el proyecto original jQuery para ir haciendolo más "React", copiaremos todos los ficheros que habíamos descargado del proyecto original, dentro de este nuevo directorio (esto incluye los ficheros `index.html`, `weather.css` y `weather.js`).
39 |
40 | ## Integración con webpack
41 |
42 | Antes de migrar a React el proyecto, vamos primero a poner a punto nuestro workflow de trabajo con webpack. Para ello necesitamos instalarlo:
43 |
44 | $ npm install --global webpack
45 |
46 | Si todo ha ido bien, deberíais poder ejecutar:
47 |
48 | $ webpack -v
49 | 3.5.6
50 |
51 | ### Empaquetado de ficheros JavaScript
52 |
53 | Para comenzar a integrar nuestros ficheros JavaScript en la build, es necesario crear el fichero de configuración de webpack `webpack.config.js`. Partiremos de la configuración más simple posible:
54 |
55 | ```javascript
56 | module.exports = {
57 | entry: './weather.js',
58 | output: {
59 | filename: 'bundle.js',
60 | path: __dirname
61 | }
62 | }
63 | ```
64 |
65 | Si ahora ejecutamos webpack, se generará el fichero de salida `bundle.js` con el resultado de empaquetar todos los recursos:
66 |
67 | $ webpack
68 | Hash: 298c1b69b8649145efa7
69 | Version: webpack 1.12.4
70 | Time: 71ms
71 | Asset Size Chunks Chunk Names
72 | bundle.js 6.47 kB 0 [emitted] main
73 | [0] ./weather.js 4.93 kB {0} [built]
74 |
75 | Ya sólo nos queda sustituir la siguiente linea en el `index.html`:
76 |
77 |
78 |
79 | Por la del bundle generado por webpack:
80 |
81 |
82 |
83 | Si no hemos metido la pata, todo debería seguir funcionando de la misma forma :)
84 |
85 | Recuerda que si quieres generar la versión de producción del bundle, sólo debes ejecutar webpack con el siguiente flag:
86 |
87 | webpack -p
88 | Hash: 645a62865caf048b5458
89 | Version: webpack 3.5.6
90 | Time: 111ms
91 | Asset Size Chunks Chunk Names
92 | bundle.js 3.83 kB 0 [emitted] main
93 | [0] ./weather.js 6.28 kB {0} [built]
94 |
95 | ### Empaquetado de ficheros CSS
96 |
97 | Webpack permite empaquetar y gestionar múltiples recursos al margen de los de tipo JavaScript, ofreciendo soporte también para CSS, SASS o mucho muchos más.
98 |
99 | Como en el proyecto ya tenemos nuestros estilos en un fichero CSS, vamos a incluir `weather.css` en la build.
100 |
101 | Para ello, debemos indicar a webpack que procese estos nuevos recursos modificando su fichero de configuración:
102 |
103 | ```javascript
104 | module.exports = {
105 | entry: './weather.js',
106 | output: {
107 | filename: 'bundle.js',
108 | path: __dirname
109 | },
110 | module: {
111 | loaders: [
112 | {
113 | test: /\.css$/,
114 | loader: 'style-loader!css-loader'
115 | }
116 | ]
117 | }
118 | }
119 | ```
120 |
121 | Esta nueva definición require de que los loaders `style` y `css` sean instalados previamente mediante NPM:
122 |
123 | npm install --save-dev style-loader css-loader
124 |
125 | Sólo nos queda pues modificar el fichero `weather.js`y hacer en su primera línea, un `require` del `weather.css`:
126 |
127 | ```javascript
128 | var styles = require('./weather.css');
129 |
130 | var g, GLoc = {
131 | settings: {
132 | ...
133 | ```
134 |
135 | Para finalizar, ya podemos eliminar la linea del `index.html` donde se incluye el fichero de estilos al estar ya empaquetado en el `bundle.js`:
136 |
137 |
138 |
139 | De nuevo, todo debería seguir funcionado.
140 |
141 | # Migrando a React
142 |
143 | ## React con webpack
144 |
145 | Para poder comenzar a utilizar React en este proyecto, necesitamos que webpack sea capaz de procesar JSX (lenguaje con el que definiremos declarativamente las vistas en React). Esto se consigue, como en todos los casos previos, mediante la inclusión de un loader (`babel-loader` en este caso). Es importante tener en cuenta que gracias a este loader, vamos a tener acceso también a todas las novedades de ES6 (aka ES2015).
146 |
147 | Instalemos pues las dependencias de React en primer lugar:
148 |
149 | npm install --save react react-dom
150 |
151 | Y posteriormente, los loaders que webpack necesitará para procesar el código React:
152 |
153 | npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
154 |
155 | En este caso, también es necesaria una pequeña modificación del `webpack.config.js` para que estos nuevos loaders sean cargados:
156 |
157 | ```javascript
158 | module.exports = {
159 | entry: './weather.js',
160 | output: {
161 | filename: 'bundle.js',
162 | path: __dirname
163 | },
164 | module: {
165 | loaders: [
166 | {
167 | test: /\.css$/,
168 | loader: 'style-loader!css-loader'
169 | },
170 | {
171 | test: /\.jsx?$/,
172 | exclude: /node_modules/,
173 | loader: 'babel-loader',
174 | query: {
175 | presets:['es2015','react']
176 | }
177 | }
178 | ]
179 | },
180 | resolve: {
181 | extensions: [".js", ".jsx", ".css"]
182 | }
183 | }
184 | ```
185 |
186 | ## Definición de widgets
187 |
188 | La idea es ir migrando el código HTML/JS basado en jQuery a React de forma progresiva. Para ello vamos a definir un `div` que va a contener todo el código renderizado por React y que será incluido en el `index.html`:
189 |
190 | ```html
191 | ...
192 |
193 |
194 |
195 |
196 | ...
197 | ```
198 |
199 | En el `weather.js`, crearé mi primera clase React que va a representar la aplicación del tiempo y comprobaré que webpack no se queja al procesar estos nuevos elementos:
200 |
201 | ```javascript
202 | import React from 'react';
203 | import ReactDOM from 'react-dom';
204 | import styles from './weather.css';
205 |
206 | class WeatherApp extends React.Component {
207 | render() {
208 | return (
209 |
210 | );
211 | }
212 | }
213 |
214 | ReactDOM.render(
, document.getElementById("react-output"));
215 | ```
216 |
217 | A partir de ahora, podemos dejar a webpack en modo `watch` para que vaya procesando los cambios y los podamos ir viendo al momento en el navegador:
218 |
219 | webpack -w
220 |
221 | Para iniciar la transformación, el primer paso será mover todo el marcado HTML que tenemos en `index.html` dentro del bloque etiquetado con el atributo `class="page-wrap"` al método render de Nuestra clase `WatherApp`:
222 |
223 | ```javascript
224 | class WeatherApp extends React.Component {
225 | render() {
226 | return (
227 |
228 |
241 |
242 |
243 |
Blank Canvas Weather
244 | An Obligatory Weather App
245 |
246 |
247 |
248 |
249 |
250 |
254 |
255 |
256 | Humidity: %
257 |
258 | Wind:
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 | );
267 | }
268 | }
269 | ```
270 |
271 | Como podemos comprobar, todo sigue funcionando despues de este cambio.
272 |
273 | ## Modularización
274 |
275 | Aunque ya tenemos todo el marcado en una vista React, vamos a intentar partir el ejemplo en componentes más pequeños para ir aislando su funcionalidad.
276 |
277 | Este es el resultado de la modularización:
278 |
279 | ```javascript
280 | class SearchBar extends React.Component {
281 | render() {
282 | return (
283 |
298 | );
299 | }
300 | }
301 |
302 | class Wellcome extends React.Component {
303 | render() {
304 | return (
305 |
306 |
Blank Canvas Weather
307 | An Obligatory Weather App
308 |
309 | );
310 | }
311 | }
312 |
313 | class Info extends React.Component {
314 | render() {
315 | return (
316 |
317 |
318 |
319 |
323 |
324 |
325 | Humidity: %
326 |
327 | Wind:
328 |
329 |
330 |
331 |
332 |
333 |
334 | );
335 | }
336 | }
337 |
338 | class WeatherApp extends React.Component {
339 | render() {
340 | return (
341 |
342 |
343 |
344 |
345 |
346 | );
347 | }
348 | }
349 | ```
350 |
351 | Por facilidad a la hora de seguir el ejemplo, vamos a dejar todo el código en el mismo fichero, pero la práctica habitual es separa los componentes en distintos ficheros e importarlos cuando sea necesario mediante `import`. Gracias a webpack, todo acabará siendo incluido en nuestro `bundle.js` de salida.
352 |
353 | ### Eventos
354 |
355 | Llegados a este punto, sigue siendo jQuery el que gestiona los eventos que se producen en la aplicación. Vamos pues a migrar esta parte a eventos gestionado por React.
356 |
357 | Eventos que se producen en la aplicación:
358 |
359 | - `keypress` cuando escribimos el nombre de la ciudad a buscar (si es un enter, entonces se ejecuta la búsqueda).
360 | - `click` en el botón de buscar.
361 | - `click` en el botón de geolocalización.
362 |
363 | Vamos a comenzar por el primero y vamos a hacernos cargo de la gestión del evento de `keypress`. Pare ello deberemos borrar el código jQuery que se ocupa de actuar sobre este evento y modifcar la clase `SearchBar` para tratar este evento, recuperar los datos del tiempo y enviar el resultado a `WeatherApp`:
364 |
365 | ```javascript
366 | class SearchBar extends React.Component {
367 | selectLocation(event) {
368 | if (event.keyCode !== 13) return;
369 | this.showWeather();
370 | }
371 |
372 | showWeather() {
373 | let location = this.refs["search-location-input"].value;
374 | this.props.onData(retrieveWeather(location));
375 | }
376 |
377 | render() {
378 | return (
379 |
396 | );
397 | }
398 | }
399 | ```
400 |
401 | Quedando `WeatherApp` de la siguiente forma:
402 |
403 | ```javascript
404 | class WeatherApp extends React.Component {
405 | showWeatherData(data) {
406 | console.log(data);
407 | }
408 |
409 | render() {
410 | return (
411 |
412 |
413 |
414 |
415 |
416 | );
417 | }
418 | }
419 | ```
420 |
421 | Ahora que tenemos el evento controlado, vamos a ocultar el mensaje de bienvenida y mostrar el bloque de info del tiempo. Para ello haremos uso de la gestión del estado de los componentes en React y modificaremos `WeatherApp`:
422 |
423 | ```javascript
424 | class WeatherApp extends React.Component {
425 | constructor(props) {
426 | super(props);
427 | this.state = { ready : false };
428 | }
429 |
430 | showWeatherData(data) {
431 | this.setState({ ready : true });
432 | }
433 |
434 | render() {
435 | return (
436 |
437 |
438 | { (this.state.ready) ? : }
439 |
440 | );
441 | }
442 | }
443 | ```
444 |
445 | Como podemos ver, el panel cambia, pero la info del tiempo no se muestra. Vayamos pues a completar esta parte!!
446 |
447 | Para que la información correcta se muestre, añadiremos un nuevo método al componente `Info` para que se pueda realizar la carga y lo invocaremos desde `WeatherApp`. Podemos ver como otros métodos de cálculo son necesarios en `Info`, pero no son más que una copia del
448 | código jQuery que ya teníamos:
449 |
450 | ```javascript
451 | class Info extends React.Component {
452 | constructor(props) {
453 | super(props);
454 |
455 | this.state = {
456 | loaded: false,
457 | location: '',
458 | humidity: '',
459 | description: '',
460 | temperature: '',
461 | windSpeed: 0,
462 | windDegree: '',
463 | windDirection: ''
464 | };
465 | }
466 |
467 | loadWeatherData(data) {
468 | this.setState({
469 | loaded: true,
470 | location: data.name + ', ' + data.sys.country,
471 | humidity: data.main.humidity,
472 | description: data.weather[0].description,
473 | windDirection: this.getWindDirection(data.wind.deg),
474 | temperature: Math.round(data.main.temp - 273.15),
475 | windSpeed: Math.round(data.wind.speed * 3.6)
476 | });
477 | }
478 |
479 | getWindDirection(degree) {
480 | if (degree > 337.5 || degree <= 22.5) {
481 | return 'N';
482 | } else if (22.5 < degree <= 67.5) {
483 | return 'NE';
484 | } else if (67.5 < degree <= 112.5) {
485 | return 'E';
486 | } else if (112.5 < degree <= 157.5) {
487 | return 'SE';
488 | } else if (157.5 < degree <= 202.5) {
489 | return 'S';
490 | } else if (202.5 < degree <= 247.5) {
491 | return 'SW';
492 | } else if (247.5 < degree <= 292.5) {
493 | return 'W';
494 | } else if (292.5 < degree <= 337.5) {
495 | return 'NW';
496 | }
497 | }
498 |
499 | render() {
500 | return (
501 |
502 |
{this.state.location}
503 |
504 |
505 |
506 |
{this.state.temperature}
507 |
{this.state.description}
508 |
509 |
510 |
511 | Humidity: {this.state.humidity} %
512 |
513 | Wind: {this.state.windDirection} {this.state.windSpeed} {this.state.speedUnit}
514 |
515 |
516 |
517 |
518 |
519 | );
520 | }
521 | }
522 |
523 | class WeatherApp extends React.Component {
524 | constructor(props) {
525 | super(props);
526 | this.state = { ready : false };
527 | }
528 |
529 | showWeatherData(data) {
530 | this.setState({ ready : true });
531 | this.refs["info"].loadWeatherData(data);
532 | }
533 |
534 | render() {
535 | return (
536 |
537 |
538 | { (this.state.ready) ? : }
539 |
540 | );
541 | }
542 | }
543 | ```
544 |
545 | Ya sólo nos queda que pueda funcionar el botón de buscar y el de geoposicionar:
546 |
547 | ```javascript
548 | class SearchBar extends React.Component {
549 | selectLocation(event) {
550 | if (event.keyCode !== 13) return;
551 | this.showWeather();
552 | }
553 |
554 | selectCurrentLocation() {
555 | navigator.geolocation.getCurrentPosition((position) => {
556 | this.showWeatherByLatitude(position.coords.longitude, position.coords.latitude);
557 | });
558 | }
559 |
560 | showWeather() {
561 | let location = this.refs["search-location-input"].value;
562 | this.props.onData(retrieveWeather(location));
563 | }
564 |
565 | showWeatherByLatitude(longitude, latitude) {
566 | this.props.onData(retrieveWeather("Castellón"));
567 | }
568 |
569 | render() {
570 | return (
571 |
591 | );
592 | }
593 | }
594 | ```
595 |
596 | ### Wrap-up y algunas mejoras
597 |
598 | Con todo el marcado migrado, los componentes React extraidos y los eventos definidos, ya sólo nos queda borrar todo el código jQuery de `weather.js` y comprobar que la aplicación sigue siendo funcional.
599 |
600 | **NOTA:** Si has llegado hasta aquí y algo no te funciona, revisa tu código comparándolo con el que he publicado en el directorio `react` de este mismo repo.
601 |
602 | A partir de aquí, si queremos prescindir totalmente de jQuery, podemos usar `fetch` para realizar las peticiones AJAX al servicio del tiempo que se usa en la aplicación. Como [fetch aún no está implementado en todos los navegadores](http://caniuse.com/#search=fetch), podemos usar una librería externa que actua como polyfill. En nuestro caso vamos a utilizar `isomorphic-fetch` porque podemos usarla tanto en cliente como en servidor con NodeJS y soporta promesas ES6.
603 |
604 | Para ello, instalamos como siempre las dependencias necesarias:
605 |
606 | npm install --save isomorphic-fetch es6-promise
607 |
608 | Y sustituiremos la parte de `$.getJSON` que obtiene los datos en JSON mediante jQuery, por la parte de `fetch`:
609 |
610 | ```javascript
611 | import es6promise from 'es6-promise';
612 | import fetch from 'isomorphic-fetch';
613 | es6promise.polyfill();
614 |
615 | const API_TOKEN = "0596fe0573fa9daa94c2912e5e383ed3";
616 |
617 | class SearchBar extends React.Component {
618 |
619 | ...
620 |
621 | showWeather() {
622 | let location = this.refs["search-location-input"].value;
623 | let url = `http://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${API_TOKEN}`;
624 |
625 | fetch(url)
626 | .then(function (response) {
627 | return response.json();
628 | })
629 | .then((data) => {
630 | this.props.onData(data);
631 | });
632 | }
633 |
634 | showWeatherByLatitude(longitude, latitude) {
635 | let url = `http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_TOKEN}`;
636 |
637 | fetch(url)
638 | .then(function (response) {
639 | return response.json();
640 | })
641 | .then((data) => {
642 | this.props.onData(data);
643 | });
644 | }
645 |
646 | render() {
647 | ...
648 | }
649 | }
650 | ```
651 |
652 | Si hemos integrado `fetch` en la aplicación, ya podemos quitar tranquilamente la dependencia de jQuery del `index.html` y seguir funcionando con normalidad.
653 |
--------------------------------------------------------------------------------
/jquery/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Weather App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
30 |
31 |
32 |
Blank Canvas Weather
33 | An Obligatory Weather App
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 | Humidity: %
46 |
47 | Wind:
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/jquery/weather.css:
--------------------------------------------------------------------------------
1 | *, *:before, *:after {
2 | -moz-box-sizing: border-box;
3 | -webkit-box-sizing: border-box;
4 | box-sizing: border-box;
5 | }
6 |
7 | .hide {
8 | display: none;
9 | }
10 |
11 | .geo-error-message, .attribution-links {
12 | max-width: 400px;
13 | -webkit-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
14 | -moz-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
15 | box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
16 | background: #fff;
17 | padding: 25px;
18 | margin: 6rem auto;
19 | position: relative;
20 | z-index: 10;
21 | }
22 |
23 | .close-error, .close-attribution {
24 | position: absolute;
25 | top: 0;
26 | right: 0;
27 | border-radius: 0;
28 | border: 1px solid #ccc;
29 | background-color: #fff;
30 | }
31 |
32 | .search-bar {
33 | background: #fff;
34 | height: 129px;
35 | color: #555;
36 | padding-top: 5px;
37 | }
38 |
39 | .search-location-text {
40 | font-size: 16px;
41 | font-weight: 300;
42 | }
43 |
44 | .search-text {
45 | text-align: center;
46 | margin-bottom: 10px;
47 | }
48 |
49 | .search-button {
50 | border: none;
51 | width: 50%;
52 | background-color: #ddd;
53 | color: #555;
54 | height: 50px;
55 | font-size: 16px;
56 | display: inline-block;
57 | }
58 |
59 | .search-location-button {
60 | border-right: 1px solid #fff;
61 | }
62 |
63 | .geo-button {
64 | border-left: 1px solid #fff;
65 | }
66 |
67 | .search-location-input {
68 | color: #000;
69 | padding: 7px;
70 | }
71 |
72 | @media all and (min-width: 401px) {
73 | .search-bar {
74 | height: 107px;
75 | }
76 |
77 | }
78 |
79 | @media all and (min-width: 505px) {
80 |
81 | .search-bar {
82 | padding-top: 0;
83 | height: 50px;
84 | }
85 |
86 | .search-text {
87 | text-align: left;
88 | float: left;
89 | margin-top: 5px;
90 | margin-bottom: 0;
91 | padding-left: 10px;
92 | }
93 |
94 | .search-location-button-group {
95 | float: right;
96 | }
97 |
98 | .search-button {
99 | width: 50px;
100 | }
101 |
102 | }
103 |
104 | @media all and (min-width: 753px) {
105 |
106 | .search-bar {
107 | height: 64px;
108 | padding-top: 0;
109 | }
110 |
111 | .search-location-text {
112 | font-size: 24px;
113 | position: relative;
114 | top: 5px;
115 | }
116 |
117 | .search-text {
118 | text-align: left;
119 | display: inline;
120 | margin: 0;
121 | }
122 |
123 | .search-location-button-group {
124 | display: inline;
125 | float: right;
126 | }
127 |
128 | .search-button {
129 | width: 64px;
130 | float: left;
131 | height: 64px;
132 | }
133 | }
134 |
135 | body {
136 | margin: 0;
137 | padding: 0;
138 | font-family: 'Open Sans', sans-serif;
139 | }
140 |
141 | #rain-canvas, #weather-canvas, #cloud-canvas, #time-canvas, #lightning-canvas {
142 | position: absolute;
143 | z-index: -1;
144 | }
145 |
146 | /* Canvas Background Classes */
147 |
148 | .thunderstorm {
149 | background-color: #34495E;
150 | }
151 |
152 | .drizzle, .rain {
153 | background-color: #2080B6;
154 | }
155 |
156 | .snow {
157 | background-color: #959B9F;
158 | }
159 |
160 | .atmosphere {
161 | background-color: #5E668F;
162 | }
163 |
164 | .clouds {
165 | background-color: #3498DB;
166 | }
167 |
168 | .clearsky, .default-weather {
169 | background-color: #6FC2D4;
170 | }
171 |
172 | .extreme-weather {
173 | background-color: #C0392B;
174 | }
175 |
176 | .thunderstorm.nighttime {
177 | background-color: #1B2631;
178 | }
179 |
180 | .drizzle.nighttime, .rain.nighttime {
181 | background-color: #0E3A53;
182 | }
183 |
184 | .snow.nighttime {
185 | background-color: #333537;
186 | }
187 |
188 | .atmosphere.nighttime {
189 | background-color: #32364A;
190 | }
191 |
192 | .clouds.nighttime {
193 | background-color: #164362;
194 | }
195 |
196 | .clearsky.nighttime {
197 | background-color: #222;
198 | }
199 |
200 | .extreme-weather.nighttime {
201 | background-color: #491611;
202 | }
203 |
204 | h1, h2, h3, h4, h5, h6 {
205 | font-weight: 300;
206 | }
207 |
208 | .weather, .front-page-description {
209 | text-align: center;
210 | color: #fff;
211 | font-size: 20px;
212 | font-weight: 300;
213 | }
214 |
215 | .nighttime .weather {
216 | text-shadow: 1px 1px 4px #000;
217 | font-weight: 400;
218 | }
219 |
220 | .nighttime .checked {
221 | color: rgba(255, 255, 255, .5);
222 | }
223 |
224 | .weather-info {
225 | list-style-type: none;
226 | padding: 0;
227 | text-align: center;
228 | font-size: 16px;
229 | }
230 |
231 | .weather-item {
232 | text-align: center;
233 | display: inline-block;
234 | background-repeat: no-repeat;
235 | background-size: contain;
236 | }
237 |
238 | .middle {
239 | margin: 10px 0;
240 | }
241 |
242 | @media all and (min-width: 500px) {
243 | .middle {
244 | position: absolute;
245 | top: 50%;
246 | right: 0;
247 | left: 0;
248 | -webkit-transform: translateY(-50%);
249 | -moz-transform: translateY(-50%);
250 | -ms-transform: translateY(-50%);
251 | -o-transform: translateY(-50%);
252 | transform: translateY(-50%);
253 | }
254 | }
255 |
256 | .temp-change {
257 | clear: both;
258 | }
259 |
260 | .weather-container {
261 | position: relative;
262 | }
263 |
264 | .weather-description {
265 | display: inline-block;
266 | text-transform: capitalize;
267 | }
268 |
269 | .weather-description, .weather-box {
270 | padding: 10px 0;
271 | }
272 |
273 | .temp-change-button {
274 | background: none;
275 | border: none;
276 | padding: 0 10px;
277 | font-weight: 600;
278 | font-size: 14px;
279 | }
280 |
281 | .celsius {
282 | border-right: 2px solid #fff;
283 | }
284 |
285 | .checked {
286 | color: rgba(0, 0, 0, .5);
287 | }
288 |
289 | .temperature {
290 | font-size: 8rem;
291 | line-height: 1;
292 | margin-left: 35px;
293 | }
294 |
295 | @media all and (min-width: 320px) {
296 | .temperature {
297 | font-size: 12rem;
298 | }
299 | }
300 |
301 | .temperature:after {
302 | content: '\B0 C';
303 | font-size: 40px;
304 | line-height: 2.2;
305 | vertical-align: top;
306 | margin-left: -6px;
307 | }
308 |
309 | .fahrenheit-degree:after {
310 | content: '\B0 F';
311 | }
312 |
313 | .celsius-degree:after {
314 | content: '\B0 C';
315 | }
316 |
317 | a {
318 | color: #333;
319 | }
320 |
321 | /* Sticky Footer */
322 |
323 | * {
324 | margin: 0;
325 | }
326 |
327 | html, body {
328 | height: 100%;
329 | }
330 |
331 | .page-wrap {
332 | min-height: 100%;
333 | /* equal to footer height */
334 | margin-bottom: -100px;
335 | }
336 |
337 | .page-wrap:after {
338 | content: "";
339 | display: block;
340 | }
341 |
342 | .site-footer, .page-wrap:after {
343 | height: 100px;
344 | }
345 |
346 | .site-footer {
347 | background: #fff;
348 | color: #555;
349 | font-size: 14px;
350 | text-align: center;
351 | }
352 |
353 | .site-footer-section {
354 | padding: 5px 5px 0 5px;
355 | font-size: 14px;
356 | }
357 |
358 | .noun-project {
359 | border: none;
360 | background: none;
361 | padding: 0;
362 | text-decoration: underline;
363 | }
--------------------------------------------------------------------------------
/jquery/weather.js:
--------------------------------------------------------------------------------
1 | var g, GLoc = {
2 | settings: {
3 | geoButton: $('#geo-button'),
4 | startPos: '',
5 | searchQuery: ''
6 | },
7 |
8 | init: function () {
9 | g = this.settings;
10 | this.bindUIActions();
11 | },
12 |
13 | bindUIActions: function () {
14 | g.geoButton.on('click', function () {
15 | GLoc.getGeoLocation();
16 | });
17 | },
18 |
19 | getGeoLocation: function (numToGet) {
20 | navigator.geolocation.getCurrentPosition(GLoc.geoSuccess);
21 | },
22 |
23 | geoSuccess: function (position) {
24 | WeatherInfo.setWeatherData(retrieveWeather('Castellón'));
25 | }
26 | };
27 |
28 | var w, WeatherInfo = {
29 | settings: {
30 | tempIcon: $('#temp-icon'),
31 | weather: $('#weather'),
32 | weatherInfo: $('#weather-info'),
33 | location: $('#location'),
34 | weatherDescription: $('#weather-description'),
35 | temperature: $('#temperature'),
36 | tempNumber: '',
37 | fahrenheit: $('#fahrenheit'),
38 | celsius: $('#celsius'),
39 | wind: $('#wind'),
40 | searchLocationInput: $('#search-location-input'),
41 | searchLocationButton: $('#search-location-button'),
42 | celsiusButton: $('#celsius'),
43 | fahrenheitButton: $('#fahrenheit'),
44 | humidity: $('#humidity'),
45 | speedUnit: $('#speed-unit'),
46 | windSpeed: '',
47 | windDirection: $('#wind-direction'),
48 | windDegree: '',
49 | dayOrNight: '',
50 | closeAttribution: $('#close-attribution'),
51 | openAttribution: $('#noun-project'),
52 | attributionModal: $('#attribution-links')
53 | },
54 |
55 | init: function () {
56 | w = this.settings;
57 | this.bindUIActions();
58 | w.searchLocationInput.keypress(function (e) {
59 | if (e.keyCode === 13) {
60 | w.searchLocationButton.click();
61 | }
62 | });
63 | },
64 |
65 | bindUIActions: function () {
66 | w.searchLocationButton.on('click', function () {
67 | WeatherInfo.getWeatherData();
68 | });
69 |
70 | w.closeAttribution.on('click', function () {
71 | WeatherInfo.closeAttributionModal();
72 | });
73 |
74 | w.openAttribution.on('click', function () {
75 | WeatherInfo.openAttributionModal();
76 | });
77 | },
78 |
79 | closeAttributionModal: function () {
80 | w.attributionModal.addClass('hide');
81 | },
82 |
83 |
84 | openAttributionModal: function () {
85 | w.attributionModal.removeClass('hide');
86 | },
87 |
88 | getWeatherData: function (searchQuery) {
89 | if (w.searchLocationInput.val() !== '') {
90 | WeatherInfo.setWeatherData(retrieveWeather(w.searchLocationInput.val()));
91 | }
92 | },
93 |
94 | setWeatherData: function (data) {
95 | if (!data.sys) {
96 | alert('Country error!!');
97 | return;
98 | }
99 |
100 | $('#front-page-description').addClass('hide');
101 | w.weather.removeClass('hide');
102 | w.location.text(data.name + ', ' + data.sys.country);
103 | w.humidity.text(data.main.humidity);
104 | w.weatherDescription.text(data.weather[0].description);
105 | w.tempNumber = data.main.temp;
106 | w.windSpeed = data.wind.speed;
107 | w.windDegree = data.wind.deg;
108 | WeatherInfo.getWeatherDirection();
109 | WeatherInfo.changeTempUnit();
110 | },
111 |
112 | getWeatherDirection: function () {
113 | if (w.windDegree > 337.5 || w.windDegree <= 22.5) {
114 | w.windDirection.text('N');
115 | } else if (22.5 < w.windDegree <= 67.5) {
116 | w.windDirection.text('NE');
117 | } else if (67.5 < w.windDegree <= 112.5) {
118 | w.windDirection.text('E');
119 | } else if (112.5 < w.windDegree <= 157.5) {
120 | w.windDirection.text('SE');
121 | } else if (157.5 < w.windDegree <= 202.5) {
122 | w.windDirection.text('S');
123 | } else if (202.5 < w.windDegree <= 247.5) {
124 | w.windDirection.text('SW');
125 | } else if (247.5 < w.windDegree <= 292.5) {
126 | w.windDirection.text('W');
127 | } else if (292.5 < w.windDegree <= 337.5) {
128 | w.windDirection.text('NW');
129 | }
130 |
131 | },
132 |
133 | isValid: function (weatherDataPiece) {
134 | if (typeof weatherDataPiece !== undefined) {
135 | return weatherDataPiece + ' ';
136 | } else {
137 | return '';
138 | }
139 | },
140 |
141 | changeTempUnit: function (unit) {
142 | var newTemp = w.tempNumber - 273.15;
143 |
144 | w.celsius.addClass('checked');
145 | w.fahrenheit.removeClass('checked');
146 | w.temperature.addClass('celsius-degree');
147 | w.temperature.removeClass('fahrenheit-degree');
148 | w.temperature.html(Math.round(newTemp));
149 | WeatherInfo.changeSpeedUnit('km');
150 | },
151 |
152 | changeSpeedUnit: function () {
153 | w.wind.text('' + Math.round(w.windSpeed * 3.6));
154 | w.speedUnit.text('km/h');
155 | }
156 | };
157 |
158 | function retrieveWeather(city) {
159 | var data;
160 |
161 | if (city === 'Valencia') {
162 | data = {
163 | main: {
164 | humidity: 15,
165 | temp: 281,
166 | },
167 | wind: {
168 | speed: 21,
169 | deg: 18
170 | },
171 | sys: {
172 | country: 'Spain'
173 | },
174 | name: 'Valencia',
175 | weather: [
176 | {
177 | description: 'Sunny'
178 | }
179 | ]
180 | };
181 | }
182 | else {
183 | data = {
184 | main: {
185 | humidity: 15,
186 | temp: 288,
187 | },
188 | wind: {
189 | speed: 11,
190 | deg: 15
191 | },
192 | sys: {
193 | country: 'Spain'
194 | },
195 | name: 'Castellón',
196 | weather: [
197 | {
198 | description: 'Sunny'
199 | }
200 | ]
201 | };
202 | }
203 |
204 | return data;
205 | }
206 |
207 | $(function () {
208 | GLoc.init();
209 | WeatherInfo.init();
210 | });
211 |
--------------------------------------------------------------------------------
/react/weather-react.001-creacion/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Weather App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
30 |
31 |
32 |
Blank Canvas Weather
33 | An Obligatory Weather App
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 | Humidity: %
46 |
47 | Wind:
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/react/weather-react.001-creacion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "weather-react.001",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "weather.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC"
12 | }
13 |
--------------------------------------------------------------------------------
/react/weather-react.001-creacion/weather.css:
--------------------------------------------------------------------------------
1 | *, *:before, *:after {
2 | -moz-box-sizing: border-box;
3 | -webkit-box-sizing: border-box;
4 | box-sizing: border-box;
5 | }
6 |
7 | .hide {
8 | display: none;
9 | }
10 |
11 | .geo-error-message, .attribution-links {
12 | max-width: 400px;
13 | -webkit-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
14 | -moz-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
15 | box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
16 | background: #fff;
17 | padding: 25px;
18 | margin: 6rem auto;
19 | position: relative;
20 | z-index: 10;
21 | }
22 |
23 | .close-error, .close-attribution {
24 | position: absolute;
25 | top: 0;
26 | right: 0;
27 | border-radius: 0;
28 | border: 1px solid #ccc;
29 | background-color: #fff;
30 | }
31 |
32 | .search-bar {
33 | background: #fff;
34 | height: 129px;
35 | color: #555;
36 | padding-top: 5px;
37 | }
38 |
39 | .search-location-text {
40 | font-size: 16px;
41 | font-weight: 300;
42 | }
43 |
44 | .search-text {
45 | text-align: center;
46 | margin-bottom: 10px;
47 | }
48 |
49 | .search-button {
50 | border: none;
51 | width: 50%;
52 | background-color: #ddd;
53 | color: #555;
54 | height: 50px;
55 | font-size: 16px;
56 | display: inline-block;
57 | }
58 |
59 | .search-location-button {
60 | border-right: 1px solid #fff;
61 | }
62 |
63 | .geo-button {
64 | border-left: 1px solid #fff;
65 | }
66 |
67 | .search-location-input {
68 | color: #000;
69 | padding: 7px;
70 | }
71 |
72 | @media all and (min-width: 401px) {
73 | .search-bar {
74 | height: 107px;
75 | }
76 |
77 | }
78 |
79 | @media all and (min-width: 505px) {
80 |
81 | .search-bar {
82 | padding-top: 0;
83 | height: 50px;
84 | }
85 |
86 | .search-text {
87 | text-align: left;
88 | float: left;
89 | margin-top: 5px;
90 | margin-bottom: 0;
91 | padding-left: 10px;
92 | }
93 |
94 | .search-location-button-group {
95 | float: right;
96 | }
97 |
98 | .search-button {
99 | width: 50px;
100 | }
101 |
102 | }
103 |
104 | @media all and (min-width: 753px) {
105 |
106 | .search-bar {
107 | height: 64px;
108 | padding-top: 0;
109 | }
110 |
111 | .search-location-text {
112 | font-size: 24px;
113 | position: relative;
114 | top: 5px;
115 | }
116 |
117 | .search-text {
118 | text-align: left;
119 | display: inline;
120 | margin: 0;
121 | }
122 |
123 | .search-location-button-group {
124 | display: inline;
125 | float: right;
126 | }
127 |
128 | .search-button {
129 | width: 64px;
130 | float: left;
131 | height: 64px;
132 | }
133 | }
134 |
135 | body {
136 | margin: 0;
137 | padding: 0;
138 | font-family: 'Open Sans', sans-serif;
139 | }
140 |
141 | #rain-canvas, #weather-canvas, #cloud-canvas, #time-canvas, #lightning-canvas {
142 | position: absolute;
143 | z-index: -1;
144 | }
145 |
146 | /* Canvas Background Classes */
147 |
148 | .thunderstorm {
149 | background-color: #34495E;
150 | }
151 |
152 | .drizzle, .rain {
153 | background-color: #2080B6;
154 | }
155 |
156 | .snow {
157 | background-color: #959B9F;
158 | }
159 |
160 | .atmosphere {
161 | background-color: #5E668F;
162 | }
163 |
164 | .clouds {
165 | background-color: #3498DB;
166 | }
167 |
168 | .clearsky, .default-weather {
169 | background-color: #6FC2D4;
170 | }
171 |
172 | .extreme-weather {
173 | background-color: #C0392B;
174 | }
175 |
176 | .thunderstorm.nighttime {
177 | background-color: #1B2631;
178 | }
179 |
180 | .drizzle.nighttime, .rain.nighttime {
181 | background-color: #0E3A53;
182 | }
183 |
184 | .snow.nighttime {
185 | background-color: #333537;
186 | }
187 |
188 | .atmosphere.nighttime {
189 | background-color: #32364A;
190 | }
191 |
192 | .clouds.nighttime {
193 | background-color: #164362;
194 | }
195 |
196 | .clearsky.nighttime {
197 | background-color: #222;
198 | }
199 |
200 | .extreme-weather.nighttime {
201 | background-color: #491611;
202 | }
203 |
204 | h1, h2, h3, h4, h5, h6 {
205 | font-weight: 300;
206 | }
207 |
208 | .weather, .front-page-description {
209 | text-align: center;
210 | color: #fff;
211 | font-size: 20px;
212 | font-weight: 300;
213 | }
214 |
215 | .nighttime .weather {
216 | text-shadow: 1px 1px 4px #000;
217 | font-weight: 400;
218 | }
219 |
220 | .nighttime .checked {
221 | color: rgba(255, 255, 255, .5);
222 | }
223 |
224 | .weather-info {
225 | list-style-type: none;
226 | padding: 0;
227 | text-align: center;
228 | font-size: 16px;
229 | }
230 |
231 | .weather-item {
232 | text-align: center;
233 | display: inline-block;
234 | background-repeat: no-repeat;
235 | background-size: contain;
236 | }
237 |
238 | .middle {
239 | margin: 10px 0;
240 | }
241 |
242 | @media all and (min-width: 500px) {
243 | .middle {
244 | position: absolute;
245 | top: 50%;
246 | right: 0;
247 | left: 0;
248 | -webkit-transform: translateY(-50%);
249 | -moz-transform: translateY(-50%);
250 | -ms-transform: translateY(-50%);
251 | -o-transform: translateY(-50%);
252 | transform: translateY(-50%);
253 | }
254 | }
255 |
256 | .temp-change {
257 | clear: both;
258 | }
259 |
260 | .weather-container {
261 | position: relative;
262 | }
263 |
264 | .weather-description {
265 | display: inline-block;
266 | text-transform: capitalize;
267 | }
268 |
269 | .weather-description, .weather-box {
270 | padding: 10px 0;
271 | }
272 |
273 | .temp-change-button {
274 | background: none;
275 | border: none;
276 | padding: 0 10px;
277 | font-weight: 600;
278 | font-size: 14px;
279 | }
280 |
281 | .celsius {
282 | border-right: 2px solid #fff;
283 | }
284 |
285 | .checked {
286 | color: rgba(0, 0, 0, .5);
287 | }
288 |
289 | .temperature {
290 | font-size: 8rem;
291 | line-height: 1;
292 | margin-left: 35px;
293 | }
294 |
295 | @media all and (min-width: 320px) {
296 | .temperature {
297 | font-size: 12rem;
298 | }
299 | }
300 |
301 | .temperature:after {
302 | content: '\B0 C';
303 | font-size: 40px;
304 | line-height: 2.2;
305 | vertical-align: top;
306 | margin-left: -6px;
307 | }
308 |
309 | .fahrenheit-degree:after {
310 | content: '\B0 F';
311 | }
312 |
313 | .celsius-degree:after {
314 | content: '\B0 C';
315 | }
316 |
317 | a {
318 | color: #333;
319 | }
320 |
321 | /* Sticky Footer */
322 |
323 | * {
324 | margin: 0;
325 | }
326 |
327 | html, body {
328 | height: 100%;
329 | }
330 |
331 | .page-wrap {
332 | min-height: 100%;
333 | /* equal to footer height */
334 | margin-bottom: -100px;
335 | }
336 |
337 | .page-wrap:after {
338 | content: "";
339 | display: block;
340 | }
341 |
342 | .site-footer, .page-wrap:after {
343 | height: 100px;
344 | }
345 |
346 | .site-footer {
347 | background: #fff;
348 | color: #555;
349 | font-size: 14px;
350 | text-align: center;
351 | }
352 |
353 | .site-footer-section {
354 | padding: 5px 5px 0 5px;
355 | font-size: 14px;
356 | }
357 |
358 | .noun-project {
359 | border: none;
360 | background: none;
361 | padding: 0;
362 | text-decoration: underline;
363 | }
--------------------------------------------------------------------------------
/react/weather-react.001-creacion/weather.js:
--------------------------------------------------------------------------------
1 | var g, GLoc = {
2 | settings: {
3 | geoButton: $('#geo-button'),
4 | startPos: '',
5 | searchQuery: ''
6 | },
7 |
8 | init: function () {
9 | g = this.settings;
10 | this.bindUIActions();
11 | },
12 |
13 | bindUIActions: function () {
14 | g.geoButton.on('click', function () {
15 | GLoc.getGeoLocation();
16 | });
17 | },
18 |
19 | getGeoLocation: function (numToGet) {
20 | navigator.geolocation.getCurrentPosition(GLoc.geoSuccess);
21 | },
22 |
23 | geoSuccess: function (position) {
24 | // Do magic with the location
25 | g.startPos = position;
26 | g.searchQuery = 'http://api.openweathermap.org/data/2.5/weather?lat=' + g.startPos.coords.latitude + '&lon=' + g.startPos.coords.longitude + '&appid=0596fe0573fa9daa94c2912e5e383ed3' + '';
27 |
28 | $.getJSON(g.searchQuery, function (data) {
29 | WeatherInfo.setWeatherData(data);
30 | });
31 | }
32 | };
33 |
34 | var w, WeatherInfo = {
35 | settings: {
36 | tempIcon: $('#temp-icon'),
37 | weather: $('#weather'),
38 | weatherInfo: $('#weather-info'),
39 | location: $('#location'),
40 | weatherDescription: $('#weather-description'),
41 | temperature: $('#temperature'),
42 | tempNumber: '',
43 | fahrenheit: $('#fahrenheit'),
44 | celsius: $('#celsius'),
45 | wind: $('#wind'),
46 | searchLocationInput: $('#search-location-input'),
47 | searchLocationButton: $('#search-location-button'),
48 | celsiusButton: $('#celsius'),
49 | fahrenheitButton: $('#fahrenheit'),
50 | humidity: $('#humidity'),
51 | speedUnit: $('#speed-unit'),
52 | windSpeed: '',
53 | windDirection: $('#wind-direction'),
54 | windDegree: '',
55 | dayOrNight: '',
56 | closeAttribution: $('#close-attribution'),
57 | openAttribution: $('#noun-project'),
58 | attributionModal: $('#attribution-links')
59 | },
60 |
61 | init: function () {
62 | w = this.settings;
63 | this.bindUIActions();
64 | w.searchLocationInput.keypress(function (e) {
65 | if (e.keyCode === 13) {
66 | w.searchLocationButton.click();
67 | }
68 | });
69 | },
70 |
71 | bindUIActions: function () {
72 | w.searchLocationButton.on('click', function () {
73 | WeatherInfo.getWeatherData();
74 | });
75 |
76 | w.closeAttribution.on('click', function () {
77 | WeatherInfo.closeAttributionModal();
78 | });
79 |
80 | w.openAttribution.on('click', function () {
81 | WeatherInfo.openAttributionModal();
82 | });
83 | },
84 |
85 | closeAttributionModal: function () {
86 | w.attributionModal.addClass('hide');
87 | },
88 |
89 |
90 | openAttributionModal: function () {
91 | w.attributionModal.removeClass('hide');
92 | },
93 |
94 | getWeatherData: function (searchQuery) {
95 | if (w.searchLocationInput.val() !== '') {
96 | w.searchQuery = 'http://api.openweathermap.org/data/2.5/weather?q=' + w.searchLocationInput.val() + '&appid=0596fe0573fa9daa94c2912e5e383ed3' + '';
97 | $.getJSON(w.searchQuery, function (data) {
98 | WeatherInfo.setWeatherData(data);
99 | });
100 | }
101 | },
102 |
103 | setWeatherData: function (data) {
104 | if (!data.sys) {
105 | alert('Country error!!');
106 | return;
107 | }
108 |
109 | $('#front-page-description').addClass('hide');
110 | w.weather.removeClass('hide');
111 | w.location.text(data.name + ', ' + data.sys.country);
112 | w.humidity.text(data.main.humidity);
113 | w.weatherDescription.text(data.weather[0].description);
114 | w.tempNumber = data.main.temp;
115 | w.windSpeed = data.wind.speed;
116 | w.windDegree = data.wind.deg;
117 | WeatherInfo.getWeatherDirection();
118 | WeatherInfo.changeTempUnit();
119 | },
120 |
121 | getWeatherDirection: function () {
122 | if (w.windDegree > 337.5 || w.windDegree <= 22.5) {
123 | w.windDirection.text('N');
124 | } else if (22.5 < w.windDegree <= 67.5) {
125 | w.windDirection.text('NE');
126 | } else if (67.5 < w.windDegree <= 112.5) {
127 | w.windDirection.text('E');
128 | } else if (112.5 < w.windDegree <= 157.5) {
129 | w.windDirection.text('SE');
130 | } else if (157.5 < w.windDegree <= 202.5) {
131 | w.windDirection.text('S');
132 | } else if (202.5 < w.windDegree <= 247.5) {
133 | w.windDirection.text('SW');
134 | } else if (247.5 < w.windDegree <= 292.5) {
135 | w.windDirection.text('W');
136 | } else if (292.5 < w.windDegree <= 337.5) {
137 | w.windDirection.text('NW');
138 | }
139 |
140 | },
141 |
142 | isValid: function (weatherDataPiece) {
143 | if (typeof weatherDataPiece !== undefined) {
144 | return weatherDataPiece + ' ';
145 | } else {
146 | return '';
147 | }
148 | },
149 |
150 | changeTempUnit: function (unit) {
151 | var newTemp = w.tempNumber - 273.15;
152 |
153 | w.celsius.addClass('checked');
154 | w.fahrenheit.removeClass('checked');
155 | w.temperature.addClass('celsius-degree');
156 | w.temperature.removeClass('fahrenheit-degree');
157 | w.temperature.html(Math.round(newTemp));
158 | WeatherInfo.changeSpeedUnit('km');
159 | },
160 |
161 | changeSpeedUnit: function () {
162 | w.wind.text('' + Math.round(w.windSpeed * 3.6));
163 | w.speedUnit.text('km/h');
164 | }
165 | };
166 |
167 | $(function () {
168 | GLoc.init();
169 | WeatherInfo.init();
170 | });
171 |
--------------------------------------------------------------------------------
/react/weather-react.002-webpack-js/bundle.js:
--------------------------------------------------------------------------------
1 | !function(e){function t(n){if(i[n])return i[n].exports;var o=i[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t){var i,n,o={settings:{geoButton:$("#geo-button"),startPos:"",searchQuery:""},init:function(){i=this.settings,this.bindUIActions()},bindUIActions:function(){i.geoButton.on("click",function(){o.getGeoLocation()})},getGeoLocation:function(e){navigator.geolocation.getCurrentPosition(o.geoSuccess)},geoSuccess:function(e){i.startPos=e,i.searchQuery="http://api.openweathermap.org/data/2.5/weather?lat="+i.startPos.coords.latitude+"&lon="+i.startPos.coords.longitude+"&appid=0596fe0573fa9daa94c2912e5e383ed3",$.getJSON(i.searchQuery,function(e){r.setWeatherData(e)})}},r={settings:{tempIcon:$("#temp-icon"),weather:$("#weather"),weatherInfo:$("#weather-info"),location:$("#location"),weatherDescription:$("#weather-description"),temperature:$("#temperature"),tempNumber:"",fahrenheit:$("#fahrenheit"),celsius:$("#celsius"),wind:$("#wind"),searchLocationInput:$("#search-location-input"),searchLocationButton:$("#search-location-button"),celsiusButton:$("#celsius"),fahrenheitButton:$("#fahrenheit"),humidity:$("#humidity"),speedUnit:$("#speed-unit"),windSpeed:"",windDirection:$("#wind-direction"),windDegree:"",dayOrNight:"",closeAttribution:$("#close-attribution"),openAttribution:$("#noun-project"),attributionModal:$("#attribution-links")},init:function(){n=this.settings,this.bindUIActions(),n.searchLocationInput.keypress(function(e){13===e.keyCode&&n.searchLocationButton.click()})},bindUIActions:function(){n.searchLocationButton.on("click",function(){r.getWeatherData()}),n.closeAttribution.on("click",function(){r.closeAttributionModal()}),n.openAttribution.on("click",function(){r.openAttributionModal()})},closeAttributionModal:function(){n.attributionModal.addClass("hide")},openAttributionModal:function(){n.attributionModal.removeClass("hide")},getWeatherData:function(e){""!==n.searchLocationInput.val()&&(n.searchQuery="http://api.openweathermap.org/data/2.5/weather?q="+n.searchLocationInput.val()+"&appid=0596fe0573fa9daa94c2912e5e383ed3",$.getJSON(n.searchQuery,function(e){r.setWeatherData(e)}))},setWeatherData:function(e){return e.sys?($("#front-page-description").addClass("hide"),n.weather.removeClass("hide"),n.location.text(e.name+", "+e.sys.country),n.humidity.text(e.main.humidity),n.weatherDescription.text(e.weather[0].description),n.tempNumber=e.main.temp,n.windSpeed=e.wind.speed,n.windDegree=e.wind.deg,r.getWeatherDirection(),void r.changeTempUnit()):void alert("Country error!!")},getWeatherDirection:function(){n.windDegree>337.5||n.windDegree<=22.5?n.windDirection.text("N"):22.5
2 |
3 |
4 |
5 |
6 | Weather App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
30 |
31 |
32 |
Blank Canvas Weather
33 | An Obligatory Weather App
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 | Humidity: %
46 |
47 | Wind:
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/react/weather-react.002-webpack-js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "weather-react.001",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "weather.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC"
12 | }
13 |
--------------------------------------------------------------------------------
/react/weather-react.002-webpack-js/weather.css:
--------------------------------------------------------------------------------
1 | *, *:before, *:after {
2 | -moz-box-sizing: border-box;
3 | -webkit-box-sizing: border-box;
4 | box-sizing: border-box;
5 | }
6 |
7 | .hide {
8 | display: none;
9 | }
10 |
11 | .geo-error-message, .attribution-links {
12 | max-width: 400px;
13 | -webkit-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
14 | -moz-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
15 | box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.75);
16 | background: #fff;
17 | padding: 25px;
18 | margin: 6rem auto;
19 | position: relative;
20 | z-index: 10;
21 | }
22 |
23 | .close-error, .close-attribution {
24 | position: absolute;
25 | top: 0;
26 | right: 0;
27 | border-radius: 0;
28 | border: 1px solid #ccc;
29 | background-color: #fff;
30 | }
31 |
32 | .search-bar {
33 | background: #fff;
34 | height: 129px;
35 | color: #555;
36 | padding-top: 5px;
37 | }
38 |
39 | .search-location-text {
40 | font-size: 16px;
41 | font-weight: 300;
42 | }
43 |
44 | .search-text {
45 | text-align: center;
46 | margin-bottom: 10px;
47 | }
48 |
49 | .search-button {
50 | border: none;
51 | width: 50%;
52 | background-color: #ddd;
53 | color: #555;
54 | height: 50px;
55 | font-size: 16px;
56 | display: inline-block;
57 | }
58 |
59 | .search-location-button {
60 | border-right: 1px solid #fff;
61 | }
62 |
63 | .geo-button {
64 | border-left: 1px solid #fff;
65 | }
66 |
67 | .search-location-input {
68 | color: #000;
69 | padding: 7px;
70 | }
71 |
72 | @media all and (min-width: 401px) {
73 | .search-bar {
74 | height: 107px;
75 | }
76 |
77 | }
78 |
79 | @media all and (min-width: 505px) {
80 |
81 | .search-bar {
82 | padding-top: 0;
83 | height: 50px;
84 | }
85 |
86 | .search-text {
87 | text-align: left;
88 | float: left;
89 | margin-top: 5px;
90 | margin-bottom: 0;
91 | padding-left: 10px;
92 | }
93 |
94 | .search-location-button-group {
95 | float: right;
96 | }
97 |
98 | .search-button {
99 | width: 50px;
100 | }
101 |
102 | }
103 |
104 | @media all and (min-width: 753px) {
105 |
106 | .search-bar {
107 | height: 64px;
108 | padding-top: 0;
109 | }
110 |
111 | .search-location-text {
112 | font-size: 24px;
113 | position: relative;
114 | top: 5px;
115 | }
116 |
117 | .search-text {
118 | text-align: left;
119 | display: inline;
120 | margin: 0;
121 | }
122 |
123 | .search-location-button-group {
124 | display: inline;
125 | float: right;
126 | }
127 |
128 | .search-button {
129 | width: 64px;
130 | float: left;
131 | height: 64px;
132 | }
133 | }
134 |
135 | body {
136 | margin: 0;
137 | padding: 0;
138 | font-family: 'Open Sans', sans-serif;
139 | }
140 |
141 | #rain-canvas, #weather-canvas, #cloud-canvas, #time-canvas, #lightning-canvas {
142 | position: absolute;
143 | z-index: -1;
144 | }
145 |
146 | /* Canvas Background Classes */
147 |
148 | .thunderstorm {
149 | background-color: #34495E;
150 | }
151 |
152 | .drizzle, .rain {
153 | background-color: #2080B6;
154 | }
155 |
156 | .snow {
157 | background-color: #959B9F;
158 | }
159 |
160 | .atmosphere {
161 | background-color: #5E668F;
162 | }
163 |
164 | .clouds {
165 | background-color: #3498DB;
166 | }
167 |
168 | .clearsky, .default-weather {
169 | background-color: #6FC2D4;
170 | }
171 |
172 | .extreme-weather {
173 | background-color: #C0392B;
174 | }
175 |
176 | .thunderstorm.nighttime {
177 | background-color: #1B2631;
178 | }
179 |
180 | .drizzle.nighttime, .rain.nighttime {
181 | background-color: #0E3A53;
182 | }
183 |
184 | .snow.nighttime {
185 | background-color: #333537;
186 | }
187 |
188 | .atmosphere.nighttime {
189 | background-color: #32364A;
190 | }
191 |
192 | .clouds.nighttime {
193 | background-color: #164362;
194 | }
195 |
196 | .clearsky.nighttime {
197 | background-color: #222;
198 | }
199 |
200 | .extreme-weather.nighttime {
201 | background-color: #491611;
202 | }
203 |
204 | h1, h2, h3, h4, h5, h6 {
205 | font-weight: 300;
206 | }
207 |
208 | .weather, .front-page-description {
209 | text-align: center;
210 | color: #fff;
211 | font-size: 20px;
212 | font-weight: 300;
213 | }
214 |
215 | .nighttime .weather {
216 | text-shadow: 1px 1px 4px #000;
217 | font-weight: 400;
218 | }
219 |
220 | .nighttime .checked {
221 | color: rgba(255, 255, 255, .5);
222 | }
223 |
224 | .weather-info {
225 | list-style-type: none;
226 | padding: 0;
227 | text-align: center;
228 | font-size: 16px;
229 | }
230 |
231 | .weather-item {
232 | text-align: center;
233 | display: inline-block;
234 | background-repeat: no-repeat;
235 | background-size: contain;
236 | }
237 |
238 | .middle {
239 | margin: 10px 0;
240 | }
241 |
242 | @media all and (min-width: 500px) {
243 | .middle {
244 | position: absolute;
245 | top: 50%;
246 | right: 0;
247 | left: 0;
248 | -webkit-transform: translateY(-50%);
249 | -moz-transform: translateY(-50%);
250 | -ms-transform: translateY(-50%);
251 | -o-transform: translateY(-50%);
252 | transform: translateY(-50%);
253 | }
254 | }
255 |
256 | .temp-change {
257 | clear: both;
258 | }
259 |
260 | .weather-container {
261 | position: relative;
262 | }
263 |
264 | .weather-description {
265 | display: inline-block;
266 | text-transform: capitalize;
267 | }
268 |
269 | .weather-description, .weather-box {
270 | padding: 10px 0;
271 | }
272 |
273 | .temp-change-button {
274 | background: none;
275 | border: none;
276 | padding: 0 10px;
277 | font-weight: 600;
278 | font-size: 14px;
279 | }
280 |
281 | .celsius {
282 | border-right: 2px solid #fff;
283 | }
284 |
285 | .checked {
286 | color: rgba(0, 0, 0, .5);
287 | }
288 |
289 | .temperature {
290 | font-size: 8rem;
291 | line-height: 1;
292 | margin-left: 35px;
293 | }
294 |
295 | @media all and (min-width: 320px) {
296 | .temperature {
297 | font-size: 12rem;
298 | }
299 | }
300 |
301 | .temperature:after {
302 | content: '\B0 C';
303 | font-size: 40px;
304 | line-height: 2.2;
305 | vertical-align: top;
306 | margin-left: -6px;
307 | }
308 |
309 | .fahrenheit-degree:after {
310 | content: '\B0 F';
311 | }
312 |
313 | .celsius-degree:after {
314 | content: '\B0 C';
315 | }
316 |
317 | a {
318 | color: #333;
319 | }
320 |
321 | /* Sticky Footer */
322 |
323 | * {
324 | margin: 0;
325 | }
326 |
327 | html, body {
328 | height: 100%;
329 | }
330 |
331 | .page-wrap {
332 | min-height: 100%;
333 | /* equal to footer height */
334 | margin-bottom: -100px;
335 | }
336 |
337 | .page-wrap:after {
338 | content: "";
339 | display: block;
340 | }
341 |
342 | .site-footer, .page-wrap:after {
343 | height: 100px;
344 | }
345 |
346 | .site-footer {
347 | background: #fff;
348 | color: #555;
349 | font-size: 14px;
350 | text-align: center;
351 | }
352 |
353 | .site-footer-section {
354 | padding: 5px 5px 0 5px;
355 | font-size: 14px;
356 | }
357 |
358 | .noun-project {
359 | border: none;
360 | background: none;
361 | padding: 0;
362 | text-decoration: underline;
363 | }
--------------------------------------------------------------------------------
/react/weather-react.002-webpack-js/weather.js:
--------------------------------------------------------------------------------
1 | var g, GLoc = {
2 | settings: {
3 | geoButton: $('#geo-button'),
4 | startPos: '',
5 | searchQuery: ''
6 | },
7 |
8 | init: function () {
9 | g = this.settings;
10 | this.bindUIActions();
11 | },
12 |
13 | bindUIActions: function () {
14 | g.geoButton.on('click', function () {
15 | GLoc.getGeoLocation();
16 | });
17 | },
18 |
19 | getGeoLocation: function (numToGet) {
20 | navigator.geolocation.getCurrentPosition(GLoc.geoSuccess);
21 | },
22 |
23 | geoSuccess: function (position) {
24 | // Do magic with the location
25 | g.startPos = position;
26 | g.searchQuery = 'http://api.openweathermap.org/data/2.5/weather?lat=' + g.startPos.coords.latitude + '&lon=' + g.startPos.coords.longitude + '&appid=0596fe0573fa9daa94c2912e5e383ed3' + '';
27 |
28 | $.getJSON(g.searchQuery, function (data) {
29 | WeatherInfo.setWeatherData(data);
30 | });
31 | }
32 | };
33 |
34 | var w, WeatherInfo = {
35 | settings: {
36 | tempIcon: $('#temp-icon'),
37 | weather: $('#weather'),
38 | weatherInfo: $('#weather-info'),
39 | location: $('#location'),
40 | weatherDescription: $('#weather-description'),
41 | temperature: $('#temperature'),
42 | tempNumber: '',
43 | fahrenheit: $('#fahrenheit'),
44 | celsius: $('#celsius'),
45 | wind: $('#wind'),
46 | searchLocationInput: $('#search-location-input'),
47 | searchLocationButton: $('#search-location-button'),
48 | celsiusButton: $('#celsius'),
49 | fahrenheitButton: $('#fahrenheit'),
50 | humidity: $('#humidity'),
51 | speedUnit: $('#speed-unit'),
52 | windSpeed: '',
53 | windDirection: $('#wind-direction'),
54 | windDegree: '',
55 | dayOrNight: '',
56 | closeAttribution: $('#close-attribution'),
57 | openAttribution: $('#noun-project'),
58 | attributionModal: $('#attribution-links')
59 | },
60 |
61 | init: function () {
62 | w = this.settings;
63 | this.bindUIActions();
64 | w.searchLocationInput.keypress(function (e) {
65 | if (e.keyCode === 13) {
66 | w.searchLocationButton.click();
67 | }
68 | });
69 | },
70 |
71 | bindUIActions: function () {
72 | w.searchLocationButton.on('click', function () {
73 | WeatherInfo.getWeatherData();
74 | });
75 |
76 | w.closeAttribution.on('click', function () {
77 | WeatherInfo.closeAttributionModal();
78 | });
79 |
80 | w.openAttribution.on('click', function () {
81 | WeatherInfo.openAttributionModal();
82 | });
83 | },
84 |
85 | closeAttributionModal: function () {
86 | w.attributionModal.addClass('hide');
87 | },
88 |
89 |
90 | openAttributionModal: function () {
91 | w.attributionModal.removeClass('hide');
92 | },
93 |
94 | getWeatherData: function (searchQuery) {
95 | if (w.searchLocationInput.val() !== '') {
96 | w.searchQuery = 'http://api.openweathermap.org/data/2.5/weather?q=' + w.searchLocationInput.val() + '&appid=0596fe0573fa9daa94c2912e5e383ed3' + '';
97 | $.getJSON(w.searchQuery, function (data) {
98 | WeatherInfo.setWeatherData(data);
99 | });
100 | }
101 | },
102 |
103 | setWeatherData: function (data) {
104 | if (!data.sys) {
105 | alert('Country error!!');
106 | return;
107 | }
108 |
109 | $('#front-page-description').addClass('hide');
110 | w.weather.removeClass('hide');
111 | w.location.text(data.name + ', ' + data.sys.country);
112 | w.humidity.text(data.main.humidity);
113 | w.weatherDescription.text(data.weather[0].description);
114 | w.tempNumber = data.main.temp;
115 | w.windSpeed = data.wind.speed;
116 | w.windDegree = data.wind.deg;
117 | WeatherInfo.getWeatherDirection();
118 | WeatherInfo.changeTempUnit();
119 | },
120 |
121 | getWeatherDirection: function () {
122 | if (w.windDegree > 337.5 || w.windDegree <= 22.5) {
123 | w.windDirection.text('N');
124 | } else if (22.5 < w.windDegree <= 67.5) {
125 | w.windDirection.text('NE');
126 | } else if (67.5 < w.windDegree <= 112.5) {
127 | w.windDirection.text('E');
128 | } else if (112.5 < w.windDegree <= 157.5) {
129 | w.windDirection.text('SE');
130 | } else if (157.5 < w.windDegree <= 202.5) {
131 | w.windDirection.text('S');
132 | } else if (202.5 < w.windDegree <= 247.5) {
133 | w.windDirection.text('SW');
134 | } else if (247.5 < w.windDegree <= 292.5) {
135 | w.windDirection.text('W');
136 | } else if (292.5 < w.windDegree <= 337.5) {
137 | w.windDirection.text('NW');
138 | }
139 |
140 | },
141 |
142 | isValid: function (weatherDataPiece) {
143 | if (typeof weatherDataPiece !== undefined) {
144 | return weatherDataPiece + ' ';
145 | } else {
146 | return '';
147 | }
148 | },
149 |
150 | changeTempUnit: function (unit) {
151 | var newTemp = w.tempNumber - 273.15;
152 |
153 | w.celsius.addClass('checked');
154 | w.fahrenheit.removeClass('checked');
155 | w.temperature.addClass('celsius-degree');
156 | w.temperature.removeClass('fahrenheit-degree');
157 | w.temperature.html(Math.round(newTemp));
158 | WeatherInfo.changeSpeedUnit('km');
159 | },
160 |
161 | changeSpeedUnit: function () {
162 | w.wind.text('' + Math.round(w.windSpeed * 3.6));
163 | w.speedUnit.text('km/h');
164 | }
165 | };
166 |
167 | $(function () {
168 | GLoc.init();
169 | WeatherInfo.init();
170 | });
171 |
--------------------------------------------------------------------------------
/react/weather-react.002-webpack-js/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: './weather.js',
3 | output: {
4 | filename: 'bundle.js',
5 | path: __dirname
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/react/weather-react.003-webpack-css/bundle.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 | var styles = __webpack_require__(1);
48 |
49 | var g, GLoc = {
50 | settings: {
51 | geoButton: $('#geo-button'),
52 | startPos: '',
53 | searchQuery: ''
54 | },
55 |
56 | init: function () {
57 | g = this.settings;
58 | this.bindUIActions();
59 | },
60 |
61 | bindUIActions: function () {
62 | g.geoButton.on('click', function () {
63 | GLoc.getGeoLocation();
64 | });
65 | },
66 |
67 | getGeoLocation: function (numToGet) {
68 | navigator.geolocation.getCurrentPosition(GLoc.geoSuccess);
69 | },
70 |
71 | geoSuccess: function (position) {
72 | // Do magic with the location
73 | g.startPos = position;
74 | g.searchQuery = 'http://api.openweathermap.org/data/2.5/weather?lat=' + g.startPos.coords.latitude + '&lon=' + g.startPos.coords.longitude + '&appid=0596fe0573fa9daa94c2912e5e383ed3' + '';
75 |
76 | $.getJSON(g.searchQuery, function (data) {
77 | WeatherInfo.setWeatherData(data);
78 | });
79 | }
80 | };
81 |
82 | var w, WeatherInfo = {
83 | settings: {
84 | tempIcon: $('#temp-icon'),
85 | weather: $('#weather'),
86 | weatherInfo: $('#weather-info'),
87 | location: $('#location'),
88 | weatherDescription: $('#weather-description'),
89 | temperature: $('#temperature'),
90 | tempNumber: '',
91 | fahrenheit: $('#fahrenheit'),
92 | celsius: $('#celsius'),
93 | wind: $('#wind'),
94 | searchLocationInput: $('#search-location-input'),
95 | searchLocationButton: $('#search-location-button'),
96 | celsiusButton: $('#celsius'),
97 | fahrenheitButton: $('#fahrenheit'),
98 | humidity: $('#humidity'),
99 | speedUnit: $('#speed-unit'),
100 | windSpeed: '',
101 | windDirection: $('#wind-direction'),
102 | windDegree: '',
103 | dayOrNight: '',
104 | closeAttribution: $('#close-attribution'),
105 | openAttribution: $('#noun-project'),
106 | attributionModal: $('#attribution-links')
107 | },
108 |
109 | init: function () {
110 | w = this.settings;
111 | this.bindUIActions();
112 | w.searchLocationInput.keypress(function (e) {
113 | if (e.keyCode === 13) {
114 | w.searchLocationButton.click();
115 | }
116 | });
117 | },
118 |
119 | bindUIActions: function () {
120 | w.searchLocationButton.on('click', function () {
121 | WeatherInfo.getWeatherData();
122 | });
123 |
124 | w.closeAttribution.on('click', function () {
125 | WeatherInfo.closeAttributionModal();
126 | });
127 |
128 | w.openAttribution.on('click', function () {
129 | WeatherInfo.openAttributionModal();
130 | });
131 | },
132 |
133 | closeAttributionModal: function () {
134 | w.attributionModal.addClass('hide');
135 | },
136 |
137 |
138 | openAttributionModal: function () {
139 | w.attributionModal.removeClass('hide');
140 | },
141 |
142 | getWeatherData: function (searchQuery) {
143 | if (w.searchLocationInput.val() !== '') {
144 | w.searchQuery = 'http://api.openweathermap.org/data/2.5/weather?q=' + w.searchLocationInput.val() + '&appid=0596fe0573fa9daa94c2912e5e383ed3' + '';
145 | $.getJSON(w.searchQuery, function (data) {
146 | WeatherInfo.setWeatherData(data);
147 | });
148 | }
149 | },
150 |
151 | setWeatherData: function (data) {
152 | if (!data.sys) {
153 | alert('Country error!!');
154 | return;
155 | }
156 |
157 | $('#front-page-description').addClass('hide');
158 | w.weather.removeClass('hide');
159 | w.location.text(data.name + ', ' + data.sys.country);
160 | w.humidity.text(data.main.humidity);
161 | w.weatherDescription.text(data.weather[0].description);
162 | w.tempNumber = data.main.temp;
163 | w.windSpeed = data.wind.speed;
164 | w.windDegree = data.wind.deg;
165 | WeatherInfo.getWeatherDirection();
166 | WeatherInfo.changeTempUnit();
167 | },
168 |
169 | getWeatherDirection: function () {
170 | if (w.windDegree > 337.5 || w.windDegree <= 22.5) {
171 | w.windDirection.text('N');
172 | } else if (22.5 < w.windDegree <= 67.5) {
173 | w.windDirection.text('NE');
174 | } else if (67.5 < w.windDegree <= 112.5) {
175 | w.windDirection.text('E');
176 | } else if (112.5 < w.windDegree <= 157.5) {
177 | w.windDirection.text('SE');
178 | } else if (157.5 < w.windDegree <= 202.5) {
179 | w.windDirection.text('S');
180 | } else if (202.5 < w.windDegree <= 247.5) {
181 | w.windDirection.text('SW');
182 | } else if (247.5 < w.windDegree <= 292.5) {
183 | w.windDirection.text('W');
184 | } else if (292.5 < w.windDegree <= 337.5) {
185 | w.windDirection.text('NW');
186 | }
187 |
188 | },
189 |
190 | isValid: function (weatherDataPiece) {
191 | if (typeof weatherDataPiece !== undefined) {
192 | return weatherDataPiece + ' ';
193 | } else {
194 | return '';
195 | }
196 | },
197 |
198 | changeTempUnit: function (unit) {
199 | var newTemp = w.tempNumber - 273.15;
200 |
201 | w.celsius.addClass('checked');
202 | w.fahrenheit.removeClass('checked');
203 | w.temperature.addClass('celsius-degree');
204 | w.temperature.removeClass('fahrenheit-degree');
205 | w.temperature.html(Math.round(newTemp));
206 | WeatherInfo.changeSpeedUnit('km');
207 | },
208 |
209 | changeSpeedUnit: function () {
210 | w.wind.text('' + Math.round(w.windSpeed * 3.6));
211 | w.speedUnit.text('km/h');
212 | }
213 | };
214 |
215 | $(function () {
216 | GLoc.init();
217 | WeatherInfo.init();
218 | });
219 |
220 |
221 | /***/ },
222 | /* 1 */
223 | /***/ function(module, exports, __webpack_require__) {
224 |
225 | // style-loader: Adds some css to the DOM by adding a