├── LICENSE ├── README.md ├── bower.json ├── css └── weather7.css ├── gulpfile.js ├── img ├── delete.svg ├── icon.png └── yahoo-logo.png ├── index.html ├── js └── weather7.js ├── manifest.php ├── package.json └── src ├── jade └── index.jade └── less └── weather7.less /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Vladimir Kharlampidi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![devDependency Status](https://david-dm.org/nolimits4web/weather7-material/dev-status.svg)](https://david-dm.org/nolimits4web/weather7-material#info=devDependencies) 2 | [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=nolimits4web&url=https://github.com/nolimits4web/weather7-material/&title=Weather7&language=JavaScript&tags=github&category=software) 3 | 4 | # Weather7-Material 5 | 6 | Weather7 is the simple weather webapp that demonstrates how easy to create fully functioning Android Material app with Framework7. With PhoneGap you can easily convert it to native Android app. 7 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Weather7 Material", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/nolimits4web/Weather7-Material.git" 6 | }, 7 | "description": "Framework7 weather demo app in Material style", 8 | "version": "1.0.0", 9 | "author": "Vladimir Kharlampidi", 10 | "homepage": "http://www.idangero.us/framework7", 11 | "keywords": ["mobile", "framework", "android", "material", "google", "cordova", "phonegap", "native", "touch", "appstore", "app", "f7", "framework7"], 12 | "main": "./", 13 | "license": ["MIT"], 14 | "ignore": [ 15 | ".*", 16 | "gulpfile.js", 17 | "node_modules", 18 | "package.json", 19 | "libs" 20 | ], 21 | "dependencies": { 22 | "framework7": "~1.2.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /css/weather7.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | overflow: hidden; 4 | height: 100%; 5 | width: 100%; 6 | } 7 | /*============================== 8 | Homepage places list 9 | ==============================*/ 10 | .places-list { 11 | margin: 0; 12 | } 13 | .places-list .item-after { 14 | font-size: 24px; 15 | max-height: none; 16 | color: rgba(0, 0, 0, 0.54); 17 | } 18 | .places-list .city { 19 | font-size: 20px; 20 | position: relative; 21 | overflow: hidden; 22 | max-width: 100%; 23 | text-overflow: ellipsis; 24 | white-space: nowrap; 25 | } 26 | .places-list .country { 27 | font-size: 14px; 28 | color: rgba(0, 0, 0, 0.54); 29 | } 30 | .places-list .list-block-label { 31 | text-align: center; 32 | opacity: 0.6; 33 | } 34 | .places-list .item-link .item-inner { 35 | background: none; 36 | padding-right: 16px; 37 | } 38 | .places-list .icon-delete { 39 | width: 24px; 40 | height: 24px; 41 | background: url(../img/delete.svg); 42 | opacity: 0.54; 43 | } 44 | .places-list .swipeout-actions-right a.swipeout-delete { 45 | background: #d3d3d3; 46 | } 47 | .places-list .item-link-more { 48 | position: absolute; 49 | width: 24px; 50 | height: 24px; 51 | right: 16px; 52 | top: 50%; 53 | margin-top: -12px; 54 | text-align: right; 55 | } 56 | .places-list .list-block ul:empty:after { 57 | display: none; 58 | } 59 | /*============================== 60 | Search popup styling 61 | ==============================*/ 62 | .popup .searchbar-input { 63 | height: 38px; 64 | } 65 | .popup .searchbar { 66 | height: 100%; 67 | } 68 | .popup .list-block { 69 | margin: 0; 70 | } 71 | .popup .list-block span { 72 | font-size: 14px; 73 | color: rgba(0, 0, 0, 0.54); 74 | margin-left: 5px; 75 | } 76 | .popup .list-block .item-link .item-inner { 77 | background: none; 78 | } 79 | .popup .preloader { 80 | position: absolute; 81 | right: 40px; 82 | width: 24px; 83 | height: 24px; 84 | top: 50%; 85 | margin-top: -12px; 86 | opacity: 0.8; 87 | pointer-events: none; 88 | display: none; 89 | } 90 | /*============================== 91 | Details page styles 92 | ==============================*/ 93 | .detail-page-header { 94 | background: #222; 95 | height: -webkit-calc(100vw / 4 * 3); 96 | height: calc(100vw / 4 * 3); 97 | max-height: -webkit-calc(100vh - 56px); 98 | max-height: calc(100vh - 56px); 99 | background-size: cover; 100 | background-position: center; 101 | position: relative; 102 | color: #fff; 103 | overflow: hidden; 104 | } 105 | .detail-page-header .detail-page-header-overlay { 106 | background: linear-gradient(to top, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0) 50%); 107 | position: absolute; 108 | left: 0; 109 | top: 0; 110 | width: 100%; 111 | height: 100%; 112 | } 113 | .detail-temp { 114 | font-size: 112px; 115 | line-height: 100px; 116 | font-weight: 300; 117 | position: absolute; 118 | left: 16px; 119 | bottom: 16px; 120 | } 121 | .detail-condition { 122 | text-align: center; 123 | position: absolute; 124 | right: 16px; 125 | bottom: 16px; 126 | font-size: 20px; 127 | font-weight: 300; 128 | z-index: 2; 129 | } 130 | .detail-condition:before { 131 | content: ''; 132 | left: 50%; 133 | top: 50%; 134 | width: 0%; 135 | height: 0%; 136 | border-radius: 100%; 137 | box-shadow: 0px 0px 50px 20px rgba(0, 0, 0, 0.2); 138 | position: absolute; 139 | z-index: -1; 140 | } 141 | .list-block.forecast-list { 142 | margin: 0; 143 | } 144 | .list-block.forecast-list ul:before, 145 | .list-block.forecast-list ul:after { 146 | display: none; 147 | } 148 | .list-block.forecast-list span { 149 | display: inline-block; 150 | } 151 | .list-block.forecast-list .item-inner { 152 | padding-top: 16px; 153 | padding-bottom: 16px; 154 | } 155 | .list-block.forecast-list .item-after { 156 | display: block; 157 | text-align: right; 158 | color: rgba(0, 0, 0, 0.87); 159 | height: auto; 160 | max-height: none; 161 | } 162 | .list-block.forecast-list .state { 163 | display: block; 164 | } 165 | .list-block.forecast-list .date { 166 | font-size: 14px; 167 | color: rgba(0, 0, 0, 0.54); 168 | } 169 | .list-block.forecast-list .temps span { 170 | width: 30px; 171 | } 172 | .list-block.forecast-list .temps span.low { 173 | color: rgba(0, 0, 0, 0.54); 174 | } 175 | /*============================== 176 | Tablet 177 | ==============================*/ 178 | @media (min-width: 720px) { 179 | .views .page { 180 | background: #eee; 181 | } 182 | .views .navbar { 183 | background-color: transparent !important; 184 | pointer-events: none; 185 | overflow: visible; 186 | } 187 | .views .navbar .left { 188 | pointer-events: auto; 189 | } 190 | .views .navbar .center { 191 | display: block; 192 | max-width: -webkit-calc(100% - 80% - 56px - 16px); 193 | max-width: -calc(100% - 80% - 56px - 16px); 194 | } 195 | .views .navbar-inner { 196 | overflow: visible; 197 | } 198 | .views .list-block { 199 | background: #fff; 200 | border-radius: 3px; 201 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 202 | width: 60%; 203 | margin: 32px auto; 204 | } 205 | .views .list-block ul:before { 206 | display: none; 207 | } 208 | .views .list-block:first-child { 209 | margin-top: 0; 210 | } 211 | .views .list-block .list-block-label { 212 | margin: 0; 213 | padding: 16px 0; 214 | font-size: 0; 215 | } 216 | .views .list-block.forecast-list { 217 | margin: 0 auto 32px; 218 | } 219 | .floating-button { 220 | left: 80%; 221 | margin-left: 32px; 222 | top: 76px; 223 | } 224 | .fake-navbar { 225 | height: 104px; 226 | position: absolute; 227 | left: 0; 228 | top: 0; 229 | width: 100%; 230 | } 231 | .detail-page-header { 232 | width: 60%; 233 | height: -webkit-calc(60vw / 16 * 9); 234 | height: calc(60vw / 16 * 9); 235 | margin-left: auto; 236 | margin-right: auto; 237 | border-radius: 3px 3px 0 0; 238 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 239 | z-index: 1; 240 | max-height: none; 241 | } 242 | .detail-page-header + .list-block { 243 | border-radius: 0 0 3px 3px; 244 | z-index: 2; 245 | position: relative; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 'use strict'; 3 | var gulp = require('gulp'), 4 | connect = require('gulp-connect'), 5 | open = require('gulp-open'), 6 | less = require('gulp-less'), 7 | jade = require('gulp-jade'), 8 | path = require('path'), 9 | fs = require('fs'), 10 | paths = { 11 | root: './', 12 | css: 'css/', 13 | js: 'js/', 14 | source: { 15 | less: 'src/less/', 16 | jade: 'src/jade/' 17 | } 18 | }, 19 | app = { 20 | filename: 'weather7', 21 | pkg: require('./bower.json'), 22 | banner: [ 23 | '/**', 24 | ' * <%= pkg.name %> <%= pkg.version %>', 25 | ' * <%= pkg.description %>', 26 | ' * ', 27 | ' * <%= pkg.homepage %>', 28 | ' * ', 29 | ' * Copyright <%= date.year %>, <%= pkg.author %>', 30 | ' * The iDangero.us', 31 | ' * http://www.idangero.us/', 32 | ' * ', 33 | ' * Licensed under <%= pkg.license.join(" & ") %>', 34 | ' * ', 35 | ' * Released on: <%= date.month %> <%= date.day %>, <%= date.year %>', 36 | ' */', 37 | ''].join('\n'), 38 | date: { 39 | year: new Date().getFullYear(), 40 | month: ('January February March April May June July August September October November December').split(' ')[new Date().getMonth()], 41 | day: new Date().getDate() 42 | }, 43 | 44 | }; 45 | 46 | /* ================================================================== 47 | Build App 48 | ================================================================== */ 49 | gulp.task('styles', function (cb) { 50 | gulp.src([paths.source.less + app.filename + '.less']) 51 | .pipe(less({ 52 | paths: [ path.join(__dirname, 'less', 'includes') ] 53 | })) 54 | .pipe(gulp.dest(paths.css)) 55 | .pipe(connect.reload()) 56 | .on('end', function () { 57 | cb(); 58 | }); 59 | }); 60 | gulp.task('jade', function (cb) { 61 | gulp.src([paths.source.jade + '*.jade']) 62 | .pipe(jade({ 63 | pretty: true, 64 | })) 65 | .pipe(gulp.dest(paths.root)) 66 | .pipe(connect.reload()) 67 | .on('end', function () { 68 | cb(); 69 | }); 70 | }); 71 | 72 | gulp.task('build', ['styles', 'jade'], function (cb) { 73 | cb(); 74 | }); 75 | 76 | /* ================================= 77 | Watch 78 | ================================= */ 79 | gulp.task('watch', function () { 80 | gulp.watch(paths.source.less + '*.less', [ 'styles' ]); 81 | gulp.watch(paths.source.jade + '*.jade', [ 'jade' ]); 82 | 83 | gulp.watch(paths.css + '*.css', function () { 84 | gulp.src(paths.css) 85 | .pipe(connect.reload()); 86 | }); 87 | gulp.watch(paths.js + '*.js', function () { 88 | gulp.src(paths.js) 89 | .pipe(connect.reload()); 90 | }); 91 | gulp.watch(paths.root + '*.html', function () { 92 | gulp.src(paths.root) 93 | .pipe(connect.reload()); 94 | }); 95 | 96 | }); 97 | 98 | gulp.task('connect', function () { 99 | return connect.server({ 100 | root: [ paths.root ], 101 | livereload: true, 102 | port:'3000' 103 | }); 104 | }); 105 | 106 | gulp.task('open', function () { 107 | return gulp.src(paths.root + 'index.html').pipe(open({ uri: 'http://localhost:3000/index.html'})); 108 | }); 109 | 110 | gulp.task('server', [ 'watch', 'connect', 'open' ]); 111 | 112 | gulp.task('default', [ 'server' ]); 113 | 114 | gulp.task('test', [ 'build' ]); 115 | })(); -------------------------------------------------------------------------------- /img/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/framework7io/Weather7-Material/59a2cf234d2537521946e4bd8eb69e44fb6924cb/img/icon.png -------------------------------------------------------------------------------- /img/yahoo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/framework7io/Weather7-Material/59a2cf234d2537521946e4bd8eb69e44fb6924cb/img/yahoo-logo.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Weather7 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 36 |
37 | 38 | 61 | 62 | 71 | 89 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /js/weather7.js: -------------------------------------------------------------------------------- 1 | // Initialize your app 2 | var myApp = new Framework7({ 3 | modalTitle: 'Weather7', 4 | material: true, 5 | materialPageLoadDelay: 200 6 | }); 7 | // Export selectors engine 8 | var $$ = Dom7; 9 | 10 | // Register required Template7 helpers, before templates compilation 11 | Template7.registerHelper('dayOfWeek', function (date) { 12 | date = new Date(date); 13 | var days = ('Sunday Monday Tuesday Wednesday Thursday Friday Saturday').split(' '); 14 | return days[date.getDay()]; 15 | }); 16 | Template7.registerHelper('formatedDated', function (date) { 17 | date = new Date(date); 18 | var months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '); 19 | return months[date.getMonth()] + ' ' + date.getDate() + ' ' + date.getFullYear(); 20 | }); 21 | // Fickr API Key. CHANGE TO YOUR OWN!!! 22 | var flickrAPIKey = '664c33273570a6c80067779f55f548d1'; 23 | 24 | // Templates using Template7 template engine 25 | myApp.searchResultsTemplate = Template7.compile($$('#search-results-template').html()); 26 | myApp.homeItemsTemplate = Template7.compile($$('#home-items-template').html()); 27 | myApp.detailsTemplate = Template7.compile($$('#details-template').html()); 28 | 29 | // Add view 30 | var mainView = myApp.addView('.view-main'); 31 | 32 | // Search Locations 33 | var searchTimeout; 34 | 35 | myApp.searchLocation = function (search) { 36 | if (search.trim() === '') { 37 | $$('.popup .search-results').html(''); 38 | return; 39 | } 40 | var query = encodeURIComponent('select * from geo.places where text="' + search + '"'); 41 | var q = 'http://query.yahooapis.com/v1/public/yql?q=' + query + '&format=json'; 42 | if (searchTimeout) clearTimeout(searchTimeout); 43 | $$('.popup .preloader').show(); 44 | searchTimeout = setTimeout(function () { 45 | $$.get(q, function (results) { 46 | var html = ''; 47 | results = JSON.parse(results); 48 | $$('.popup .preloader').hide(); 49 | if (results.query.count > 0) { 50 | var places = results.query.results.place; 51 | html = myApp.searchResultsTemplate(places); 52 | } 53 | $$('.popup .search-results').html(html); 54 | }); 55 | }, 300); 56 | }; 57 | 58 | // Handle search results 59 | var mySearchbar = myApp.searchbar('.searchbar', { 60 | customSearch: true, 61 | onDisable: function (s) { 62 | $$('.popup input[type="search"]')[0].blur(); 63 | myApp.closeModal('.popup'); 64 | }, 65 | onSearch: function (s, q) { 66 | myApp.searchLocation(s.query); 67 | }, 68 | onClear: function (s) { 69 | $$('.popup .search-results').html(''); 70 | } 71 | }); 72 | $$('.popup').on('open', function () { 73 | mySearchbar.enable(); 74 | }); 75 | $$('.popup').on('opened', function () { 76 | $$('.popup input[type="search"]')[0].focus(); 77 | }); 78 | $$('.popup .search-results').on('click', 'li', function () { 79 | var li = $$(this); 80 | var woeid = li.attr('data-woeid'); 81 | var city = li.attr('data-city'); 82 | var country = li.attr('data-country'); 83 | var places; 84 | if (localStorage.w7Places) places = JSON.parse(localStorage.w7Places); 85 | else places = []; 86 | places.push({ 87 | woeid: li.attr('data-woeid'), 88 | city: li.attr('data-city'), 89 | country: li.attr('data-country') 90 | }); 91 | localStorage.w7Places = JSON.stringify(places); 92 | myApp.updateWeatherData(function () { 93 | myApp.buildWeatherHTML(); 94 | }); 95 | }); 96 | // Get locations weather data 97 | myApp.updateWeatherData = function (callback) { 98 | var woeids = []; 99 | if (!localStorage.w7Places) return; 100 | var places = JSON.parse(localStorage.w7Places); 101 | if (places.length === 0) { 102 | localStorage.w7Data = JSON.stringify([]); 103 | return; 104 | } 105 | if (!navigator.onLine) { 106 | myApp.alert('You need internet connection to update weather data'); 107 | } 108 | for (var i = 0; i < places.length; i++) { 109 | woeids.push(places[i].woeid); 110 | } 111 | var query = encodeURIComponent('select * from weather.forecast where woeid in (' + JSON.stringify(woeids).replace('[', '').replace(']', '') + ') and u="c"'); 112 | var q = 'http://query.yahooapis.com/v1/public/yql?q=' + query + '&format=json'; 113 | myApp.showIndicator(); 114 | $$.get(q, function (data) { 115 | var weatherData = []; 116 | myApp.hideIndicator(); 117 | data = JSON.parse(data); 118 | if (!data.query || !data.query.results) return; 119 | var places = data.query.results.channel; 120 | var place; 121 | if ($$.isArray(places)) { 122 | for (var i = 0; i < places.length; i++) { 123 | place = places[i]; 124 | weatherData.push({ 125 | city: place.location.city, 126 | country: place.location.country, 127 | region: place.location.region, 128 | humidity: place.atmosphere.humidity, 129 | pressure: place.atmosphere.pressure, 130 | sunrise: place.astronomy.sunrise, 131 | sunset: place.astronomy.sunset, 132 | wind: place.wind, 133 | condition: place.item.condition, 134 | forecast: place.item.forecast, 135 | lat: place.item.lat, 136 | long: place.item.long, 137 | woeid: woeids[i] 138 | }); 139 | } 140 | } 141 | else { 142 | place = places; 143 | weatherData.push({ 144 | city: place.location.city, 145 | country: place.location.country, 146 | region: place.location.region, 147 | humidity: place.atmosphere.humidity, 148 | pressure: place.atmosphere.pressure, 149 | sunrise: place.astronomy.sunrise, 150 | sunset: place.astronomy.sunset, 151 | wind: place.wind, 152 | condition: place.item.condition, 153 | forecast: place.item.forecast, 154 | lat: place.item.lat, 155 | long: place.item.long, 156 | woeid: woeids[0] 157 | }); 158 | } 159 | localStorage.w7Data = JSON.stringify(weatherData); 160 | if (callback) callback(); 161 | }); 162 | }; 163 | // Build list of places on home page 164 | myApp.buildWeatherHTML = function () { 165 | var weatherData = localStorage.w7Data; 166 | if (!weatherData) return; 167 | $$('.places-list ul').html(''); 168 | weatherData = JSON.parse(weatherData); 169 | var html = myApp.homeItemsTemplate(weatherData); 170 | $$('.places-list ul').html(html); 171 | }; 172 | 173 | // Delete place 174 | $$('.places-list').on('delete', '.swipeout', function () { 175 | var woeid = $$(this).attr('data-woeid'); 176 | // Update Places 177 | if (!localStorage.w7Places) return; 178 | var places = JSON.parse(localStorage.w7Places); 179 | for (var i = 0; i < places.length; i++) { 180 | if (places[i].woeid === woeid) places.splice(i, 1); 181 | } 182 | localStorage.w7Places = JSON.stringify(places); 183 | // Update places data 184 | if (!localStorage.w7Data) return; 185 | var data = JSON.parse(localStorage.w7Data); 186 | for (i = 0; i < data.length; i++) { 187 | if (data[i].woeid === woeid) data.splice(i, 1); 188 | } 189 | localStorage.w7Data = JSON.stringify(data); 190 | if (data.length === 0) myApp.buildWeatherHTML(); 191 | }); 192 | 193 | 194 | 195 | // Update html and weather data on app load 196 | myApp.buildWeatherHTML(); 197 | myApp.updateWeatherData(function () { 198 | myApp.buildWeatherHTML(); 199 | }); 200 | 201 | // Build details page 202 | $$('.places-list').on('click', 'a.item-link', function (e) { 203 | var woeid = $$(this).attr('data-woeid'); 204 | var item; 205 | var weatherData = JSON.parse(localStorage.w7Data); 206 | for (var i = 0; i < weatherData.length; i++) { 207 | if (weatherData[i].woeid === woeid) item = weatherData[i]; 208 | } 209 | var pageContent = myApp.detailsTemplate(item); 210 | mainView.loadContent(pageContent); 211 | }); 212 | 213 | var photoXHR; 214 | var photoCache = {}; 215 | myApp.onPageAfterAnimation('detail', function (page) { 216 | var woeid = $$(page.container).attr('data-woeid'); 217 | var weatherData = JSON.parse(localStorage.w7Data); 218 | for (var i = 0; i < weatherData.length; i++) { 219 | if (weatherData[i].woeid === woeid) item = weatherData[i]; 220 | } 221 | 222 | function placePhotos(photos) { 223 | if (photos && photos.query && photos.query.count > 0) { 224 | var photo = photos.query.results.photo[Math.floor(Math.random() * photos.query.count)]; 225 | $$('.detail-page-header').css('background-image', 'url(https://farm'+photo.farm+'.staticflickr.com/'+photo.server+'/'+photo.id+'_'+photo.secret+'_c.jpg)'); 226 | } 227 | } 228 | if (photoCache[woeid]) { 229 | placePhotos(photoCache[woeid]); 230 | } 231 | else { 232 | var query = encodeURIComponent('select * from flickr.photos.search where has_geo="true" and woe_id="'+woeid+'" and api_key="92bd0de55a63046155c09f1a06876875"'); 233 | var q = 'https://query.yahooapis.com/v1/public/yql?q=' + query + '&format=json'; 234 | photoXHR = $$.get(q, function (res) { 235 | if (res) { 236 | photoCache[woeid] = JSON.parse(res); 237 | placePhotos(photoCache[woeid]); 238 | } 239 | }); 240 | } 241 | }); 242 | myApp.onPageBack('detail', function (page) { 243 | if (photoXHR) photoXHR.abort(); 244 | }); 245 | 246 | // Update app when manifest updated 247 | // http://www.html5rocks.com/en/tutorials/appcache/beginner/ 248 | // Check if a new cache is available on page load. 249 | window.addEventListener('load', function (e) { 250 | window.applicationCache.addEventListener('updateready', function (e) { 251 | if (window.applicationCache.status === window.applicationCache.UPDATEREADY) { 252 | // Browser downloaded a new app cache. 253 | myApp.confirm('A new version of weather7 is available. Do you want to load it right now?', function () { 254 | window.location.reload(); 255 | }); 256 | } else { 257 | // Manifest didn't changed. Nothing new to server. 258 | } 259 | }, false); 260 | }, false); 261 | -------------------------------------------------------------------------------- /manifest.php: -------------------------------------------------------------------------------- 1 | 13 | CACHE MANIFEST 14 | 15 | CACHE: 16 | 24 | 25 | NETWORK: 26 | * 27 | 28 | # Hash Version: -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather7-material", 3 | "version": "1.0.0", 4 | "description": "Framework7 weather demo app in Material style", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/nolimits4web/Weather7-Material.git" 8 | }, 9 | "author": "Vladimir Kharlampidi", 10 | "license": [ 11 | "MIT" 12 | ], 13 | "bugs": { 14 | "url": "https://github.com/nolimits4web/Weather7-Material/issues" 15 | }, 16 | "homepage": "http://www.idangero.us/framework7/", 17 | "engines": { 18 | "node": ">= 0.10.0" 19 | }, 20 | "devDependencies": { 21 | "gulp": "~3.9.0", 22 | "gulp-less": "~3.0.3", 23 | "gulp-jade": "~1.1.0", 24 | "gulp-connect": "~2.2.0", 25 | "gulp-open": "~1.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/jade/index.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(manifest="manifest.php") 3 | head 4 | meta(charset="utf-8") 5 | meta(name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui") 6 | meta(name='apple-mobile-web-app-capable', content='yes') 7 | meta(name="theme-color", content="#9c27b0") 8 | title Weather7 9 | link(href='http://fonts.googleapis.com/css?family=Roboto:400,300,500,700', rel='stylesheet', type='text/css') 10 | link(rel="stylesheet", href="libs/framework7/dist/css/framework7.material.min.css") 11 | link(rel="stylesheet", href="libs/framework7/dist/css/framework7.material.colors.min.css") 12 | link(rel="stylesheet", href="css/weather7.css") 13 | link(rel="icon", href="img/icon.png") 14 | body 15 | // Main views 16 | .views 17 | .view.view-main.navbar-fixed 18 | .pages 19 | .page(data-page="index") 20 | a(href="#").open-popup.link.floating-button.color-blue 21 | i.icon.icon-plus 22 | .navbar.theme-purple 23 | .navbar-inner 24 | .center Weather7 25 | .fake-navbar.bg-purple 26 | .page-content 27 | .list-block.places-list 28 | ul 29 | .list-block-label 30 | img(src="img/yahoo-logo.png", height="20") 31 | // Search popup 32 | .popup 33 | .view 34 | .pages 35 | .page.navbar-fixed 36 | .navbar 37 | .navbar-inner 38 | form.searchbar 39 | .searchbar-input 40 | input(type="search", placeholder="Search") 41 | a(href="#").searchbar-clear 42 | .preloader.preloader-white 43 | .page-content 44 | .list-block 45 | ul.search-results 46 | // Templates 47 | script(id="search-results-template", type="text/template") 48 | | {{#each this}} 49 | | {{#if admin1}}{{#if admin1.content}} 50 | li.close-popup(data-woeid="{{woeid}}", data-city="{{name}}", data-country="{{country.content}}") 51 | a(href="#").item-content.item-link 52 | .item-inner 53 | .item-title 54 | | {{name}} 55 | span {{admin1.content}}, {{country.content}} 56 | | {{/if}}{{/if}} 57 | | {{/each}} 58 | 59 | script(id="home-items-template", type="text/template") 60 | | {{#each this}} 61 | li.swipeout(data-woeid="{{woeid}}") 62 | .swipeout-content 63 | a(href="#", data-woeid="{{woeid}}").item-content.item-link 64 | .item-inner 65 | .item-title 66 | .city {{city}} 67 | .country {{country}} 68 | .item-after {{condition.temp}}° 69 | .swipeout-actions-right 70 | a(href="#").swipeout-delete 71 | i.icon.icon-delete 72 | | {{else}} 73 | li.item-content 74 | .item-inner 75 | .item-title You haven't added any places yet 76 | | {{/each}} 77 | script(id="details-template", type="text/template") 78 | .pages.navbar-fixed 79 | .page(data-page="detail", data-woeid="{{woeid}}") 80 | .navbar.theme-purple 81 | .navbar-inner 82 | .left 83 | a(href="index.html").back.link.icon-only 84 | i.icon.icon-back 85 | .center {{city}} 86 | .fake-navbar.bg-purple 87 | .page-content 88 | .detail-page-header 89 | .detail-page-header-overlay 90 | .detail-temp {{condition.temp}}° 91 | .detail-condition {{condition.text}} 92 | .list-block.forecast-list 93 | ul 94 | | {{#each forecast}} 95 | li.item-content 96 | .item-inner 97 | .item-title 98 | .day {{dayOfWeek date}} 99 | .date {{formatedDated date}} 100 | .item-after 101 | .state {{text}} 102 | .temps 103 | span.high {{high}}° 104 | span.low {{low}}° 105 | | {{/each}} 106 | 107 | // Path to Framework7 Library JS 108 | script(type="text/javascript", src="libs/framework7/dist/js/framework7.min.js") 109 | // Path to your app js 110 | script(type="text/javascript", src="js/weather7.js") 111 | -------------------------------------------------------------------------------- /src/less/weather7.less: -------------------------------------------------------------------------------- 1 | //Mixins 2 | .transition(@t) { 3 | -webkit-transition-duration: @t; 4 | -moz-transition-duration: @t; 5 | -ms-transition-duration: @t; 6 | -o-transition-duration: @t; 7 | transition-duration: @t; 8 | } 9 | .transform(@t) { 10 | -webkit-transform: @t; 11 | -moz-transform: @t; 12 | -ms-transform: @t; 13 | -o-transform: @t; 14 | transform: @t; 15 | } 16 | html,body { 17 | overflow: hidden; 18 | height: 100%; 19 | width: 100%; 20 | } 21 | 22 | 23 | 24 | /*============================== 25 | Homepage places list 26 | ==============================*/ 27 | .places-list { 28 | margin: 0; 29 | .item-after { 30 | font-size: 24px; 31 | max-height: none; 32 | color: rgba(0,0,0,0.54); 33 | } 34 | .city { 35 | font-size: 20px; 36 | // font-weight: 300; 37 | position: relative; 38 | overflow: hidden; 39 | max-width: 100%; 40 | text-overflow:ellipsis; 41 | white-space: nowrap; 42 | } 43 | .country { 44 | font-size: 14px; 45 | color: rgba(0,0,0,0.54); 46 | } 47 | .list-block-label { 48 | text-align: center; 49 | opacity: 0.6; 50 | } 51 | .item-link .item-inner { 52 | background: none; 53 | padding-right: 16px; 54 | } 55 | .icon-delete { 56 | width: 24px; 57 | height: 24px; 58 | background: url(../img/delete.svg); 59 | opacity: 0.54; 60 | } 61 | .swipeout-actions-right a.swipeout-delete { 62 | background: #d3d3d3; 63 | } 64 | .item-link-more { 65 | position: absolute; 66 | width: 24px; 67 | height: 24px; 68 | right: 16px; 69 | top: 50%; 70 | margin-top: -12px; 71 | text-align: right; 72 | } 73 | .list-block ul:empty:after { 74 | display: none; 75 | } 76 | } 77 | /*============================== 78 | Search popup styling 79 | ==============================*/ 80 | .popup { 81 | .searchbar-input { 82 | height: 38px; 83 | } 84 | .searchbar { 85 | height: 100%; 86 | } 87 | .list-block { 88 | margin: 0; 89 | span { 90 | font-size: 14px; 91 | color: rgba(0,0,0,0.54); 92 | margin-left: 5px; 93 | } 94 | .item-link .item-inner { 95 | background: none; 96 | } 97 | } 98 | .preloader { 99 | position: absolute; 100 | right: 40px; 101 | width: 24px; 102 | height: 24px; 103 | top: 50%; 104 | margin-top: -12px; 105 | opacity: 0.8; 106 | pointer-events: none; 107 | display: none; 108 | } 109 | } 110 | /*============================== 111 | Details page styles 112 | ==============================*/ 113 | .detail-page-header { 114 | background: #222; 115 | height: ~"-webkit-calc(100vw / 4 * 3)"; 116 | height: ~"calc(100vw / 4 * 3)"; 117 | max-height: ~"-webkit-calc(100vh - 56px)"; 118 | max-height: ~"calc(100vh - 56px)"; 119 | background-size: cover; 120 | background-position: center; 121 | position: relative; 122 | color:#fff; 123 | overflow: hidden; 124 | .detail-page-header-overlay { 125 | background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0) 50%); 126 | position: absolute; 127 | left: 0; 128 | top: 0; 129 | width: 100%; 130 | height: 100%; 131 | } 132 | } 133 | .detail-temp { 134 | font-size: 112px; 135 | line-height: 100px; 136 | font-weight: 300; 137 | position: absolute; 138 | left: 16px; 139 | bottom: 16px; 140 | } 141 | .detail-condition { 142 | text-align: center; 143 | position: absolute; 144 | right: 16px; 145 | bottom: 16px; 146 | font-size: 20px; 147 | font-weight: 300; 148 | z-index: 2; 149 | &:before { 150 | content: ''; 151 | left: 50%; 152 | top: 50%; 153 | width: 0%; 154 | height: 0%; 155 | border-radius: 100%; 156 | box-shadow: 0px 0px 50px 20px rgba(0,0,0,0.2); 157 | position: absolute; 158 | z-index: -1; 159 | } 160 | } 161 | 162 | .list-block.forecast-list { 163 | margin: 0; 164 | ul { 165 | &:before, &:after { 166 | display: none; 167 | } 168 | } 169 | span { 170 | display: inline-block; 171 | } 172 | .item-inner { 173 | padding-top: 16px; 174 | padding-bottom: 16px; 175 | } 176 | .item-after { 177 | display: block; 178 | text-align: right; 179 | color: rgba(0,0,0,0.87); 180 | height: auto; 181 | max-height: none; 182 | } 183 | .state { 184 | display: block; 185 | } 186 | .date { 187 | font-size: 14px; 188 | color: rgba(0,0,0,0.54); 189 | } 190 | .temps { 191 | span { 192 | width: 30px; 193 | } 194 | span.low { 195 | color: rgba(0,0,0,0.54); 196 | } 197 | } 198 | } 199 | 200 | /*============================== 201 | Tablet 202 | ==============================*/ 203 | @media (min-width:720px) { 204 | .views { 205 | .page { 206 | background: #eee; 207 | } 208 | .navbar { 209 | background-color: transparent !important; 210 | pointer-events: none; 211 | overflow: visible; 212 | .left { 213 | pointer-events: auto; 214 | } 215 | .center { 216 | display: block; 217 | max-width: ~"-webkit-calc(100% - 80% - 56px - 16px)"; 218 | max-width: ~"-calc(100% - 80% - 56px - 16px)"; 219 | } 220 | } 221 | .navbar-inner { 222 | overflow: visible; 223 | } 224 | .list-block { 225 | background: #fff; 226 | border-radius: 3px; 227 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 228 | ul { 229 | &:before { 230 | display: none; 231 | } 232 | } 233 | width: 60%; 234 | margin: 32px auto; 235 | &:first-child { 236 | margin-top: 0; 237 | } 238 | 239 | .list-block-label { 240 | margin: 0; 241 | padding: 16px 0; 242 | font-size: 0; 243 | } 244 | &.forecast-list { 245 | margin: 0 auto 32px; 246 | } 247 | } 248 | } 249 | 250 | .floating-button { 251 | left: 80%; 252 | margin-left: 32px; 253 | top: 48px + 56px - 56/2; 254 | } 255 | .fake-navbar { 256 | height: 56px + 48px; 257 | position: absolute; 258 | left: 0; 259 | top: 0; 260 | width: 100%; 261 | } 262 | .detail-page-header { 263 | width: 60%; 264 | height: ~"-webkit-calc(60vw / 16 * 9)"; 265 | height: ~"calc(60vw / 16 * 9)"; 266 | margin-left: auto; 267 | margin-right: auto; 268 | border-radius: 3px 3px 0 0; 269 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 270 | z-index: 1; 271 | max-height: none; 272 | + .list-block { 273 | border-radius: 0 0 3px 3px; 274 | z-index: 2; 275 | position: relative; 276 | } 277 | } 278 | 279 | } --------------------------------------------------------------------------------