├── .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 |
229 |

230 | What is the weather like in 231 | ? 232 | 233 |

234 | 235 |
236 | 238 | 239 |
240 |
241 | 242 |
243 |

Blank Canvas Weather

244 |

An Obligatory Weather App

245 |
246 | 247 |
248 |
249 |
250 |
251 |
252 |
253 |
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 |
284 |

285 | What is the weather like in 286 | ? 289 |

290 | 291 |
292 | 294 | 296 |
297 |
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 |
320 |
321 |
322 |
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 |
380 |

381 | What is the weather like in 382 | ? 387 | 388 |

389 | 390 |
391 | 393 | 394 |
395 |
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 |
572 |

573 | What is the weather like in 574 | ? 579 | 580 |

581 | 582 |
583 | 586 | 589 |
590 |
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 |
40 |
41 |
42 |
43 |
44 |
    45 |
  • Humidity: %
  • 46 |
  • 47 | Wind: 48 | 49 |
  • 50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 | Enjoy the weather at Codemotion 2015!!! 58 |
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 |
40 |
41 |
42 |
43 |
44 |
    45 |
  • Humidity: %
  • 46 |
  • 47 | Wind: 48 | 49 |
  • 50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 | Enjoy the weather at Codemotion 2015!!! 58 |
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 |
40 |
41 |
42 |
43 |
44 |
    45 |
  • Humidity: %
  • 46 |
  • 47 | Wind: 48 | 49 |
  • 50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 | Enjoy the weather at Codemotion 2015!!! 58 |
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