├── .DS_Store ├── .gitignore ├── README.md ├── app ├── .DS_Store ├── app.js ├── controllers │ └── main.controller.js ├── directives │ └── selectclick.directive.js ├── filters │ ├── int.filter.js │ └── totrusted.filter.js └── services │ └── web.service.js ├── assets ├── .DS_Store ├── css │ ├── main.css │ └── vendor.css ├── font │ ├── weathericons-regular-webfont.eot │ ├── weathericons-regular-webfont.svg │ ├── weathericons-regular-webfont.ttf │ ├── weathericons-regular-webfont.woff │ └── weathericons-regular-webfont.woff2 ├── images │ ├── .DS_Store │ ├── loading.svg │ └── sky.jpg ├── js │ ├── main.js │ └── vendor.js └── styl │ ├── .DS_Store │ ├── _flexbox.styl │ ├── _mixins.styl │ ├── _reset.styl │ └── app.styl ├── bower.json ├── gulpfile.js ├── index.html └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # climapp 2 | Uma simples aplicação de previsão do tempo com [AngularJS](https://angularjs.org/) e [Dark Sky API](https://darksky.net/dev/). 3 | 4 | ## Estrutura 5 | Como é um app pequeno a organização ficou bem simples: 6 | 7 | ``` 8 | app/ 9 | controllers/ 10 | main.controller.js 11 | directives/ 12 | selectclick.directive.js 13 | filters/ 14 | int.filter.js 15 | totrusted.filter.js 16 | services/ 17 | web.service.js 18 | app.js 19 | assets/ 20 | css/ 21 | main.css -> arquivos compilados para produção 22 | vendor.css -> arquivos compilados para produção 23 | font/ -> icones (Weather Icons) 24 | ... 25 | images/ 26 | ... 27 | js/ 28 | main.js -> arquivos compilados para produção 29 | vendor.js -> arquivos compilados para produção 30 | styl/ 31 | _flexbox.styl 32 | _mixins.styl 33 | _reset.styl 34 | app.styl -> arquivo principal para desenvolvimento 35 | 36 | index.html 37 | ``` 38 | 39 | ## Stylus 40 | Na criação dos estilos usei o pré-processador [Stylus](http://stylus-lang.com/). Pra mim esse é o melhor pré-processador de css ;). 41 | 42 | ## Gulp 43 | A compilação, minificação e organização dos arquivos é feito com o [Gulp](http://gulpjs.com/) 44 | 45 | ## Iniciando 46 | Para iniciar o projeto siga para desenvolvimento e produção siga estes passos: 47 | 48 | Instalando dependências do Npm: 49 | ``` 50 | $ npm install 51 | ``` 52 | 53 | Instalando dependências do Bower: 54 | ``` 55 | $ bower install 56 | ``` 57 | 58 | Montando a estrutura com o Gulp: 59 | ``` 60 | $ gulp 61 | ``` 62 | Para atualizar e compilar os arquivos automaticamente enquanto desenvolve: 63 | ``` 64 | $ gulp watch 65 | ``` 66 | 67 | ## Demonstração 68 | [https://camposdev.github.io/climapp/](https://camposdev.github.io/climapp/) 69 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/app/.DS_Store -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Iniciando projeto ;) 4 | * 5 | */ 6 | 7 | var app = angular.module('climApp', ['ngCookies', 'chart.js']); -------------------------------------------------------------------------------- /app/controllers/main.controller.js: -------------------------------------------------------------------------------- 1 | app.controller('MainController', ['$rootScope', '$http', 'WebService', '$filter', function( $rootScope, $http, WebService, $filter ){ 2 | 3 | var vm = this; 4 | 5 | /** 6 | * 7 | * Inicia consumindo a API e mostrando as informações da data atual 8 | * 9 | */ 10 | 11 | // Define unidade default 12 | $rootScope.unitMetrics = 'ca'; 13 | 14 | // Inicia com o loading na página 15 | vm.loaded = false; 16 | 17 | // Pega latitude e longitude com HTML5 18 | navigator.geolocation.getCurrentPosition(function(location) { 19 | // Chama função passando coordenadas da geolocalização 20 | setLocation( location.coords.latitude, location.coords.longitude ); 21 | }, 22 | function() { 23 | // Caso não consiga a localização configura manualmente 24 | setLocation( '-27.595378', '-48.548050' ); 25 | }); 26 | 27 | // Função que busca dados da API referente a latitude e logitude 28 | function setLocation ( lat, long ) { 29 | // Grava a localização atual 30 | vm.defaultLatitude = lat; 31 | vm.defaultLongitude = long; 32 | 33 | // Busca o nome da cidade pela localização 34 | WebService.geolocation( lat, long, function( local ) { 35 | vm.searchCity = local.city + ', ' + local.state + ', ' + local.country; 36 | }); 37 | 38 | // Mostra as informações do dia atual 39 | getForecast( lat, long, null, $rootScope.unitMetrics ); 40 | 41 | // Mostra os próximos 6 dias 42 | getDays( lat, long, null, $rootScope.unitMetrics ); 43 | 44 | // Desativa o loading da página 45 | vm.loaded = true; 46 | } 47 | 48 | // Atualiza a unidade de temperatura 49 | vm.changeUnit = function( unit ) { 50 | // Atualiza a unidade selecionada 51 | $rootScope.unitMetrics = unit; 52 | // Busca as informações novamente com a nova unidade selecionada 53 | getForecast( vm.defaultLatitude, vm.defaultLongitude, null, $rootScope.unitMetrics ); 54 | getDays( vm.defaultLatitude, vm.defaultLongitude, null, $rootScope.unitMetrics ); 55 | }; 56 | 57 | // Atualiza informações ao selecionar outro dia 58 | vm.changeDay = function( time, index ) { 59 | getForecast( vm.defaultLatitude, vm.defaultLongitude, time, $rootScope.unitMetrics ); 60 | // Ativa botão do dia selecionado 61 | vm.selectedDay = index; 62 | } 63 | 64 | // Lista de cidades ao digitar na busca 65 | vm.autoComplete = function( value ){ 66 | if ( vm.formSearch.$valid ){ 67 | WebService.getCity( vm.searchCity, function( res ) { 68 | vm.listCities = res.data.RESULTS; 69 | vm.loaded = true; 70 | }); 71 | } 72 | } 73 | 74 | // Seleciona uma nova cidade e recarrega os 75 | vm.selectCity = function( res ) { 76 | vm.searchCity = res.name; 77 | 78 | getForecast( res.lat, res.lon, null, $rootScope.unitMetrics ); 79 | getDays( res.lat, res.lon, null, $rootScope.unitMetrics ); 80 | 81 | vm.selectedDay = null; 82 | } 83 | 84 | 85 | /** 86 | * Função que pega dados da previsão para o dia atual OU do dia selecionado 87 | * @params {string} lat 88 | * @params {string} long 89 | * @params {date} time 90 | */ 91 | 92 | function getForecast( lat, long, time, unit ) { 93 | 94 | // Ativa classe para esconder o conteúdo até carregar os dados 95 | vm.loaded = false; 96 | 97 | return WebService.forecast( lat, long, time, unit, function( res ) { 98 | 99 | // Variavel para buscar informações atuais do tempo 100 | var currently = res.currently; 101 | 102 | // Formata a data atual 103 | vm.currentDay = $filter('date')( new Date(currently.time * 1000), 'EEEE'); 104 | 105 | // Se for selecionado outro dia oculta o horário atual 106 | if ( !time ) { 107 | vm.currentTime = ' - ' + $filter('date')( new Date(currently.time * 1000), 'HH:mm'); 108 | } else { 109 | vm.currentTime = ''; 110 | } 111 | 112 | // Armazena a descrição da pevisão atual e icone 113 | vm.currentStatus = currently.summary; 114 | vm.currentIcon = 'wi-forecast-io-' + currently.icon; 115 | 116 | vm.currentTemp = parseInt( currently.temperature ); 117 | 118 | // Armazena informação de umidade e velocidade do vendo 119 | vm.currentHumidity = parseInt( currently.humidity * 100 ); 120 | vm.curentMind = parseInt( currently.windSpeed ); 121 | 122 | // Desativa a classe para esconder o conteúdo depois de carregar 123 | vm.loaded = true; 124 | 125 | 126 | // Variavel para buscar informações por horário 127 | var hourly = res.hourly.data; 128 | 129 | // Cria um array para horários e temperaturas a serem inseridos no gráfico 130 | var hours = []; 131 | var temps = []; 132 | 133 | // Pega os próximos horários de 3 em 3 horas 134 | var count = 0; 135 | for ( var i = 0; i < 9; i++ ) { 136 | if ( i == 0 ) { 137 | count = i; 138 | } else { 139 | count += 3; 140 | } 141 | if ( hourly[count] ) { 142 | 143 | hours.push( $filter('date')( new Date( hourly[count]['time'] * 1000 ), 'HH:mm') ); 144 | temps.push( parseInt( hourly[count]['temperature'] ) ); 145 | 146 | } else { // Pega o ultimo horário do array 147 | 148 | hours.push( $filter('date')( new Date( hourly.slice(-1)[0]['time'] * 1000 ), 'HH:mm') ); 149 | temps.push( parseInt( hourly.slice(-1)[0]['temperature'] ) ); 150 | } 151 | } 152 | 153 | // Chama a função que monta o gráfico de temperaturas e horários 154 | vm.chart = setChart( hours, temps ); 155 | 156 | }); 157 | } 158 | 159 | /** 160 | * Função que busca da API a previsão dos próximos 6 dias 161 | * @params {string} lat 162 | * @params {string} long 163 | */ 164 | 165 | function getDays( lat, long, time, unit ) { 166 | 167 | return WebService.forecast( lat, long, null, unit, function( res ) { 168 | 169 | var daily = res.daily.data; 170 | vm.dailyList = daily; 171 | 172 | // Formata a data nos dias atuais e grava a data original para a chamada de outro dia 173 | for ( var key in vm.dailyList ) { 174 | vm.dailyList[key]['original_time'] = vm.dailyList[key]['time']; 175 | vm.dailyList[key]['time'] = new Date(vm.dailyList[key]['time'] * 1000); 176 | } 177 | }); 178 | } 179 | 180 | /** 181 | * Função que monta o gráfico de temperaturas 182 | * @params {array} hours 183 | * @params {array} temps 184 | */ 185 | 186 | function setChart( hours, temps ) { 187 | var chart = { 188 | labels: hours, 189 | colors: ['#ffffff'], 190 | data: [ 191 | temps 192 | ], 193 | datasetOverride: [{ yAxisID: 'y-axis-1' }], 194 | options: { 195 | scales: { 196 | yAxes: [ 197 | { 198 | id: 'y-axis-1', 199 | type: 'linear', 200 | display: false, 201 | position: 'left', 202 | gridLines: { 203 | display: false 204 | }, 205 | ticks: { 206 | display: false 207 | } 208 | } 209 | ], 210 | xAxes: [ 211 | { 212 | gridLines: { 213 | display: false 214 | }, 215 | ticks: { 216 | fontColor: "#FFF", 217 | fontFamily: 'montserrat', 218 | fontSize: 16 219 | } 220 | } 221 | ] 222 | }, 223 | hover: false, 224 | legend: { 225 | display: false 226 | }, 227 | tooltips: { 228 | enabled: true, 229 | mode: 'single', 230 | callbacks: { 231 | label: function(tooltipItem, data) { 232 | var label = data.labels[tooltipItem.index]; 233 | var datasetLabel = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; 234 | if ( $rootScope.unitMetrics == 'ca' ) { 235 | var unit = '°C'; 236 | } else { 237 | var unit = '°F'; 238 | } 239 | return datasetLabel + unit; 240 | } 241 | } 242 | }, 243 | maintainAspectRatio: false, 244 | scaleShowVerticalLines: false, 245 | } 246 | } 247 | 248 | return chart; 249 | } 250 | 251 | }]); -------------------------------------------------------------------------------- /app/directives/selectclick.directive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Seleciona o texto do campo de busca ao 4 | * para melhorar a expriência do usuário 5 | * 6 | */ 7 | 8 | app.directive('selectOnClick', ['$window', function ($window) { 9 | return { 10 | restrict: 'A', 11 | link: function (scope, element, attrs) { 12 | element.on('click', function () { 13 | if (!$window.getSelection().toString()) { 14 | this.setSelectionRange(0, this.value.length) 15 | } 16 | }); 17 | } 18 | }; 19 | }]); -------------------------------------------------------------------------------- /app/filters/int.filter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Filtrar números decimais 4 | * 5 | */ 6 | 7 | app.filter('int', function() { 8 | return function( input ) { 9 | return parseInt(input); 10 | }; 11 | }); -------------------------------------------------------------------------------- /app/filters/totrusted.filter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Corrige bind de texto 4 | * 5 | */ 6 | 7 | app.filter('toTrusted', ['$sce', function( $sce ) { 8 | return function( text ) { 9 | return $sce.trustAsHtml( text) ; 10 | } 11 | }]); -------------------------------------------------------------------------------- /app/services/web.service.js: -------------------------------------------------------------------------------- 1 | app.factory('WebService', ['$http', function( $http ){ 2 | 3 | 4 | // API Dark Sky 5 | var apiKey = '78840139b008a4a740b25540a4f59574'; 6 | 7 | /** 8 | * Busca informações da API 9 | * @params {string} lat 10 | * @params {string} long 11 | * @params {date} time 12 | * @params {string} unit 13 | * @params {function} callback 14 | * See https://darksky.net/dev/docs/forecast 15 | */ 16 | 17 | function forecast( lat, long, time, unit, callback ) { 18 | 19 | if ( time == null ) { 20 | var url = 'https://api.darksky.net/forecast/' + apiKey + '/' + lat + ',' + long + '?lang=pt&units=' + unit + '&callback=JSON_CALLBACK'; 21 | } else { 22 | var url = 'https://api.darksky.net/forecast/' + apiKey + '/' + lat + ',' + long + ',' + time + '?lang=pt&units=' + unit + '&callback=JSON_CALLBACK'; 23 | } 24 | 25 | return $http.jsonp( url ).then( function success( res ) { 26 | callback( res.data ); 27 | }); 28 | } 29 | 30 | /** 31 | * Busca de cidades com autocomplete 32 | * @param {string} input 33 | * @param {function} callback 34 | * See https://www.wunderground.com/weather/api/d/docs?d=autocomplete-api&MR=1 35 | */ 36 | 37 | function getCity( input, callback ) { 38 | 39 | var url = 'https://autocomplete.wunderground.com/aq?query=' + input + '&cb=JSON_CALLBACK'; 40 | return $http.jsonp( url ).then( function success( res ) { 41 | callback( res ); 42 | }); 43 | }; 44 | 45 | 46 | /** 47 | * Busca a localização referente as coordenadas passadas 48 | * @param {string} lat 49 | * @param {string} long 50 | * @param {function} callback 51 | */ 52 | 53 | function geolocation( lat, long, callback ) { 54 | 55 | var url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='+ lat +','+ long +'&key=AIzaSyBLBgEFOwy1Ghm0Ov-R6tol7RzYjAztjkw&callback='; 56 | return $http.get( url ).then( function success( res ) { 57 | 58 | // Filtra dados para pegar cidade, estado e pais 59 | var local = {}; 60 | for (var ac = 0; ac < res.data.results[0].address_components.length; ac++) { 61 | var component = res.data.results[0].address_components[ac]; 62 | 63 | switch(component.types[0]) { 64 | case 'locality': 65 | local.city = component.long_name; 66 | break; 67 | case 'administrative_area_level_1': 68 | local.state = component.short_name; 69 | break; 70 | case 'country': 71 | local.country = component.long_name; 72 | local.registered_country_iso_code = component.short_name; 73 | break; 74 | } 75 | }; 76 | 77 | callback( local ); 78 | }); 79 | } 80 | 81 | 82 | var service = { 83 | forecast: forecast, 84 | getCity: getCity, 85 | geolocation: geolocation 86 | } 87 | 88 | return service; 89 | 90 | }]); -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/.DS_Store -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | } 6 | body { 7 | margin: 0; 8 | } 9 | article, 10 | aside, 11 | details, 12 | figcaption, 13 | figure, 14 | footer, 15 | header, 16 | main, 17 | menu, 18 | nav, 19 | section, 20 | summary { 21 | display: block; 22 | } 23 | audio, 24 | canvas, 25 | progress, 26 | video { 27 | display: inline-block; 28 | } 29 | audio:not([controls]) { 30 | display: none; 31 | height: 0; 32 | } 33 | progress { 34 | vertical-align: baseline; 35 | } 36 | template, 37 | [hidden] { 38 | display: none; 39 | } 40 | a { 41 | background-color: transparent; 42 | -webkit-text-decoration-skip: objects; 43 | } 44 | a:active, 45 | a:hover { 46 | outline-width: 0; 47 | } 48 | abbr[title] { 49 | border-bottom: none; 50 | text-decoration: underline; 51 | text-decoration: underline dotted; 52 | } 53 | b, 54 | strong { 55 | font-weight: inherit; 56 | } 57 | b, 58 | strong { 59 | font-weight: bolder; 60 | } 61 | dfn { 62 | font-style: italic; 63 | } 64 | h1 { 65 | font-size: 2em; 66 | margin: 0.67em 0; 67 | } 68 | mark { 69 | background-color: #ff0; 70 | color: #000; 71 | } 72 | small { 73 | font-size: 80%; 74 | } 75 | sub, 76 | sup { 77 | font-size: 75%; 78 | line-height: 0; 79 | position: relative; 80 | vertical-align: baseline; 81 | } 82 | sub { 83 | bottom: -0.25em; 84 | } 85 | sup { 86 | top: -0.5em; 87 | } 88 | img { 89 | border-style: none; 90 | } 91 | svg:not(:root) { 92 | overflow: hidden; 93 | } 94 | code, 95 | kbd, 96 | pre, 97 | samp { 98 | font-family: monospace, monospace; 99 | font-size: 1em; 100 | } 101 | figure { 102 | margin: 1em 40px; 103 | } 104 | hr { 105 | box-sizing: content-box; 106 | height: 0; 107 | overflow: visible; 108 | } 109 | button, 110 | input, 111 | select, 112 | textarea { 113 | font: inherit; 114 | margin: 0; 115 | } 116 | optgroup { 117 | font-weight: bold; 118 | } 119 | button, 120 | input { 121 | overflow: visible; 122 | } 123 | button, 124 | select { 125 | text-transform: none; 126 | } 127 | button, 128 | html [type="button"], 129 | [type="reset"], 130 | [type="submit"] { 131 | -webkit-appearance: button; 132 | } 133 | button::-moz-focus-inner, 134 | [type="button"]::-moz-focus-inner, 135 | [type="reset"]::-moz-focus-inner, 136 | [type="submit"]::-moz-focus-inner { 137 | border-style: none; 138 | padding: 0; 139 | } 140 | button:-moz-focusring, 141 | [type="button"]:-moz-focusring, 142 | [type="reset"]:-moz-focusring, 143 | [type="submit"]:-moz-focusring { 144 | outline: 1px dotted ButtonText; 145 | } 146 | fieldset { 147 | border: 1px solid #c0c0c0; 148 | margin: 0 2px; 149 | padding: 0.35em 0.625em 0.75em; 150 | } 151 | legend { 152 | box-sizing: border-box; 153 | color: inherit; 154 | display: table; 155 | max-width: 100%; 156 | padding: 0; 157 | white-space: normal; 158 | } 159 | textarea { 160 | overflow: auto; 161 | } 162 | [type="checkbox"], 163 | [type="radio"] { 164 | box-sizing: border-box; 165 | padding: 0; 166 | } 167 | [type="number"]::-webkit-inner-spin-button, 168 | [type="number"]::-webkit-outer-spin-button { 169 | height: auto; 170 | } 171 | [type="search"] { 172 | -webkit-appearance: textfield; 173 | outline-offset: -2px; 174 | } 175 | [type="search"]::-webkit-search-cancel-button, 176 | [type="search"]::-webkit-search-decoration { 177 | -webkit-appearance: none; 178 | } 179 | ::-webkit-input-placeholder { 180 | color: inherit; 181 | opacity: 0.54; 182 | } 183 | ::-webkit-file-upload-button { 184 | -webkit-appearance: button; 185 | font: inherit; 186 | } 187 | h1, 188 | h2, 189 | h3 { 190 | font-weight: 400; 191 | } 192 | * { 193 | text-rendering: optimizeLegibility; 194 | -webkit-font-smoothing: antialiased; 195 | -moz-osx-font-smoothing: grayscale; 196 | text-shadow: 1px 1px 1px rgba(0,0,0,0.004); 197 | outline: none !important; 198 | box-sizing: border-box; 199 | } 200 | /*============================== 201 | = Global = 202 | ==============================*/ 203 | html { 204 | font-size: 62.5%; 205 | min-height: 100%; 206 | } 207 | body { 208 | min-height: 100%; 209 | font-family: 'Montserrat', sans-serif; 210 | font-weight: 400; 211 | color: #fff; 212 | background: #00b6db; 213 | background: -moz-linear-gradient(-45deg, #00b6db 0%, #00ba88 100%); 214 | background: -webkit-linear-gradient(-45deg, #00b6db 0%, #00ba88 100%); 215 | background: linear-gradient(135deg, #00b6db 0%, #00ba88 100%); 216 | opacity: 1; 217 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 218 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 219 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 220 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 221 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 222 | -webkit-transition: all 0.3s ease-out; 223 | -moz-transition: all 0.3s ease-out; 224 | -ms-transition: all 0.3s ease-out; 225 | -o-transition: all 0.3s ease-out; 226 | transition: all 0.3s ease-out; 227 | } 228 | body:before { 229 | content: ''; 230 | position: absolute; 231 | z-index: -1; 232 | width: 100%; 233 | height: 100%; 234 | background: url("../images/sky.jpg") center center; 235 | background-size: cover; 236 | -webkit-filter: blur(10px); 237 | -moz-filter: blur(10px); 238 | -ms-filter: blur(10px); 239 | -o-filter: blur(10px); 240 | filter: blur(10px); 241 | opacity: 0.2; 242 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); 243 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); 244 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); 245 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); 246 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); 247 | } 248 | body.ng-cloak { 249 | opacity: 0; 250 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 251 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 252 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 253 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 254 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 255 | } 256 | /*================================= 257 | = Variables = 258 | =================================*/ 259 | /*=============================== 260 | = Content = 261 | ===============================*/ 262 | .container { 263 | max-width: 850px; 264 | margin: 0 auto; 265 | } 266 | @media (max-width: 870px) { 267 | .container { 268 | width: 100%; 269 | padding: 0 20px; 270 | } 271 | } 272 | /*============================== 273 | = Header = 274 | ==============================*/ 275 | .header-page { 276 | padding: 70px 0 50px; 277 | text-align: center; 278 | } 279 | .title-page { 280 | font-size: 7rem; 281 | letter-spacing: -0.5rem; 282 | font-weight: 400; 283 | margin: 0; 284 | } 285 | /*====================================== 286 | = Search section = 287 | ======================================*/ 288 | .search-city { 289 | position: relative; 290 | margin: 0 auto 20px auto; 291 | max-width: 600px; 292 | } 293 | @media (max-width: 870px) { 294 | .search-city { 295 | max-width: 100%; 296 | } 297 | } 298 | .search-city .search { 299 | width: 100%; 300 | height: 60px; 301 | border: 1px solid rgba(255,255,255,0.5); 302 | background: transparent; 303 | font-size: 2.8rem; 304 | color: #fff; 305 | text-align: center; 306 | padding: 0 20px; 307 | box-sizing: border-box; 308 | -webkit-border-radius: 3px; 309 | -moz-border-radius: 3px; 310 | -ms-border-radius: 3px; 311 | -o-border-radius: 3px; 312 | border-radius: 3px; 313 | -webkit-transition: all 0.3s ease-out; 314 | -moz-transition: all 0.3s ease-out; 315 | -ms-transition: all 0.3s ease-out; 316 | -o-transition: all 0.3s ease-out; 317 | transition: all 0.3s ease-out; 318 | } 319 | @media (max-width: 870px) { 320 | .search-city .search { 321 | font-size: 1.8rem; 322 | } 323 | } 324 | .search-city .search:hover, 325 | .search-city .search:focus, 326 | .search-city .search.ng-not-empty { 327 | border-color: #fff; 328 | } 329 | .search-list { 330 | position: absolute; 331 | z-index: 3; 332 | top: 100%; 333 | left: 0; 334 | width: 100%; 335 | max-height: 0; 336 | padding: 0; 337 | margin: -2px 0 0; 338 | list-style: none; 339 | overflow-y: auto; 340 | background-color: #fff; 341 | border: 1px solid #fff; 342 | border-top: 0; 343 | box-sizing: border-box; 344 | visibility: hidden; 345 | opacity: 0; 346 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 347 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 348 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 349 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 350 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 351 | -webkit-border-radius: 0 0 3px 3px; 352 | -moz-border-radius: 0 0 3px 3px; 353 | -ms-border-radius: 0 0 3px 3px; 354 | -o-border-radius: 0 0 3px 3px; 355 | border-radius: 0 0 3px 3px; 356 | -webkit-transition: all 0.3s 0.3s ease-out; 357 | -moz-transition: all 0.3s 0.3s ease-out; 358 | -ms-transition: all 0.3s 0.3s ease-out; 359 | -o-transition: all 0.3s 0.3s ease-out; 360 | transition: all 0.3s 0.3s ease-out; 361 | } 362 | .search-list.open { 363 | opacity: 1; 364 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 365 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 366 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 367 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 368 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 369 | visibility: visible; 370 | max-height: 250px; 371 | } 372 | .search-list >.item { 373 | cursor: pointer; 374 | padding: 10px; 375 | font-size: 1.6rem; 376 | -webkit-transition: all 0.3s ease-out; 377 | -moz-transition: all 0.3s ease-out; 378 | -ms-transition: all 0.3s ease-out; 379 | -o-transition: all 0.3s ease-out; 380 | transition: all 0.3s ease-out; 381 | color: #00b6db; 382 | -webkit-transition: all 0.3s ease-out; 383 | -moz-transition: all 0.3s ease-out; 384 | -ms-transition: all 0.3s ease-out; 385 | -o-transition: all 0.3s ease-out; 386 | transition: all 0.3s ease-out; 387 | } 388 | .search-list >.item:hover, 389 | .search-list >.item:focus { 390 | color: #fff; 391 | background-color: #00b6db; 392 | } 393 | /*====================================== 394 | = Forecast Daily = 395 | ======================================*/ 396 | .daily-forecast { 397 | position: relative; 398 | } 399 | .daily-forecast:after { 400 | content: ''; 401 | position: absolute; 402 | z-index: 9; 403 | width: 100%; 404 | height: 100%; 405 | top: 0; 406 | left: 0; 407 | background: url("../images/loading.svg") center center no-repeat; 408 | opacity: 1; 409 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 410 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 411 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 412 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 413 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 414 | -webkit-transition: all 0.3s ease-out; 415 | -moz-transition: all 0.3s ease-out; 416 | -ms-transition: all 0.3s ease-out; 417 | -o-transition: all 0.3s ease-out; 418 | transition: all 0.3s ease-out; 419 | } 420 | .daily-forecast.load .container { 421 | opacity: 1; 422 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 423 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 424 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 425 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 426 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 427 | } 428 | .daily-forecast.load:after { 429 | opacity: 0; 430 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 431 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 432 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 433 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 434 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 435 | z-index: -1; 436 | } 437 | .daily-forecast .container { 438 | opacity: 0; 439 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 440 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 441 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 442 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 443 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); 444 | -webkit-transition: all 0.3s ease-out; 445 | -moz-transition: all 0.3s ease-out; 446 | -ms-transition: all 0.3s ease-out; 447 | -o-transition: all 0.3s ease-out; 448 | transition: all 0.3s ease-out; 449 | position: relative; 450 | } 451 | .daily-forecast .today { 452 | margin: 20px 0; 453 | text-align: center; 454 | font-size: 20px; 455 | } 456 | @media (max-width: 870px) { 457 | .daily-forecast .today { 458 | font-size: 1.8rem; 459 | } 460 | } 461 | .box-daily { 462 | display: -webkit-flex; 463 | display: -ms-flexbox; 464 | display: flex; 465 | -webkit-align-items: center; 466 | -ms-flex-align: center; 467 | align-items: center; 468 | -webkit-justify-content: center; 469 | -ms-flex-pack: center; 470 | justify-content: center; 471 | } 472 | @media (max-width: 870px) { 473 | .box-daily { 474 | position: relative; 475 | -webkit-flex-direction: column; 476 | -ms-flex-direction: column; 477 | flex-direction: column; 478 | } 479 | } 480 | .box-daily >.item { 481 | margin: 0 10px; 482 | } 483 | .box-daily >.item.-icon { 484 | font-size: 6.5rem; 485 | } 486 | @media (max-width: 870px) { 487 | .box-daily >.item.-icon { 488 | position: absolute; 489 | top: 30px; 490 | left: 50%; 491 | margin-left: -140px; 492 | font-size: 5rem; 493 | } 494 | } 495 | .box-daily >.item.-temp { 496 | font-size: 12rem; 497 | line-height: 12rem; 498 | } 499 | .unit-items { 500 | position: relative; 501 | width: 36px; 502 | -webkit-align-self: flex-start; 503 | -ms-flex-item-align: start; 504 | align-self: flex-start; 505 | } 506 | @media (max-width: 870px) { 507 | .unit-items { 508 | position: absolute; 509 | top: 0; 510 | right: 50%; 511 | margin-right: -130px !important; 512 | } 513 | } 514 | .unit-items >.unit { 515 | position: absolute; 516 | display: -webkit-flex; 517 | display: -ms-flexbox; 518 | display: flex; 519 | -webkit-align-items: center; 520 | -ms-flex-align: center; 521 | align-items: center; 522 | -webkit-justify-content: center; 523 | -ms-flex-pack: center; 524 | justify-content: center; 525 | width: 36px; 526 | height: 36px; 527 | top: 55px; 528 | margin: 0; 529 | font-size: 1.8rem; 530 | border: 1px solid transparent; 531 | cursor: pointer; 532 | -webkit-border-radius: 50%; 533 | -moz-border-radius: 50%; 534 | -ms-border-radius: 50%; 535 | -o-border-radius: 50%; 536 | border-radius: 50%; 537 | opacity: 0.5; 538 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 539 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 540 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 541 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 542 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 543 | -webkit-transition: all 0.3s ease-out; 544 | -moz-transition: all 0.3s ease-out; 545 | -ms-transition: all 0.3s ease-out; 546 | -o-transition: all 0.3s ease-out; 547 | transition: all 0.3s ease-out; 548 | } 549 | .unit-items >.unit:hover { 550 | opacity: 1; 551 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 552 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 553 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 554 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 555 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 556 | } 557 | .unit-items >.unit.-selected { 558 | top: 10px; 559 | border-color: #fff; 560 | margin-top: 0; 561 | opacity: 1; 562 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 563 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 564 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 565 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 566 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 567 | } 568 | .description-daily { 569 | max-width: 300px; 570 | } 571 | @media (max-width: 870px) { 572 | .description-daily { 573 | text-align: center; 574 | } 575 | } 576 | .description-daily >.status { 577 | font-size: 2.8rem; 578 | line-height: 2.8rem; 579 | margin: 0 0 5px; 580 | } 581 | @media (max-width: 870px) { 582 | .description-daily >.status { 583 | font-size: 2.2rem; 584 | line-height: 2.2rem; 585 | } 586 | } 587 | .list-status { 588 | padding: 0; 589 | margin: 0; 590 | list-style: none; 591 | } 592 | .list-status >.status { 593 | font-size: 1.8rem; 594 | } 595 | @media (max-width: 870px) { 596 | .list-status >.status { 597 | font-size: 1.6rem; 598 | } 599 | } 600 | /*======================================= 601 | = Forecast Hourly = 602 | =======================================*/ 603 | .box-chart { 604 | max-height: 100px; 605 | margin-top: 20px; 606 | } 607 | /*===================================== 608 | = Forecast Days = 609 | =====================================*/ 610 | .forecast-days { 611 | display: -webkit-flex; 612 | display: -ms-flexbox; 613 | display: flex; 614 | -webkit-justify-content: center; 615 | -ms-flex-pack: center; 616 | justify-content: center; 617 | -webkit-align-items: flex-start; 618 | -ms-flex-align: start; 619 | align-items: flex-start; 620 | margin: 40px 0; 621 | } 622 | @media (max-width: 870px) { 623 | .forecast-days { 624 | -webkit-flex-wrap: wrap; 625 | -ms-flex-wrap: wrap; 626 | flex-wrap: wrap; 627 | } 628 | } 629 | .box-day { 630 | border: 1px solid rgba(255,255,255,0.5); 631 | padding: 10px 15px; 632 | margin: 0 10px; 633 | -webkit-border-radius: 3px; 634 | -moz-border-radius: 3px; 635 | -ms-border-radius: 3px; 636 | -o-border-radius: 3px; 637 | border-radius: 3px; 638 | background-color: transparent; 639 | color: #fff; 640 | -webkit-flex: 0; 641 | -ms-flex: 0; 642 | flex: 0; 643 | -webkit-transition: all 0.3s ease-out; 644 | -moz-transition: all 0.3s ease-out; 645 | -ms-transition: all 0.3s ease-out; 646 | -o-transition: all 0.3s ease-out; 647 | transition: all 0.3s ease-out; 648 | } 649 | @media (max-width: 870px) { 650 | .box-day { 651 | margin: 10px; 652 | } 653 | } 654 | .box-day:hover, 655 | .box-day.-active { 656 | border-color: #fff; 657 | background-color: rgba(255,255,255,0.2); 658 | } 659 | .box-day:hover .title, 660 | .box-day.-active .title { 661 | opacity: 1; 662 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 663 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 664 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 665 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 666 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 667 | } 668 | .box-day:disabled { 669 | cursor: not-allowed; 670 | } 671 | .box-day >.title { 672 | display: block; 673 | text-align: center; 674 | margin: 0; 675 | font-size: 1.6rem; 676 | opacity: 0.5; 677 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 678 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 679 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 680 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 681 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 682 | -webkit-transition: all 0.3s ease-out; 683 | -moz-transition: all 0.3s ease-out; 684 | -ms-transition: all 0.3s ease-out; 685 | -o-transition: all 0.3s ease-out; 686 | transition: all 0.3s ease-out; 687 | } 688 | .box-day >.icon { 689 | display: block; 690 | text-align: center; 691 | font-size: 3rem; 692 | margin: 5px 0; 693 | } 694 | .temp-status { 695 | display: -webkit-flex; 696 | display: -ms-flexbox; 697 | display: flex; 698 | -webkit-align-items: flex-start; 699 | -ms-flex-align: start; 700 | align-items: flex-start; 701 | -webkit-justify-content: center; 702 | -ms-flex-pack: center; 703 | justify-content: center; 704 | } 705 | .temp-status >.max { 706 | font-size: 2.7rem; 707 | } 708 | .temp-status >.min { 709 | font-size: 1.8rem; 710 | opacity: 0.5; 711 | -webkit-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 712 | -moz-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 713 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 714 | -o-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 715 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); 716 | } 717 | /*============================== 718 | = Footer = 719 | ==============================*/ 720 | .footer-page { 721 | margin: 80px 0; 722 | text-align: center; 723 | } 724 | .footer-page p { 725 | font-size: 1.2rem; 726 | text-transform: uppercase; 727 | letter-spacing: 5px; 728 | } 729 | -------------------------------------------------------------------------------- /assets/css/vendor.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Weather Icons 2.0 3 | * Updated August 1, 2015 4 | * Weather themed icons for Bootstrap 5 | * Author - Erik Flowers - erik@helloerik.com 6 | * Email: erik@helloerik.com 7 | * Twitter: http://twitter.com/Erik_UX 8 | * ------------------------------------------------------------------------------ 9 | * Maintained at http://erikflowers.github.io/weather-icons 10 | * 11 | * License 12 | * ------------------------------------------------------------------------------ 13 | * - Font licensed under SIL OFL 1.1 - 14 | * http://scripts.sil.org/OFL 15 | * - CSS, SCSS and LESS are licensed under MIT License - 16 | * http://opensource.org/licenses/mit-license.html 17 | * - Documentation licensed under CC BY 3.0 - 18 | * http://creativecommons.org/licenses/by/3.0/ 19 | * - Inspired by and works great as a companion with Font Awesome 20 | * "Font Awesome by Dave Gandy - http://fontawesome.io" 21 | *//*! 22 | * Weather Icons 2.0 23 | * Updated August 1, 2015 24 | * Weather themed icons for Bootstrap 25 | * Author - Erik Flowers - erik@helloerik.com 26 | * Email: erik@helloerik.com 27 | * Twitter: http://twitter.com/Erik_UX 28 | * ------------------------------------------------------------------------------ 29 | * Maintained at http://erikflowers.github.io/weather-icons 30 | * 31 | * License 32 | * ------------------------------------------------------------------------------ 33 | * - Font licensed under SIL OFL 1.1 - 34 | * http://scripts.sil.org/OFL 35 | * - CSS, SCSS and LESS are licensed under MIT License - 36 | * http://opensource.org/licenses/mit-license.html 37 | * - Documentation licensed under CC BY 3.0 - 38 | * http://creativecommons.org/licenses/by/3.0/ 39 | * - Inspired by and works great as a companion with Font Awesome 40 | * "Font Awesome by Dave Gandy - http://fontawesome.io" 41 | */@font-face{font-family:weathericons;src:url(../font/weathericons-regular-webfont.eot);src:url(../font/weathericons-regular-webfont.eot?#iefix) format('embedded-opentype'),url(../font/weathericons-regular-webfont.woff2) format('woff2'),url(../font/weathericons-regular-webfont.woff) format('woff'),url(../font/weathericons-regular-webfont.ttf) format('truetype'),url(../font/weathericons-regular-webfont.svg#weather_iconsregular) format('svg');font-weight:400;font-style:normal}.wi{display:inline-block;font-family:weathericons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.wi-fw{text-align:center;width:1.4em}.wi-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.wi-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.wi-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.wi-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.wi-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}.wi-day-sunny:before{content:"\f00d"}.wi-day-cloudy:before{content:"\f002"}.wi-day-cloudy-gusts:before{content:"\f000"}.wi-day-cloudy-windy:before{content:"\f001"}.wi-day-fog:before{content:"\f003"}.wi-day-hail:before{content:"\f004"}.wi-day-haze:before{content:"\f0b6"}.wi-day-lightning:before{content:"\f005"}.wi-day-rain:before{content:"\f008"}.wi-day-rain-mix:before{content:"\f006"}.wi-day-rain-wind:before{content:"\f007"}.wi-day-showers:before{content:"\f009"}.wi-day-sleet:before{content:"\f0b2"}.wi-day-sleet-storm:before{content:"\f068"}.wi-day-snow:before{content:"\f00a"}.wi-day-snow-thunderstorm:before{content:"\f06b"}.wi-day-snow-wind:before{content:"\f065"}.wi-day-sprinkle:before{content:"\f00b"}.wi-day-storm-showers:before{content:"\f00e"}.wi-day-sunny-overcast:before{content:"\f00c"}.wi-day-thunderstorm:before{content:"\f010"}.wi-day-windy:before{content:"\f085"}.wi-solar-eclipse:before{content:"\f06e"}.wi-hot:before{content:"\f072"}.wi-day-cloudy-high:before{content:"\f07d"}.wi-day-light-wind:before{content:"\f0c4"}.wi-night-clear:before{content:"\f02e"}.wi-night-alt-cloudy:before{content:"\f086"}.wi-night-alt-cloudy-gusts:before{content:"\f022"}.wi-night-alt-cloudy-windy:before{content:"\f023"}.wi-night-alt-hail:before{content:"\f024"}.wi-night-alt-lightning:before{content:"\f025"}.wi-night-alt-rain:before{content:"\f028"}.wi-night-alt-rain-mix:before{content:"\f026"}.wi-night-alt-rain-wind:before{content:"\f027"}.wi-night-alt-showers:before{content:"\f029"}.wi-night-alt-sleet:before{content:"\f0b4"}.wi-night-alt-sleet-storm:before{content:"\f06a"}.wi-night-alt-snow:before{content:"\f02a"}.wi-night-alt-snow-thunderstorm:before{content:"\f06d"}.wi-night-alt-snow-wind:before{content:"\f067"}.wi-night-alt-sprinkle:before{content:"\f02b"}.wi-night-alt-storm-showers:before{content:"\f02c"}.wi-night-alt-thunderstorm:before{content:"\f02d"}.wi-night-cloudy:before{content:"\f031"}.wi-night-cloudy-gusts:before{content:"\f02f"}.wi-night-cloudy-windy:before{content:"\f030"}.wi-night-fog:before{content:"\f04a"}.wi-night-hail:before{content:"\f032"}.wi-night-lightning:before{content:"\f033"}.wi-night-partly-cloudy:before{content:"\f083"}.wi-night-rain:before{content:"\f036"}.wi-night-rain-mix:before{content:"\f034"}.wi-night-rain-wind:before{content:"\f035"}.wi-night-showers:before{content:"\f037"}.wi-night-sleet:before{content:"\f0b3"}.wi-night-sleet-storm:before{content:"\f069"}.wi-night-snow:before{content:"\f038"}.wi-night-snow-thunderstorm:before{content:"\f06c"}.wi-night-snow-wind:before{content:"\f066"}.wi-night-sprinkle:before{content:"\f039"}.wi-night-storm-showers:before{content:"\f03a"}.wi-night-thunderstorm:before{content:"\f03b"}.wi-lunar-eclipse:before{content:"\f070"}.wi-stars:before{content:"\f077"}.wi-storm-showers:before{content:"\f01d"}.wi-thunderstorm:before{content:"\f01e"}.wi-night-alt-cloudy-high:before{content:"\f07e"}.wi-night-cloudy-high:before{content:"\f080"}.wi-night-alt-partly-cloudy:before{content:"\f081"}.wi-cloud:before{content:"\f041"}.wi-cloudy:before{content:"\f013"}.wi-cloudy-gusts:before{content:"\f011"}.wi-cloudy-windy:before{content:"\f012"}.wi-fog:before{content:"\f014"}.wi-hail:before{content:"\f015"}.wi-rain:before{content:"\f019"}.wi-rain-mix:before{content:"\f017"}.wi-rain-wind:before{content:"\f018"}.wi-showers:before{content:"\f01a"}.wi-sleet:before{content:"\f0b5"}.wi-snow:before{content:"\f01b"}.wi-sprinkle:before{content:"\f01c"}.wi-storm-showers:before{content:"\f01d"}.wi-thunderstorm:before{content:"\f01e"}.wi-snow-wind:before{content:"\f064"}.wi-snow:before{content:"\f01b"}.wi-smog:before{content:"\f074"}.wi-smoke:before{content:"\f062"}.wi-lightning:before{content:"\f016"}.wi-raindrops:before{content:"\f04e"}.wi-raindrop:before{content:"\f078"}.wi-dust:before{content:"\f063"}.wi-snowflake-cold:before{content:"\f076"}.wi-windy:before{content:"\f021"}.wi-strong-wind:before{content:"\f050"}.wi-sandstorm:before{content:"\f082"}.wi-earthquake:before{content:"\f0c6"}.wi-fire:before{content:"\f0c7"}.wi-flood:before{content:"\f07c"}.wi-meteor:before{content:"\f071"}.wi-tsunami:before{content:"\f0c5"}.wi-volcano:before{content:"\f0c8"}.wi-hurricane:before{content:"\f073"}.wi-tornado:before{content:"\f056"}.wi-small-craft-advisory:before{content:"\f0cc"}.wi-gale-warning:before{content:"\f0cd"}.wi-storm-warning:before{content:"\f0ce"}.wi-hurricane-warning:before{content:"\f0cf"}.wi-wind-direction:before{content:"\f0b1"}.wi-alien:before{content:"\f075"}.wi-celsius:before{content:"\f03c"}.wi-fahrenheit:before{content:"\f045"}.wi-degrees:before{content:"\f042"}.wi-thermometer:before{content:"\f055"}.wi-thermometer-exterior:before{content:"\f053"}.wi-thermometer-internal:before{content:"\f054"}.wi-cloud-down:before{content:"\f03d"}.wi-cloud-up:before{content:"\f040"}.wi-cloud-refresh:before{content:"\f03e"}.wi-horizon:before{content:"\f047"}.wi-horizon-alt:before{content:"\f046"}.wi-sunrise:before{content:"\f051"}.wi-sunset:before{content:"\f052"}.wi-moonrise:before{content:"\f0c9"}.wi-moonset:before{content:"\f0ca"}.wi-refresh:before{content:"\f04c"}.wi-refresh-alt:before{content:"\f04b"}.wi-umbrella:before{content:"\f084"}.wi-barometer:before{content:"\f079"}.wi-humidity:before{content:"\f07a"}.wi-na:before{content:"\f07b"}.wi-train:before{content:"\f0cb"}.wi-moon-new:before{content:"\f095"}.wi-moon-waxing-crescent-1:before{content:"\f096"}.wi-moon-waxing-crescent-2:before{content:"\f097"}.wi-moon-waxing-crescent-3:before{content:"\f098"}.wi-moon-waxing-crescent-4:before{content:"\f099"}.wi-moon-waxing-crescent-5:before{content:"\f09a"}.wi-moon-waxing-crescent-6:before{content:"\f09b"}.wi-moon-first-quarter:before{content:"\f09c"}.wi-moon-waxing-gibbous-1:before{content:"\f09d"}.wi-moon-waxing-gibbous-2:before{content:"\f09e"}.wi-moon-waxing-gibbous-3:before{content:"\f09f"}.wi-moon-waxing-gibbous-4:before{content:"\f0a0"}.wi-moon-waxing-gibbous-5:before{content:"\f0a1"}.wi-moon-waxing-gibbous-6:before{content:"\f0a2"}.wi-moon-full:before{content:"\f0a3"}.wi-moon-waning-gibbous-1:before{content:"\f0a4"}.wi-moon-waning-gibbous-2:before{content:"\f0a5"}.wi-moon-waning-gibbous-3:before{content:"\f0a6"}.wi-moon-waning-gibbous-4:before{content:"\f0a7"}.wi-moon-waning-gibbous-5:before{content:"\f0a8"}.wi-moon-waning-gibbous-6:before{content:"\f0a9"}.wi-moon-third-quarter:before{content:"\f0aa"}.wi-moon-waning-crescent-1:before{content:"\f0ab"}.wi-moon-waning-crescent-2:before{content:"\f0ac"}.wi-moon-waning-crescent-3:before{content:"\f0ad"}.wi-moon-waning-crescent-4:before{content:"\f0ae"}.wi-moon-waning-crescent-5:before{content:"\f0af"}.wi-moon-waning-crescent-6:before{content:"\f0b0"}.wi-moon-alt-new:before{content:"\f0eb"}.wi-moon-alt-waxing-crescent-1:before{content:"\f0d0"}.wi-moon-alt-waxing-crescent-2:before{content:"\f0d1"}.wi-moon-alt-waxing-crescent-3:before{content:"\f0d2"}.wi-moon-alt-waxing-crescent-4:before{content:"\f0d3"}.wi-moon-alt-waxing-crescent-5:before{content:"\f0d4"}.wi-moon-alt-waxing-crescent-6:before{content:"\f0d5"}.wi-moon-alt-first-quarter:before{content:"\f0d6"}.wi-moon-alt-waxing-gibbous-1:before{content:"\f0d7"}.wi-moon-alt-waxing-gibbous-2:before{content:"\f0d8"}.wi-moon-alt-waxing-gibbous-3:before{content:"\f0d9"}.wi-moon-alt-waxing-gibbous-4:before{content:"\f0da"}.wi-moon-alt-waxing-gibbous-5:before{content:"\f0db"}.wi-moon-alt-waxing-gibbous-6:before{content:"\f0dc"}.wi-moon-alt-full:before{content:"\f0dd"}.wi-moon-alt-waning-gibbous-1:before{content:"\f0de"}.wi-moon-alt-waning-gibbous-2:before{content:"\f0df"}.wi-moon-alt-waning-gibbous-3:before{content:"\f0e0"}.wi-moon-alt-waning-gibbous-4:before{content:"\f0e1"}.wi-moon-alt-waning-gibbous-5:before{content:"\f0e2"}.wi-moon-alt-waning-gibbous-6:before{content:"\f0e3"}.wi-moon-alt-third-quarter:before{content:"\f0e4"}.wi-moon-alt-waning-crescent-1:before{content:"\f0e5"}.wi-moon-alt-waning-crescent-2:before{content:"\f0e6"}.wi-moon-alt-waning-crescent-3:before{content:"\f0e7"}.wi-moon-alt-waning-crescent-4:before{content:"\f0e8"}.wi-moon-alt-waning-crescent-5:before{content:"\f0e9"}.wi-moon-alt-waning-crescent-6:before{content:"\f0ea"}.wi-moon-0:before{content:"\f095"}.wi-moon-1:before{content:"\f096"}.wi-moon-2:before{content:"\f097"}.wi-moon-3:before{content:"\f098"}.wi-moon-4:before{content:"\f099"}.wi-moon-5:before{content:"\f09a"}.wi-moon-6:before{content:"\f09b"}.wi-moon-7:before{content:"\f09c"}.wi-moon-8:before{content:"\f09d"}.wi-moon-9:before{content:"\f09e"}.wi-moon-10:before{content:"\f09f"}.wi-moon-11:before{content:"\f0a0"}.wi-moon-12:before{content:"\f0a1"}.wi-moon-13:before{content:"\f0a2"}.wi-moon-14:before{content:"\f0a3"}.wi-moon-15:before{content:"\f0a4"}.wi-moon-16:before{content:"\f0a5"}.wi-moon-17:before{content:"\f0a6"}.wi-moon-18:before{content:"\f0a7"}.wi-moon-19:before{content:"\f0a8"}.wi-moon-20:before{content:"\f0a9"}.wi-moon-21:before{content:"\f0aa"}.wi-moon-22:before{content:"\f0ab"}.wi-moon-23:before{content:"\f0ac"}.wi-moon-24:before{content:"\f0ad"}.wi-moon-25:before{content:"\f0ae"}.wi-moon-26:before{content:"\f0af"}.wi-moon-27:before{content:"\f0b0"}.wi-time-1:before{content:"\f08a"}.wi-time-2:before{content:"\f08b"}.wi-time-3:before{content:"\f08c"}.wi-time-4:before{content:"\f08d"}.wi-time-5:before{content:"\f08e"}.wi-time-6:before{content:"\f08f"}.wi-time-7:before{content:"\f090"}.wi-time-8:before{content:"\f091"}.wi-time-9:before{content:"\f092"}.wi-time-10:before{content:"\f093"}.wi-time-11:before{content:"\f094"}.wi-time-12:before{content:"\f089"}.wi-direction-up:before{content:"\f058"}.wi-direction-up-right:before{content:"\f057"}.wi-direction-right:before{content:"\f04d"}.wi-direction-down-right:before{content:"\f088"}.wi-direction-down:before{content:"\f044"}.wi-direction-down-left:before{content:"\f043"}.wi-direction-left:before{content:"\f048"}.wi-direction-up-left:before{content:"\f087"}.wi-wind-beaufort-0:before{content:"\f0b7"}.wi-wind-beaufort-1:before{content:"\f0b8"}.wi-wind-beaufort-2:before{content:"\f0b9"}.wi-wind-beaufort-3:before{content:"\f0ba"}.wi-wind-beaufort-4:before{content:"\f0bb"}.wi-wind-beaufort-5:before{content:"\f0bc"}.wi-wind-beaufort-6:before{content:"\f0bd"}.wi-wind-beaufort-7:before{content:"\f0be"}.wi-wind-beaufort-8:before{content:"\f0bf"}.wi-wind-beaufort-9:before{content:"\f0c0"}.wi-wind-beaufort-10:before{content:"\f0c1"}.wi-wind-beaufort-11:before{content:"\f0c2"}.wi-wind-beaufort-12:before{content:"\f0c3"}.wi-yahoo-0:before{content:"\f056"}.wi-yahoo-1:before{content:"\f00e"}.wi-yahoo-2:before{content:"\f073"}.wi-yahoo-3:before{content:"\f01e"}.wi-yahoo-4:before{content:"\f01e"}.wi-yahoo-5:before{content:"\f017"}.wi-yahoo-6:before{content:"\f017"}.wi-yahoo-7:before{content:"\f017"}.wi-yahoo-8:before{content:"\f015"}.wi-yahoo-9:before{content:"\f01a"}.wi-yahoo-10:before{content:"\f015"}.wi-yahoo-11:before{content:"\f01a"}.wi-yahoo-12:before{content:"\f01a"}.wi-yahoo-13:before{content:"\f01b"}.wi-yahoo-14:before{content:"\f00a"}.wi-yahoo-15:before{content:"\f064"}.wi-yahoo-16:before{content:"\f01b"}.wi-yahoo-17:before{content:"\f015"}.wi-yahoo-18:before{content:"\f017"}.wi-yahoo-19:before{content:"\f063"}.wi-yahoo-20:before{content:"\f014"}.wi-yahoo-21:before{content:"\f021"}.wi-yahoo-22:before{content:"\f062"}.wi-yahoo-23:before{content:"\f050"}.wi-yahoo-24:before{content:"\f050"}.wi-yahoo-25:before{content:"\f076"}.wi-yahoo-26:before{content:"\f013"}.wi-yahoo-27:before{content:"\f031"}.wi-yahoo-28:before{content:"\f002"}.wi-yahoo-29:before{content:"\f031"}.wi-yahoo-30:before{content:"\f002"}.wi-yahoo-31:before{content:"\f02e"}.wi-yahoo-32:before{content:"\f00d"}.wi-yahoo-33:before{content:"\f083"}.wi-yahoo-34:before{content:"\f00c"}.wi-yahoo-35:before{content:"\f017"}.wi-yahoo-36:before{content:"\f072"}.wi-yahoo-37:before{content:"\f00e"}.wi-yahoo-38:before{content:"\f00e"}.wi-yahoo-39:before{content:"\f00e"}.wi-yahoo-40:before{content:"\f01a"}.wi-yahoo-41:before{content:"\f064"}.wi-yahoo-42:before{content:"\f01b"}.wi-yahoo-43:before{content:"\f064"}.wi-yahoo-44:before{content:"\f00c"}.wi-yahoo-45:before{content:"\f00e"}.wi-yahoo-46:before{content:"\f01b"}.wi-yahoo-47:before{content:"\f00e"}.wi-yahoo-3200:before{content:"\f077"}.wi-forecast-io-clear-day:before{content:"\f00d"}.wi-forecast-io-clear-night:before{content:"\f02e"}.wi-forecast-io-rain:before{content:"\f019"}.wi-forecast-io-snow:before{content:"\f01b"}.wi-forecast-io-sleet:before{content:"\f0b5"}.wi-forecast-io-wind:before{content:"\f050"}.wi-forecast-io-fog:before{content:"\f014"}.wi-forecast-io-cloudy:before{content:"\f013"}.wi-forecast-io-partly-cloudy-day:before{content:"\f002"}.wi-forecast-io-partly-cloudy-night:before{content:"\f031"}.wi-forecast-io-hail:before{content:"\f015"}.wi-forecast-io-thunderstorm:before{content:"\f01e"}.wi-forecast-io-tornado:before{content:"\f056"}.wi-wmo4680-00:before,.wi-wmo4680-0:before{content:"\f055"}.wi-wmo4680-01:before,.wi-wmo4680-1:before{content:"\f013"}.wi-wmo4680-02:before,.wi-wmo4680-2:before{content:"\f055"}.wi-wmo4680-03:before,.wi-wmo4680-3:before{content:"\f013"}.wi-wmo4680-04:before,.wi-wmo4680-4:before{content:"\f014"}.wi-wmo4680-05:before,.wi-wmo4680-5:before{content:"\f014"}.wi-wmo4680-10:before{content:"\f014"}.wi-wmo4680-11:before{content:"\f014"}.wi-wmo4680-12:before{content:"\f016"}.wi-wmo4680-18:before{content:"\f050"}.wi-wmo4680-20:before{content:"\f014"}.wi-wmo4680-21:before{content:"\f017"}.wi-wmo4680-22:before{content:"\f017"}.wi-wmo4680-23:before{content:"\f019"}.wi-wmo4680-24:before{content:"\f01b"}.wi-wmo4680-25:before{content:"\f015"}.wi-wmo4680-26:before{content:"\f01e"}.wi-wmo4680-27:before{content:"\f063"}.wi-wmo4680-28:before{content:"\f063"}.wi-wmo4680-29:before{content:"\f063"}.wi-wmo4680-30:before{content:"\f014"}.wi-wmo4680-31:before{content:"\f014"}.wi-wmo4680-32:before{content:"\f014"}.wi-wmo4680-33:before{content:"\f014"}.wi-wmo4680-34:before{content:"\f014"}.wi-wmo4680-35:before{content:"\f014"}.wi-wmo4680-40:before{content:"\f017"}.wi-wmo4680-41:before{content:"\f01c"}.wi-wmo4680-42:before{content:"\f019"}.wi-wmo4680-43:before{content:"\f01c"}.wi-wmo4680-44:before{content:"\f019"}.wi-wmo4680-45:before{content:"\f015"}.wi-wmo4680-46:before{content:"\f015"}.wi-wmo4680-47:before{content:"\f01b"}.wi-wmo4680-48:before{content:"\f01b"}.wi-wmo4680-50:before{content:"\f01c"}.wi-wmo4680-51:before{content:"\f01c"}.wi-wmo4680-52:before{content:"\f019"}.wi-wmo4680-53:before{content:"\f019"}.wi-wmo4680-54:before{content:"\f076"}.wi-wmo4680-55:before{content:"\f076"}.wi-wmo4680-56:before{content:"\f076"}.wi-wmo4680-57:before{content:"\f01c"}.wi-wmo4680-58:before{content:"\f019"}.wi-wmo4680-60:before{content:"\f01c"}.wi-wmo4680-61:before{content:"\f01c"}.wi-wmo4680-62:before{content:"\f019"}.wi-wmo4680-63:before{content:"\f019"}.wi-wmo4680-64:before{content:"\f015"}.wi-wmo4680-65:before{content:"\f015"}.wi-wmo4680-66:before{content:"\f015"}.wi-wmo4680-67:before{content:"\f017"}.wi-wmo4680-68:before{content:"\f017"}.wi-wmo4680-70:before{content:"\f01b"}.wi-wmo4680-71:before{content:"\f01b"}.wi-wmo4680-72:before{content:"\f01b"}.wi-wmo4680-73:before{content:"\f01b"}.wi-wmo4680-74:before{content:"\f076"}.wi-wmo4680-75:before{content:"\f076"}.wi-wmo4680-76:before{content:"\f076"}.wi-wmo4680-77:before{content:"\f01b"}.wi-wmo4680-78:before{content:"\f076"}.wi-wmo4680-80:before{content:"\f019"}.wi-wmo4680-81:before{content:"\f01c"}.wi-wmo4680-82:before{content:"\f019"}.wi-wmo4680-83:before{content:"\f019"}.wi-wmo4680-84:before{content:"\f01d"}.wi-wmo4680-85:before{content:"\f017"}.wi-wmo4680-86:before{content:"\f017"}.wi-wmo4680-87:before{content:"\f017"}.wi-wmo4680-89:before{content:"\f015"}.wi-wmo4680-90:before{content:"\f016"}.wi-wmo4680-91:before{content:"\f01d"}.wi-wmo4680-92:before{content:"\f01e"}.wi-wmo4680-93:before{content:"\f01e"}.wi-wmo4680-94:before{content:"\f016"}.wi-wmo4680-95:before{content:"\f01e"}.wi-wmo4680-96:before{content:"\f01e"}.wi-wmo4680-99:before{content:"\f056"}.wi-owm-200:before{content:"\f01e"}.wi-owm-201:before{content:"\f01e"}.wi-owm-202:before{content:"\f01e"}.wi-owm-210:before{content:"\f016"}.wi-owm-211:before{content:"\f016"}.wi-owm-212:before{content:"\f016"}.wi-owm-221:before{content:"\f016"}.wi-owm-230:before{content:"\f01e"}.wi-owm-231:before{content:"\f01e"}.wi-owm-232:before{content:"\f01e"}.wi-owm-300:before{content:"\f01c"}.wi-owm-301:before{content:"\f01c"}.wi-owm-302:before{content:"\f019"}.wi-owm-310:before{content:"\f017"}.wi-owm-311:before{content:"\f019"}.wi-owm-312:before{content:"\f019"}.wi-owm-313:before{content:"\f01a"}.wi-owm-314:before{content:"\f019"}.wi-owm-321:before{content:"\f01c"}.wi-owm-500:before{content:"\f01c"}.wi-owm-501:before{content:"\f019"}.wi-owm-502:before{content:"\f019"}.wi-owm-503:before{content:"\f019"}.wi-owm-504:before{content:"\f019"}.wi-owm-511:before{content:"\f017"}.wi-owm-520:before{content:"\f01a"}.wi-owm-521:before{content:"\f01a"}.wi-owm-522:before{content:"\f01a"}.wi-owm-531:before{content:"\f01d"}.wi-owm-600:before{content:"\f01b"}.wi-owm-601:before{content:"\f01b"}.wi-owm-602:before{content:"\f0b5"}.wi-owm-611:before{content:"\f017"}.wi-owm-612:before{content:"\f017"}.wi-owm-615:before{content:"\f017"}.wi-owm-616:before{content:"\f017"}.wi-owm-620:before{content:"\f017"}.wi-owm-621:before{content:"\f01b"}.wi-owm-622:before{content:"\f01b"}.wi-owm-701:before{content:"\f01a"}.wi-owm-711:before{content:"\f062"}.wi-owm-721:before{content:"\f0b6"}.wi-owm-731:before{content:"\f063"}.wi-owm-741:before{content:"\f014"}.wi-owm-761:before{content:"\f063"}.wi-owm-762:before{content:"\f063"}.wi-owm-771:before{content:"\f011"}.wi-owm-781:before{content:"\f056"}.wi-owm-800:before{content:"\f00d"}.wi-owm-801:before{content:"\f011"}.wi-owm-802:before{content:"\f011"}.wi-owm-803:before{content:"\f012"}.wi-owm-804:before{content:"\f013"}.wi-owm-900:before{content:"\f056"}.wi-owm-901:before{content:"\f01d"}.wi-owm-902:before{content:"\f073"}.wi-owm-903:before{content:"\f076"}.wi-owm-904:before{content:"\f072"}.wi-owm-905:before{content:"\f021"}.wi-owm-906:before{content:"\f015"}.wi-owm-957:before{content:"\f050"}.wi-owm-day-200:before{content:"\f010"}.wi-owm-day-201:before{content:"\f010"}.wi-owm-day-202:before{content:"\f010"}.wi-owm-day-210:before{content:"\f005"}.wi-owm-day-211:before{content:"\f005"}.wi-owm-day-212:before{content:"\f005"}.wi-owm-day-221:before{content:"\f005"}.wi-owm-day-230:before{content:"\f010"}.wi-owm-day-231:before{content:"\f010"}.wi-owm-day-232:before{content:"\f010"}.wi-owm-day-300:before{content:"\f00b"}.wi-owm-day-301:before{content:"\f00b"}.wi-owm-day-302:before{content:"\f008"}.wi-owm-day-310:before{content:"\f008"}.wi-owm-day-311:before{content:"\f008"}.wi-owm-day-312:before{content:"\f008"}.wi-owm-day-313:before{content:"\f008"}.wi-owm-day-314:before{content:"\f008"}.wi-owm-day-321:before{content:"\f00b"}.wi-owm-day-500:before{content:"\f00b"}.wi-owm-day-501:before{content:"\f008"}.wi-owm-day-502:before{content:"\f008"}.wi-owm-day-503:before{content:"\f008"}.wi-owm-day-504:before{content:"\f008"}.wi-owm-day-511:before{content:"\f006"}.wi-owm-day-520:before{content:"\f009"}.wi-owm-day-521:before{content:"\f009"}.wi-owm-day-522:before{content:"\f009"}.wi-owm-day-531:before{content:"\f00e"}.wi-owm-day-600:before{content:"\f00a"}.wi-owm-day-601:before{content:"\f0b2"}.wi-owm-day-602:before{content:"\f00a"}.wi-owm-day-611:before{content:"\f006"}.wi-owm-day-612:before{content:"\f006"}.wi-owm-day-615:before{content:"\f006"}.wi-owm-day-616:before{content:"\f006"}.wi-owm-day-620:before{content:"\f006"}.wi-owm-day-621:before{content:"\f00a"}.wi-owm-day-622:before{content:"\f00a"}.wi-owm-day-701:before{content:"\f009"}.wi-owm-day-711:before{content:"\f062"}.wi-owm-day-721:before{content:"\f0b6"}.wi-owm-day-731:before{content:"\f063"}.wi-owm-day-741:before{content:"\f003"}.wi-owm-day-761:before{content:"\f063"}.wi-owm-day-762:before{content:"\f063"}.wi-owm-day-781:before{content:"\f056"}.wi-owm-day-800:before{content:"\f00d"}.wi-owm-day-801:before{content:"\f000"}.wi-owm-day-802:before{content:"\f000"}.wi-owm-day-803:before{content:"\f000"}.wi-owm-day-804:before{content:"\f00c"}.wi-owm-day-900:before{content:"\f056"}.wi-owm-day-902:before{content:"\f073"}.wi-owm-day-903:before{content:"\f076"}.wi-owm-day-904:before{content:"\f072"}.wi-owm-day-906:before{content:"\f004"}.wi-owm-day-957:before{content:"\f050"}.wi-owm-night-200:before{content:"\f02d"}.wi-owm-night-201:before{content:"\f02d"}.wi-owm-night-202:before{content:"\f02d"}.wi-owm-night-210:before{content:"\f025"}.wi-owm-night-211:before{content:"\f025"}.wi-owm-night-212:before{content:"\f025"}.wi-owm-night-221:before{content:"\f025"}.wi-owm-night-230:before{content:"\f02d"}.wi-owm-night-231:before{content:"\f02d"}.wi-owm-night-232:before{content:"\f02d"}.wi-owm-night-300:before{content:"\f02b"}.wi-owm-night-301:before{content:"\f02b"}.wi-owm-night-302:before{content:"\f028"}.wi-owm-night-310:before{content:"\f028"}.wi-owm-night-311:before{content:"\f028"}.wi-owm-night-312:before{content:"\f028"}.wi-owm-night-313:before{content:"\f028"}.wi-owm-night-314:before{content:"\f028"}.wi-owm-night-321:before{content:"\f02b"}.wi-owm-night-500:before{content:"\f02b"}.wi-owm-night-501:before{content:"\f028"}.wi-owm-night-502:before{content:"\f028"}.wi-owm-night-503:before{content:"\f028"}.wi-owm-night-504:before{content:"\f028"}.wi-owm-night-511:before{content:"\f026"}.wi-owm-night-520:before{content:"\f029"}.wi-owm-night-521:before{content:"\f029"}.wi-owm-night-522:before{content:"\f029"}.wi-owm-night-531:before{content:"\f02c"}.wi-owm-night-600:before{content:"\f02a"}.wi-owm-night-601:before{content:"\f0b4"}.wi-owm-night-602:before{content:"\f02a"}.wi-owm-night-611:before{content:"\f026"}.wi-owm-night-612:before{content:"\f026"}.wi-owm-night-615:before{content:"\f026"}.wi-owm-night-616:before{content:"\f026"}.wi-owm-night-620:before{content:"\f026"}.wi-owm-night-621:before{content:"\f02a"}.wi-owm-night-622:before{content:"\f02a"}.wi-owm-night-701:before{content:"\f029"}.wi-owm-night-711:before{content:"\f062"}.wi-owm-night-721:before{content:"\f0b6"}.wi-owm-night-731:before{content:"\f063"}.wi-owm-night-741:before{content:"\f04a"}.wi-owm-night-761:before{content:"\f063"}.wi-owm-night-762:before{content:"\f063"}.wi-owm-night-781:before{content:"\f056"}.wi-owm-night-800:before{content:"\f02e"}.wi-owm-night-801:before{content:"\f022"}.wi-owm-night-802:before{content:"\f022"}.wi-owm-night-803:before{content:"\f022"}.wi-owm-night-804:before{content:"\f086"}.wi-owm-night-900:before{content:"\f056"}.wi-owm-night-902:before{content:"\f073"}.wi-owm-night-903:before{content:"\f076"}.wi-owm-night-904:before{content:"\f072"}.wi-owm-night-906:before{content:"\f024"}.wi-owm-night-957:before{content:"\f050"}.wi-wu-chanceflurries:before{content:"\f064"}.wi-wu-chancerain:before{content:"\f019"}.wi-wu-chancesleat:before{content:"\f0b5"}.wi-wu-chancesnow:before{content:"\f01b"}.wi-wu-chancetstorms:before{content:"\f01e"}.wi-wu-clear:before{content:"\f00d"}.wi-wu-cloudy:before{content:"\f002"}.wi-wu-flurries:before{content:"\f064"}.wi-wu-hazy:before{content:"\f0b6"}.wi-wu-mostlycloudy:before{content:"\f002"}.wi-wu-mostlysunny:before{content:"\f00d"}.wi-wu-partlycloudy:before{content:"\f002"}.wi-wu-partlysunny:before{content:"\f00d"}.wi-wu-rain:before{content:"\f01a"}.wi-wu-sleat:before{content:"\f0b5"}.wi-wu-snow:before{content:"\f01b"}.wi-wu-sunny:before{content:"\f00d"}.wi-wu-tstorms:before{content:"\f01e"}.wi-wu-unknown:before{content:"\f00d"} -------------------------------------------------------------------------------- /assets/font/weathericons-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/font/weathericons-regular-webfont.eot -------------------------------------------------------------------------------- /assets/font/weathericons-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/font/weathericons-regular-webfont.ttf -------------------------------------------------------------------------------- /assets/font/weathericons-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/font/weathericons-regular-webfont.woff -------------------------------------------------------------------------------- /assets/font/weathericons-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/font/weathericons-regular-webfont.woff2 -------------------------------------------------------------------------------- /assets/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/images/.DS_Store -------------------------------------------------------------------------------- /assets/images/loading.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/sky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/images/sky.jpg -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Iniciando projeto ;) 4 | * 5 | */ 6 | 7 | var app = angular.module('climApp', ['ngCookies', 'chart.js']); 8 | app.controller('MainController', ['$rootScope', '$http', 'WebService', '$filter', function( $rootScope, $http, WebService, $filter ){ 9 | 10 | var vm = this; 11 | 12 | /** 13 | * 14 | * Inicia consumindo a API e mostrando as informações da data atual 15 | * 16 | */ 17 | 18 | // Define unidade default 19 | $rootScope.unitMetrics = 'ca'; 20 | 21 | // Inicia com o loading na página 22 | vm.loaded = false; 23 | 24 | // Pega latitude e longitude com HTML5 25 | navigator.geolocation.getCurrentPosition(function(location) { 26 | // Chama função passando coordenadas da geolocalização 27 | setLocation( location.coords.latitude, location.coords.longitude ); 28 | }, 29 | function() { 30 | // Caso não consiga a localização configura manualmente 31 | setLocation( '-27.595378', '-48.548050' ); 32 | }); 33 | 34 | // Função que busca dados da API referente a latitude e logitude 35 | function setLocation ( lat, long ) { 36 | // Grava a localização atual 37 | vm.defaultLatitude = lat; 38 | vm.defaultLongitude = long; 39 | 40 | // Busca o nome da cidade pela localização 41 | WebService.geolocation( lat, long, function( local ) { 42 | vm.searchCity = local.city + ', ' + local.state + ', ' + local.country; 43 | }); 44 | 45 | // Mostra as informações do dia atual 46 | getForecast( lat, long, null, $rootScope.unitMetrics ); 47 | 48 | // Mostra os próximos 6 dias 49 | getDays( lat, long, null, $rootScope.unitMetrics ); 50 | 51 | // Desativa o loading da página 52 | vm.loaded = true; 53 | } 54 | 55 | // Atualiza a unidade de temperatura 56 | vm.changeUnit = function( unit ) { 57 | // Atualiza a unidade selecionada 58 | $rootScope.unitMetrics = unit; 59 | // Busca as informações novamente com a nova unidade selecionada 60 | getForecast( vm.defaultLatitude, vm.defaultLongitude, null, $rootScope.unitMetrics ); 61 | getDays( vm.defaultLatitude, vm.defaultLongitude, null, $rootScope.unitMetrics ); 62 | }; 63 | 64 | // Atualiza informações ao selecionar outro dia 65 | vm.changeDay = function( time, index ) { 66 | getForecast( vm.defaultLatitude, vm.defaultLongitude, time, $rootScope.unitMetrics ); 67 | // Ativa botão do dia selecionado 68 | vm.selectedDay = index; 69 | } 70 | 71 | // Lista de cidades ao digitar na busca 72 | vm.autoComplete = function( value ){ 73 | if ( vm.formSearch.$valid ){ 74 | WebService.getCity( vm.searchCity, function( res ) { 75 | vm.listCities = res.data.RESULTS; 76 | vm.loaded = true; 77 | }); 78 | } 79 | } 80 | 81 | // Seleciona uma nova cidade e recarrega os 82 | vm.selectCity = function( res ) { 83 | vm.searchCity = res.name; 84 | 85 | getForecast( res.lat, res.lon, null, $rootScope.unitMetrics ); 86 | getDays( res.lat, res.lon, null, $rootScope.unitMetrics ); 87 | 88 | vm.selectedDay = null; 89 | } 90 | 91 | 92 | /** 93 | * Função que pega dados da previsão para o dia atual OU do dia selecionado 94 | * @params {string} lat 95 | * @params {string} long 96 | * @params {date} time 97 | */ 98 | 99 | function getForecast( lat, long, time, unit ) { 100 | 101 | // Ativa classe para esconder o conteúdo até carregar os dados 102 | vm.loaded = false; 103 | 104 | return WebService.forecast( lat, long, time, unit, function( res ) { 105 | 106 | // Variavel para buscar informações atuais do tempo 107 | var currently = res.currently; 108 | 109 | // Formata a data atual 110 | vm.currentDay = $filter('date')( new Date(currently.time * 1000), 'EEEE'); 111 | 112 | // Se for selecionado outro dia oculta o horário atual 113 | if ( !time ) { 114 | vm.currentTime = ' - ' + $filter('date')( new Date(currently.time * 1000), 'HH:mm'); 115 | } else { 116 | vm.currentTime = ''; 117 | } 118 | 119 | // Armazena a descrição da pevisão atual e icone 120 | vm.currentStatus = currently.summary; 121 | vm.currentIcon = 'wi-forecast-io-' + currently.icon; 122 | 123 | vm.currentTemp = parseInt( currently.temperature ); 124 | 125 | // Armazena informação de umidade e velocidade do vendo 126 | vm.currentHumidity = parseInt( currently.humidity * 100 ); 127 | vm.curentMind = parseInt( currently.windSpeed ); 128 | 129 | // Desativa a classe para esconder o conteúdo depois de carregar 130 | vm.loaded = true; 131 | 132 | 133 | // Variavel para buscar informações por horário 134 | var hourly = res.hourly.data; 135 | 136 | // Cria um array para horários e temperaturas a serem inseridos no gráfico 137 | var hours = []; 138 | var temps = []; 139 | 140 | // Pega os próximos horários de 3 em 3 horas 141 | var count = 0; 142 | for ( var i = 0; i < 9; i++ ) { 143 | if ( i == 0 ) { 144 | count = i; 145 | } else { 146 | count += 3; 147 | } 148 | if ( hourly[count] ) { 149 | 150 | hours.push( $filter('date')( new Date( hourly[count]['time'] * 1000 ), 'HH:mm') ); 151 | temps.push( parseInt( hourly[count]['temperature'] ) ); 152 | 153 | } else { // Pega o ultimo horário do array 154 | 155 | hours.push( $filter('date')( new Date( hourly.slice(-1)[0]['time'] * 1000 ), 'HH:mm') ); 156 | temps.push( parseInt( hourly.slice(-1)[0]['temperature'] ) ); 157 | } 158 | } 159 | 160 | // Chama a função que monta o gráfico de temperaturas e horários 161 | vm.chart = setChart( hours, temps ); 162 | 163 | }); 164 | } 165 | 166 | /** 167 | * Função que busca da API a previsão dos próximos 6 dias 168 | * @params {string} lat 169 | * @params {string} long 170 | */ 171 | 172 | function getDays( lat, long, time, unit ) { 173 | 174 | return WebService.forecast( lat, long, null, unit, function( res ) { 175 | 176 | var daily = res.daily.data; 177 | vm.dailyList = daily; 178 | 179 | // Formata a data nos dias atuais e grava a data original para a chamada de outro dia 180 | for ( var key in vm.dailyList ) { 181 | vm.dailyList[key]['original_time'] = vm.dailyList[key]['time']; 182 | vm.dailyList[key]['time'] = new Date(vm.dailyList[key]['time'] * 1000); 183 | } 184 | }); 185 | } 186 | 187 | /** 188 | * Função que monta o gráfico de temperaturas 189 | * @params {array} hours 190 | * @params {array} temps 191 | */ 192 | 193 | function setChart( hours, temps ) { 194 | var chart = { 195 | labels: hours, 196 | colors: ['#ffffff'], 197 | data: [ 198 | temps 199 | ], 200 | datasetOverride: [{ yAxisID: 'y-axis-1' }], 201 | options: { 202 | scales: { 203 | yAxes: [ 204 | { 205 | id: 'y-axis-1', 206 | type: 'linear', 207 | display: false, 208 | position: 'left', 209 | gridLines: { 210 | display: false 211 | }, 212 | ticks: { 213 | display: false 214 | } 215 | } 216 | ], 217 | xAxes: [ 218 | { 219 | gridLines: { 220 | display: false 221 | }, 222 | ticks: { 223 | fontColor: "#FFF", 224 | fontFamily: 'montserrat', 225 | fontSize: 16 226 | } 227 | } 228 | ] 229 | }, 230 | hover: false, 231 | legend: { 232 | display: false 233 | }, 234 | tooltips: { 235 | enabled: true, 236 | mode: 'single', 237 | callbacks: { 238 | label: function(tooltipItem, data) { 239 | var label = data.labels[tooltipItem.index]; 240 | var datasetLabel = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; 241 | if ( $rootScope.unitMetrics == 'ca' ) { 242 | var unit = '°C'; 243 | } else { 244 | var unit = '°F'; 245 | } 246 | return datasetLabel + unit; 247 | } 248 | } 249 | }, 250 | maintainAspectRatio: false, 251 | scaleShowVerticalLines: false, 252 | } 253 | } 254 | 255 | return chart; 256 | } 257 | 258 | }]); 259 | /** 260 | * 261 | * Seleciona o texto do campo de busca ao 262 | * para melhorar a expriência do usuário 263 | * 264 | */ 265 | 266 | app.directive('selectOnClick', ['$window', function ($window) { 267 | return { 268 | restrict: 'A', 269 | link: function (scope, element, attrs) { 270 | element.on('click', function () { 271 | if (!$window.getSelection().toString()) { 272 | this.setSelectionRange(0, this.value.length) 273 | } 274 | }); 275 | } 276 | }; 277 | }]); 278 | /** 279 | * 280 | * Filtrar números decimais 281 | * 282 | */ 283 | 284 | app.filter('int', function() { 285 | return function( input ) { 286 | return parseInt(input); 287 | }; 288 | }); 289 | /** 290 | * 291 | * Corrige bind de texto 292 | * 293 | */ 294 | 295 | app.filter('toTrusted', ['$sce', function( $sce ) { 296 | return function( text ) { 297 | return $sce.trustAsHtml( text) ; 298 | } 299 | }]); 300 | app.factory('WebService', ['$http', function( $http ){ 301 | 302 | 303 | // API Dark Sky 304 | var apiKey = '78840139b008a4a740b25540a4f59574'; 305 | 306 | /** 307 | * Busca informações da API 308 | * @params {string} lat 309 | * @params {string} long 310 | * @params {date} time 311 | * @params {string} unit 312 | * @params {function} callback 313 | * See https://darksky.net/dev/docs/forecast 314 | */ 315 | 316 | function forecast( lat, long, time, unit, callback ) { 317 | 318 | if ( time == null ) { 319 | var url = 'https://api.darksky.net/forecast/' + apiKey + '/' + lat + ',' + long + '?lang=pt&units=' + unit + '&callback=JSON_CALLBACK'; 320 | } else { 321 | var url = 'https://api.darksky.net/forecast/' + apiKey + '/' + lat + ',' + long + ',' + time + '?lang=pt&units=' + unit + '&callback=JSON_CALLBACK'; 322 | } 323 | 324 | return $http.jsonp( url ).then( function success( res ) { 325 | callback( res.data ); 326 | }); 327 | } 328 | 329 | /** 330 | * Busca de cidades com autocomplete 331 | * @param {string} input 332 | * @param {function} callback 333 | * See https://www.wunderground.com/weather/api/d/docs?d=autocomplete-api&MR=1 334 | */ 335 | 336 | function getCity( input, callback ) { 337 | 338 | var url = 'https://autocomplete.wunderground.com/aq?query=' + input + '&cb=JSON_CALLBACK'; 339 | return $http.jsonp( url ).then( function success( res ) { 340 | callback( res ); 341 | }); 342 | }; 343 | 344 | 345 | /** 346 | * Busca a localização referente as coordenadas passadas 347 | * @param {string} lat 348 | * @param {string} long 349 | * @param {function} callback 350 | */ 351 | 352 | function geolocation( lat, long, callback ) { 353 | 354 | var url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='+ lat +','+ long +'&key=AIzaSyBLBgEFOwy1Ghm0Ov-R6tol7RzYjAztjkw&callback='; 355 | return $http.get( url ).then( function success( res ) { 356 | 357 | // Filtra dados para pegar cidade, estado e pais 358 | var local = {}; 359 | for (var ac = 0; ac < res.data.results[0].address_components.length; ac++) { 360 | var component = res.data.results[0].address_components[ac]; 361 | 362 | switch(component.types[0]) { 363 | case 'locality': 364 | local.city = component.long_name; 365 | break; 366 | case 'administrative_area_level_1': 367 | local.state = component.short_name; 368 | break; 369 | case 'country': 370 | local.country = component.long_name; 371 | local.registered_country_iso_code = component.short_name; 372 | break; 373 | } 374 | }; 375 | 376 | callback( local ); 377 | }); 378 | } 379 | 380 | 381 | var service = { 382 | forecast: forecast, 383 | getCity: getCity, 384 | geolocation: geolocation 385 | } 386 | 387 | return service; 388 | 389 | }]); -------------------------------------------------------------------------------- /assets/styl/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/camposdev/climapp/4f7a36d42b03078ef26e58b825cacc752f193e03/assets/styl/.DS_Store -------------------------------------------------------------------------------- /assets/styl/_flexbox.styl: -------------------------------------------------------------------------------- 1 | flexbox(value) 2 | if value == inline-flex 3 | display -webkit-inline-flex 4 | display -ms-inline-flexbox 5 | display inline-flex 6 | else if value == flex 7 | display -webkit-flex 8 | display -ms-flexbox 9 | display flex 10 | 11 | flex(value) 12 | -webkit-flex value 13 | -ms-flex value 14 | flex value 15 | 16 | flex-basis(value) 17 | -webkit-flex-basis value 18 | -ms-flex-basis value 19 | flex-basis value 20 | 21 | flex-grow(value) 22 | -webkit-flex-grow value 23 | -ms-flex-grow value 24 | flex-grow value 25 | 26 | flex-shrink(value) 27 | -webkit-flex-shrink value 28 | -ms-flex-shrink value 29 | flex-shrink value 30 | 31 | flex-direction(value) 32 | -webkit-flex-direction value 33 | -ms-flex-direction value 34 | flex-direction value 35 | 36 | flex-wrap(value) 37 | -webkit-flex-wrap value 38 | -ms-flex-wrap value 39 | flex-wrap value 40 | 41 | flex-flow(value...) 42 | -webkit-flex-flow value 43 | -ms-flex-flow value 44 | flex-flow value 45 | 46 | justify-content(value) 47 | if value == start || value == end 48 | -webkit-justify-content flex-+value 49 | -ms-flex-pack value 50 | justify-content flex-+value 51 | else if value == center 52 | -webkit-justify-content value 53 | -ms-flex-pack value 54 | justify-content value 55 | else if value == space-between 56 | -webkit-justify-content value 57 | -ms-flex-pack justify 58 | justify-content value 59 | else if value == space-around 60 | -webkit-justify-content value 61 | -ms-flex-pack distribute 62 | justify-content value 63 | 64 | align-content(value) 65 | if value == space-around 66 | -webkit-align-content value 67 | -ms-flex-line-pack distribute 68 | align-content value 69 | else if value == space-between 70 | -webkit-align-content value 71 | -ms-flex-line-pack justify 72 | align-content value 73 | else if value == end or value == start 74 | -webkit-align-content flex-+value 75 | -ms-flex-line-pack value 76 | align-content flex-+value 77 | else 78 | -webkit-align-content value 79 | -ms-flex-line-pack value 80 | align-content value 81 | 82 | align-items(value) 83 | if value == start or value == end 84 | -webkit-align-items flex-+value 85 | -ms-flex-align value 86 | align-items flex-+value 87 | else 88 | -webkit-align-items value 89 | -ms-flex-align value 90 | align-items value 91 | 92 | align-self(value) 93 | if value == start or value == end 94 | -webkit-align-self flex-+value 95 | -ms-flex-item-align value 96 | align-self flex-+value 97 | else 98 | -webkit-align-self value 99 | -ms-flex-item-align value 100 | align-self value 101 | 102 | order(value) 103 | -ms-flex-order value 104 | -webkit-order value 105 | order value -------------------------------------------------------------------------------- /assets/styl/_mixins.styl: -------------------------------------------------------------------------------- 1 | vendor(prop, args) 2 | -webkit-{prop} args 3 | -moz-{prop} args 4 | -ms-{prop} args 5 | -o-{prop} args 6 | {prop} args 7 | 8 | transition() 9 | vendor('transition', all arguments ease-out) 10 | 11 | rounded() 12 | vendor('border-radius', arguments) 13 | 14 | support-for-ie ?= true 15 | opacity() 16 | opacity arguments 17 | if support-for-ie 18 | filter unquote('progid:DXImageTransform.Microsoft.Alpha(Opacity=' + round(arguments * 100) + ')') 19 | 20 | text-shadow() 21 | vendor('text-shadow', arguments) 22 | 23 | drop-shadow() 24 | vendor('box-shadow', arguments) 25 | 26 | transform() 27 | vendor('transform', arguments) 28 | 29 | animation() 30 | vendor('animation', arguments) 31 | 32 | filter() 33 | vendor('filter', arguments) -------------------------------------------------------------------------------- /assets/styl/_reset.styl: -------------------------------------------------------------------------------- 1 | /*================================= 2 | = Normalize = 3 | =================================*/ 4 | 5 | /** 6 | * 1. Change the default font family in all browsers (opinionated). 7 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS. 8 | */ 9 | 10 | html 11 | font-family sans-serif // 1 12 | -ms-text-size-adjust 100% // 2 13 | -webkit-text-size-adjust 100% // 2 14 | 15 | /** 16 | * Remove the margin in all browsers (opinionated). 17 | */ 18 | 19 | body 20 | margin 0 21 | 22 | /* HTML5 display definitions 23 | ========================================================================== */ 24 | 25 | /* 26 | * Add the correct display in IE 9-. 27 | * 1. Add the correct display in Edge, IE and Firefox. 28 | * 2. Add the correct display in IE. 29 | */ 30 | 31 | article, 32 | aside, 33 | details, // 1 34 | figcaption, 35 | figure, 36 | footer, 37 | header, 38 | main, // 2 39 | menu, 40 | nav, 41 | section, 42 | summary // 1 43 | display block 44 | 45 | /** 46 | * Add the correct display in IE 9-. 47 | */ 48 | 49 | audio, 50 | canvas, 51 | progress, 52 | video 53 | display inline-block 54 | 55 | /** 56 | * Add the correct display in iOS 4-7. 57 | */ 58 | 59 | audio:not([controls]) 60 | display none 61 | height 0 62 | 63 | /** 64 | * Add the correct vertical alignment in Chrome, Firefox, and Opera 65 | */ 66 | 67 | progress 68 | vertical-align baseline 69 | 70 | /** 71 | * Add the correct display in IE 10-. 72 | * 1. Add the correct display in IE. 73 | */ 74 | 75 | template, // 1 76 | [hidden] 77 | display none 78 | 79 | /* Links 80 | ========================================================================== */ 81 | 82 | /** 83 | * 1. Remove the gray background on active links in IE 10. 84 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 85 | */ 86 | 87 | a 88 | background-color transparent // 1 89 | -webkit-text-decoration-skip objects // 2 90 | 91 | /** 92 | * Remove the outline on focused links when they are also active or hovered 93 | * in all browsers (opinionated). 94 | */ 95 | 96 | a 97 | &:active, 98 | &:hover 99 | outline-width 0 100 | 101 | /* Text-level semantics 102 | ========================================================================== */ 103 | 104 | /** 105 | * 1. Remove the bottom border in Firefox 39-. 106 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 107 | */ 108 | 109 | abbr[title] 110 | border-bottom none // 1 111 | text-decoration underline // 2 112 | text-decoration underline dotted // 2 113 | 114 | /** 115 | * Prevent the duplicate appliclation of `bolder` by the next rule in Safari 6. 116 | */ 117 | 118 | b, 119 | strong 120 | font-weight inherit 121 | 122 | /** 123 | * Add the correct font weight in Chrome, Edge, and Safari. 124 | */ 125 | 126 | b, 127 | strong 128 | font-weight bolder 129 | 130 | /** 131 | * Add the correct font style in Android 4.3-. 132 | */ 133 | 134 | dfn 135 | font-style italic 136 | 137 | /** 138 | * Correct the font size and margin on `h1` elements within `section` and 139 | * `article` contexts in Chrome, Firefox, and Safari. 140 | */ 141 | 142 | h1 143 | font-size 2em 144 | margin 0.67em 0 145 | 146 | /** 147 | * Add the correct background and color in IE 9-. 148 | */ 149 | 150 | mark 151 | background-color #ff0 152 | color #000 153 | 154 | /** 155 | * Add the correct font size in all browsers. 156 | */ 157 | 158 | small 159 | font-size 80% 160 | 161 | /** 162 | * Prevent `sub` and `sup` elements from affecting the line height in 163 | * all browsers. 164 | */ 165 | 166 | sub, 167 | sup 168 | font-size 75% 169 | line-height 0 170 | position relative 171 | vertical-align baseline 172 | 173 | sub 174 | bottom -0.25em 175 | 176 | sup 177 | top -0.5em 178 | 179 | /* Embedded content 180 | ========================================================================== */ 181 | 182 | /** 183 | * Remove the border on images inside links in IE 10-. 184 | */ 185 | 186 | img 187 | border-style none 188 | 189 | /** 190 | * Hide the overflow in IE. 191 | */ 192 | 193 | svg:not(:root) 194 | overflow hidden 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * 1. Correct the inheritance and scaling of font size in all browsers. 201 | * 2. Correct the odd `em` font sizing in all browsers 202 | */ 203 | 204 | code, 205 | kbd, 206 | pre, 207 | samp 208 | font-family monospace, monospace // 1 209 | font-size 1em // 2 210 | 211 | /** 212 | * Add the correct marin in IE 8. 213 | */ 214 | 215 | figure 216 | margin 1em 40px 217 | 218 | /** 219 | * 1. Add the correct box sizing in Firefox 220 | * 2. Show the overflow in Edge and IE. 221 | */ 222 | 223 | hr 224 | box-sizing content-box // 1 225 | height 0 // 1 226 | overflow visible // 2 227 | 228 | /* Forms 229 | ========================================================================== */ 230 | 231 | /** 232 | * 1. Change font properties to `inherit` in all browsers (opinionated). 233 | * 2. Remove the margin in Firefox and Safari 234 | */ 235 | 236 | button, 237 | input, 238 | select, 239 | textarea 240 | font inherit // 1 241 | margin 0 // 2 242 | 243 | /** 244 | * Restore the font weight unset by previous rule. 245 | */ 246 | 247 | optgroup 248 | font-weight bold 249 | 250 | /** 251 | * Show the overflow in IE. 252 | * 1. Show the overflow in Edge. 253 | */ 254 | 255 | button, 256 | input 257 | overflow visible 258 | 259 | /** 260 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 261 | * 1. Remove the inheritance of text transform in Firefox 262 | */ 263 | 264 | button, 265 | select 266 | text-transform none 267 | 268 | /** 269 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 270 | * controls in Android 4. 271 | * 2. Correct the inability to style clickable types in iOS and Safari. 272 | */ 273 | 274 | button, 275 | html [type="button"], // 1 276 | [type="reset"], 277 | [type="submit"] 278 | -webkit-appearance button // 2 279 | 280 | /** 281 | * Remove the inner border and padding in Firefox 282 | */ 283 | 284 | button::-moz-focus-inner, 285 | [type="button"]::-moz-focus-inner, 286 | [type="reset"]::-moz-focus-inner, 287 | [type="submit"]::-moz-focus-inner 288 | border-style none 289 | padding 0 290 | 291 | /** 292 | * Restore the focus styles unset by the previous rule. 293 | */ 294 | 295 | button:-moz-focusring, 296 | [type="button"]:-moz-focusring, 297 | [type="reset"]:-moz-focusring, 298 | [type="submit"]:-moz-focusring 299 | outline 1px dotted ButtonText 300 | 301 | /** 302 | * Change the border, margin, and padding, in all browsers (opinionated). 303 | */ 304 | 305 | fieldset 306 | border 1px solid #c0c0c0 307 | margin 0 2px 308 | padding 0.35em 0.625em 0.75em 309 | 310 | /** 311 | * 1. Correct the text wrapping in Edge and IE. 312 | * 2. Correct the color inheritance from `fieldset` elements in IE. 313 | * 3. Remove the padding so developers are not caught out when they zero out 314 | * `fieldset` elements in all browsers. 315 | */ 316 | 317 | legend 318 | box-sizing border-box // 1 319 | color inherit // 2 320 | display table // 1 321 | max-width 100% // 1 322 | padding 0 // 3 323 | white-space normal // 1 324 | 325 | /** 326 | * Remove the default vertical scrollbar in IE. 327 | */ 328 | 329 | textarea 330 | overflow auto 331 | 332 | /** 333 | * 1. Add the correct box sizing in IE 10-. 334 | * 2. Remove the padding in IE 10-. 335 | */ 336 | 337 | [type="checkbox"], 338 | [type="radio"] 339 | box-sizing border-box // 1 340 | padding 0 // 2 341 | 342 | /** 343 | * Correct the cursor style of increment and decrement buttons in Chrome. 344 | */ 345 | 346 | [type="number"]::-webkit-inner-spin-button, 347 | [type="number"]::-webkit-outer-spin-button 348 | height auto 349 | 350 | /** 351 | * 1. Correct the odd appearance in Chrome and Safari. 352 | * 2. Correct the outline style in Safari. 353 | */ 354 | 355 | [type="search"] 356 | -webkit-appearance textfield 357 | outline-offset -2px 358 | 359 | /** 360 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 361 | */ 362 | 363 | [type="search"]::-webkit-search-cancel-button, 364 | [type="search"]::-webkit-search-decoration 365 | -webkit-appearance none 366 | 367 | /** 368 | * Correct the text style of placeholders in Chrome, Edge, and Safari 369 | */ 370 | 371 | ::-webkit-input-placeholder 372 | color inherit 373 | opacity 0.54 374 | 375 | /** 376 | * 1. Correct the inability to style clickable types in iOS and Safari. 377 | * 2. Change font properties to `inherit` in Safari. 378 | */ 379 | 380 | ::-webkit-file-upload-button 381 | -webkit-appearance button // 1 382 | font inherit // 2 383 | 384 | 385 | h1 386 | h2 387 | h3 388 | font-weight 400 389 | 390 | * 391 | text-rendering optimizeLegibility 392 | -webkit-font-smoothing antialiased 393 | -moz-osx-font-smoothing grayscale 394 | text-shadow 1px 1px 1px rgba(0,0,0,0.004) 395 | outline none !important 396 | box-sizing: border-box; -------------------------------------------------------------------------------- /assets/styl/app.styl: -------------------------------------------------------------------------------- 1 | @import '_reset' 2 | @import '_mixins' 3 | @import '_flexbox' 4 | 5 | 6 | /*============================== 7 | = Global = 8 | ==============================*/ 9 | 10 | html 11 | font-size 62.5% 12 | min-height 100% 13 | 14 | body 15 | min-height 100% 16 | font-family 'Montserrat', sans-serif 17 | font-weight 400 18 | color white 19 | background rgb(0,182,219) 20 | background -moz-linear-gradient(-45deg, rgba(0,182,219,1) 0%, rgba(0,186,136,1) 100%) 21 | background -webkit-linear-gradient(-45deg, rgba(0,182,219,1) 0%,rgba(0,186,136,1) 100%) 22 | background linear-gradient(135deg, rgba(0,182,219,1) 0%,rgba(0,186,136,1) 100%) 23 | opacity 1 24 | transition .3s 25 | &:before 26 | content '' 27 | position absolute 28 | z-index -1 29 | width 100% 30 | height 100% 31 | background url('../images/sky.jpg') center center 32 | background-size cover 33 | filter blur(10px) 34 | opacity .2 35 | &.ng-cloak 36 | opacity 0 37 | 38 | /*================================= 39 | = Variables = 40 | =================================*/ 41 | 42 | $blue = rgb(0,182,219) 43 | $green = rgb(0,186,136) 44 | 45 | 46 | /*=============================== 47 | = Content = 48 | ===============================*/ 49 | 50 | .container 51 | max-width 850px 52 | margin 0 auto 53 | @media( max-width 870px ) 54 | width 100% 55 | padding 0 20px 56 | 57 | /*============================== 58 | = Header = 59 | ==============================*/ 60 | 61 | .header-page 62 | padding 70px 0 50px 63 | text-align center 64 | 65 | .title-page 66 | font-size 7rem 67 | letter-spacing -.5rem 68 | font-weight 400 69 | margin 0 70 | 71 | 72 | /*====================================== 73 | = Search section = 74 | ======================================*/ 75 | 76 | .search-city 77 | position relative 78 | margin 0 auto 20px auto 79 | max-width 600px 80 | @media( max-width 870px ) 81 | max-width 100% 82 | .search 83 | width 100% 84 | height 60px 85 | border 1px solid rgba(255, 255, 255, .5) 86 | background transparent 87 | font-size 2.8rem 88 | color white 89 | text-align center 90 | padding 0 20px 91 | box-sizing border-box 92 | rounded 3px 93 | transition .3s 94 | @media( max-width 870px ) 95 | font-size 1.8rem 96 | &:hover 97 | &:focus 98 | &.ng-not-empty 99 | border-color white 100 | 101 | .search-list 102 | position absolute 103 | z-index 3 104 | top 100% 105 | left 0 106 | width 100% 107 | max-height 0 108 | padding 0 109 | margin -2px 0 0 110 | list-style none 111 | overflow-y auto 112 | background-color white 113 | border 1px solid white 114 | border-top 0 115 | box-sizing border-box 116 | visibility hidden 117 | opacity 0 118 | rounded 0 0 3px 3px 119 | transition .3s .3s 120 | &.open 121 | opacity 1 122 | visibility visible 123 | max-height 250px 124 | 125 | >.item 126 | cursor pointer 127 | padding 10px 128 | font-size 1.6rem 129 | transition .3s 130 | color $blue 131 | transition .3s 132 | &:hover 133 | &:focus 134 | color white 135 | background-color $blue 136 | 137 | 138 | /*====================================== 139 | = Forecast Daily = 140 | ======================================*/ 141 | 142 | .daily-forecast 143 | position relative 144 | &:after 145 | content '' 146 | position absolute 147 | z-index 9 148 | width 100% 149 | height 100% 150 | top 0 151 | left 0 152 | background url('../images/loading.svg') center center no-repeat 153 | opacity 1 154 | transition .3s 155 | &.load 156 | .container 157 | opacity 1 158 | &:after 159 | opacity 0 160 | z-index -1 161 | .container 162 | opacity 0 163 | transition .3s 164 | position relative 165 | .today 166 | margin 20px 0 167 | text-align center 168 | font-size 20px 169 | @media( max-width 870px ) 170 | font-size 1.8rem 171 | 172 | .box-daily 173 | flexbox flex 174 | align-items center 175 | justify-content center 176 | @media( max-width 870px ) 177 | position relative 178 | flex-direction column 179 | >.item 180 | margin 0 10px 181 | &.-icon 182 | font-size 6.5rem 183 | @media( max-width 870px ) 184 | position absolute 185 | top 30px 186 | left 50% 187 | margin-left -140px 188 | font-size 5rem 189 | 190 | &.-temp 191 | font-size 12rem 192 | line-height 12rem 193 | 194 | .unit-items 195 | position relative 196 | width 36px 197 | align-self start 198 | @media( max-width 870px ) 199 | position absolute 200 | top 0 201 | right 50% 202 | margin-right -130px !important 203 | 204 | >.unit 205 | position absolute 206 | flexbox flex 207 | align-items center 208 | justify-content center 209 | width 36px 210 | height 36px 211 | top 55px 212 | margin 0 213 | font-size 1.8rem 214 | border 1px solid transparent 215 | cursor pointer 216 | rounded 50% 217 | opacity .5 218 | transition .3s 219 | &:hover 220 | opacity 1 221 | &.-selected 222 | top 10px 223 | border-color white 224 | margin-top 0 225 | opacity 1 226 | 227 | .description-daily 228 | max-width 300px 229 | @media( max-width 870px ) 230 | text-align center 231 | >.status 232 | font-size 2.8rem 233 | line-height 2.8rem 234 | margin 0 0 5px 235 | @media( max-width 870px ) 236 | font-size 2.2rem 237 | line-height 2.2rem 238 | 239 | .list-status 240 | padding 0 241 | margin 0 242 | list-style none 243 | >.status 244 | font-size 1.8rem 245 | @media( max-width 870px ) 246 | font-size 1.6rem 247 | 248 | 249 | /*======================================= 250 | = Forecast Hourly = 251 | =======================================*/ 252 | 253 | .box-chart 254 | max-height 100px 255 | margin-top 20px 256 | 257 | 258 | /*===================================== 259 | = Forecast Days = 260 | =====================================*/ 261 | 262 | .forecast-days 263 | flexbox flex 264 | justify-content center 265 | align-items start 266 | margin 40px 0 267 | @media( max-width 870px ) 268 | flex-wrap wrap 269 | 270 | .box-day 271 | border 1px solid rgba(255, 255, 255, .5) 272 | padding 10px 15px 273 | margin 0 10px 274 | rounded 3px 275 | background-color transparent 276 | color white 277 | flex 0 0 130px 278 | transition .3s 279 | @media( max-width 870px ) 280 | margin 10px 281 | &:hover 282 | &.-active 283 | border-color white 284 | background-color rgba(255, 255, 255, .2) 285 | .title 286 | opacity 1 287 | &:disabled 288 | cursor not-allowed 289 | >.title 290 | display block 291 | text-align center 292 | margin 0 293 | font-size 1.6rem 294 | opacity .5 295 | transition .3s 296 | >.icon 297 | display block 298 | text-align center 299 | font-size 3rem 300 | margin 5px 0 301 | 302 | .temp-status 303 | flexbox flex 304 | align-items start 305 | justify-content center 306 | >.max 307 | font-size 2.7rem 308 | >.min 309 | font-size 1.8rem 310 | opacity .5 311 | 312 | 313 | /*============================== 314 | = Footer = 315 | ==============================*/ 316 | 317 | .footer-page 318 | margin 80px 0 319 | text-align center 320 | p 321 | font-size 1.2rem 322 | text-transform uppercase 323 | letter-spacing 5px 324 | 325 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "climapp", 3 | "description": "", 4 | "main": "index.js", 5 | "authors": [ 6 | "Felipe Campos " 7 | ], 8 | "license": "ISC", 9 | "homepage": "", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "angular": "~1.5.8", 19 | "angular-cookies": "~1.5.8", 20 | "angular-i18n": "~1.5.8", 21 | "angular-chart.js": "^1.1.1", 22 | "chart.js": "^2.4.0", 23 | "weather-icons": "^2.0.10" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require( 'gulp' ), 2 | concat = require( 'gulp-concat' ), 3 | uglify = require( 'gulp-uglify' ), 4 | stylus = require( 'gulp-stylus' ), 5 | cssmin = require( 'gulp-cssmin' ); 6 | 7 | 8 | // Build vendors bower 9 | gulp.task( 'vendors', function() { 10 | 11 | gulp.src( [ 12 | 'bower_components/angular/angular.js', 13 | 'bower_components/angular-cookies/angular-cookies.js', 14 | 'bower_components/angular-i18n/angular-locale_pt-br.js', 15 | 'bower_components/chart.js/dist/Chart.js', 16 | 'bower_components/angular-chart.js/dist/angular-chart.js' 17 | ] ) 18 | .pipe( concat( 'vendor.js' ) ) 19 | .pipe( gulp.dest( 'assets/js' ) ); 20 | 21 | gulp.src( 'bower_components/weather-icons/css/weather-icons.min.css' ) 22 | .pipe( concat( 'vendor.css' ) ) 23 | .pipe( gulp.dest( 'assets/css' ) ); 24 | 25 | gulp.src( 'bower_components/weather-icons/font/**/*' ) 26 | .pipe( gulp.dest( 'assets/font' ) ); 27 | 28 | }); 29 | 30 | // Build main file js 31 | gulp.task( 'buildJS', function() { 32 | 33 | return gulp.src( 'app/**/*js' ) 34 | .pipe( concat( 'main.js' ) ) 35 | .pipe( gulp.dest( 'assets/js/' )); 36 | 37 | }); 38 | 39 | // Build main file css 40 | gulp.task( 'buildCSS', function() { 41 | 42 | return gulp.src( 'assets/styl/app.styl' ) 43 | .pipe( stylus( {compress: false} ) ) 44 | .pipe( concat( 'main.css' ) ) 45 | .pipe( gulp.dest( 'assets/css/' ) ); 46 | 47 | }); 48 | 49 | // Watch 50 | gulp.task('watch', function() { 51 | 52 | gulp.watch( 'app/**/*js', ['buildJS'] ); 53 | gulp.watch( 'assets/styl/**/*.styl', ['buildCSS'] ); 54 | 55 | }); 56 | 57 | // build for development 58 | gulp.task( 'default', ['vendors', 'buildJS', 'buildCSS', 'watch'] ); 59 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Climapp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | climapp 17 | 18 | 19 | 20 | 21 | 22 | 23 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{ ctrl.currentDay }}{{ ctrl.currentTime }} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {{ ctrl.currentTemp }} 56 | 57 | 58 | 59 | °C 60 | °F 61 | 62 | 63 | 64 | {{ ctrl.currentStatus }} 65 | 66 | 67 | Umidade: {{ ctrl.currentHumidity }}% 68 | Vento: {{ ctrl.curentMind }} km/h 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 85 | {{ day.time | date: 'EEE' }} 86 | 87 | 88 | 89 | 90 | {{ day.temperatureMax | int }}° 91 | {{ day.temperatureMin | int }}° 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "climapp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "del": "^2.2.2", 13 | "gulp": "^3.9.1", 14 | "gulp-concat": "^2.6.1", 15 | "gulp-cssmin": "^0.1.7", 16 | "gulp-filter": "^4.0.0", 17 | "gulp-imagemin": "^3.1.1", 18 | "gulp-stylus": "^2.6.0", 19 | "gulp-uglify": "^2.0.0", 20 | "jasmine-core": "^2.5.2", 21 | "karma": "^1.3.0", 22 | "karma-chrome-launcher": "^2.0.0", 23 | "karma-jasmine": "^1.1.0", 24 | "main-bower-files": "^2.13.1" 25 | } 26 | } 27 | --------------------------------------------------------------------------------