From e83c2fc464e3cacdebbb2f108ad1f181ebe12dc7 Mon Sep 17 00:00:00 2001 From: Godwin Date: Tue, 13 Dec 2016 22:29:57 -0800 Subject: [PATCH] Fixed front page on mobile and improved city lookup --- Gemfile | 80 +-- Rakefile | 32 +- app/assets/javascripts/main.js | 520 ++++++++++-------- app/assets/stylesheets/_application.scss | 21 +- .../stylesheets/bumbleberry-settings.json | 2 +- app/models/city.rb | 71 ++- app/views/layouts/application.html.haml | 2 + config/initializers/geocoder.rb | 23 +- 8 files changed, 442 insertions(+), 309 deletions(-) diff --git a/Gemfile b/Gemfile index c3f591d..cdea679 100644 --- a/Gemfile +++ b/Gemfile @@ -11,9 +11,9 @@ gem 'haml' gem 'nokogiri', '~> 1.6.8.rc2' if Dir.exists?('../lingua_franca') - gem 'lingua_franca', path: '../lingua_franca' + gem 'lingua_franca', :path => '../lingua_franca' else - gem 'lingua_franca', git: 'git://github.com/lingua-franca/lingua_franca.git' + gem 'lingua_franca', :git => 'git://github.com/lingua-franca/lingua_franca.git' end gem 'tzinfo-data' @@ -21,15 +21,15 @@ gem 'sass' gem 'sass-rails' if Dir.exists?('../bumbleberry') - gem 'bumbleberry', path: "../bumbleberry" + gem 'bumbleberry', :path => "../bumbleberry" else - gem 'bumbleberry', git: 'git://github.com/bumbleberry/bumbleberry.git' + gem 'bumbleberry', :git => 'git://github.com/bumbleberry/bumbleberry.git' end if Dir.exists?('../paypal-express') - gem 'paypal-express', path: "../paypal-express" + gem 'paypal-express', :path => "../paypal-express" else - gem 'paypal-express', git: 'git://github.com/bikebike/paypal-express.git' + gem 'paypal-express', :git => 'git://github.com/bikebike/paypal-express.git' end gem 'uglifier', '>= 1.3.0' @@ -48,55 +48,55 @@ gem 'redcarpet' gem 'sidekiq' gem 'letter_opener' gem 'launchy' -gem 'to_spreadsheet', git: 'git://github.com/glebm/to_spreadsheet.git' +gem 'to_spreadsheet', :git => 'git://github.com/glebm/to_spreadsheet.git' group :test do - gem 'rspec' - gem 'rspec-rails' + gem 'rspec' + gem 'rspec-rails' end group :development do - gem 'better_errors' - gem 'binding_of_caller' - gem 'meta_request' - - gem 'capistrano', '~> 3.1' - gem 'capistrano-rails', '~> 1.1' - gem 'capistrano-faster-assets', '~> 1.0' - - gem 'eventmachine'#, :github => 'krzcho/eventmachine', :branch => 'master' - gem 'thin'#, :github => 'krzcho/thin', :branch => 'master' + gem 'better_errors' + gem 'binding_of_caller' + gem 'meta_request' + + gem 'capistrano', '~> 3.1' + gem 'capistrano-rails', '~> 1.1' + gem 'capistrano-faster-assets', '~> 1.0' + + gem 'eventmachine', :github => 'krzcho/eventmachine', :branch => 'master' + gem 'thin', :github => 'krzcho/thin', :branch => 'master' end group :test do - gem 'gherkin3', '>= 3.1.0' - gem 'cucumber' - gem 'cucumber-core' - gem 'cucumber-rails' - - gem 'poltergeist' - gem 'guard-rspec' - gem 'factory_girl_rails' - gem 'coveralls', require: false - gem 'selenium-webdriver' - gem 'simplecov', require: false - gem 'webmock', require: false - gem 'database_cleaner' - gem 'mocha' + gem 'gherkin3', '>= 3.1.0' + gem 'cucumber' + gem 'cucumber-core' + gem 'cucumber-rails' + + gem 'poltergeist' + gem 'guard-rspec' + gem 'factory_girl_rails' + gem 'coveralls', require: false + gem 'selenium-webdriver' + gem 'simplecov', require: false + gem 'webmock', require: false + gem 'database_cleaner' + gem 'mocha' end group :staging, :production, :preview do - gem 'rails_12factor' + gem 'rails_12factor' end group :production, :preview do - gem 'unicorn' - gem 'daemon-spawn' - gem 'daemons' + gem 'unicorn' + gem 'daemon-spawn' + gem 'daemons' end platforms 'mswin', 'mingw' do - group :test do - gem 'wdm', '>= 0.1.0' - end + group :test do + gem 'wdm', '>= 0.1.0' + end end diff --git a/Rakefile b/Rakefile index 9acaeca..b767df0 100644 --- a/Rakefile +++ b/Rakefile @@ -24,15 +24,25 @@ end task update_cities: :environment do Location.all.each do |l| - city = City.search(([l.city, l.territory, l.country] - [nil, '']).join(', ')) - l.city_id = city.id - l.save! + s = ([l.city, l.territory, l.country] - [nil, '']).join(', ') + unless l.city_id.present? + begin + puts "Searching for #{s}" + city = City.search(s) + l.city_id = city.id + l.save! + rescue + puts "Error searching for #{s}" + end + end end - City.all.each do |c| - location = Geocoder.search(c.address, language: 'en').first - c.place_id = location.data['place_id'] - c.save! + unless c.place_id.present? + City.all.each do |c| + location = Geocoder.search(c.address, language: 'en').first + c.place_id = location.data['place_id'] + c.save! + end end end @@ -43,3 +53,11 @@ task update_cities_es: :environment do c.save! end end + +task update_cities_fr: :environment do + City.all.each do |c| + city = c.get_translation(:fr) + c.set_column_for_locale(:city, :fr, city, 0) unless city.blank? || city == c.get_column_for_locale(:city, :fr) + c.save! + end +end diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index f2f0a4e..da2e8ce 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -1,244 +1,290 @@ (function() { - window.onerror = function(message, url, lineNumber) { - //save error and send to server for example. - var request = new XMLHttpRequest(); - request.open('POST', '/js_error', true); - request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); - request.send( - 'message=' + encodeURI(message) + - '&url=' + encodeURI(url) + - '&lineNumber=' + encodeURI(lineNumber) + - '&location=' + encodeURI(window.location.href) - ); - return false; - }; - window.forEach = function(a, f) { Array.prototype.forEach.call(a, f) }; - window.forEachElement = function(s, f, p) { forEach((p || document).querySelectorAll(s), f) }; - - var overlay = document.getElementById('content-overlay'); - if (overlay) { - var body = document.querySelector('body'); - var primaryContent = document.getElementById('primary-content'); - primaryContent.addEventListener('keydown', function(event) { - if (body.classList.contains('has-overlay')) { - event.stopPropagation(); - return false; - } - }); - document.addEventListener('focus', function(event) { - if (overlay.querySelector('.dlg.open') && !overlay.querySelector('.dlg.open :focus')) { - overlay.querySelector('.dlg.open').focus(); - } - }, true); - window.openOverlay = function(dlg, primaryContent, body) { - primaryContent.setAttribute('aria-hidden', 'true'); - body.classList.add('has-overlay'); - var type = dlg.getAttribute('data-type'); - if (type) { - body.classList.add('is-' + type + '-dlg'); - } - dlg.removeAttribute('aria-hidden'); - dlg.setAttribute('role', 'alertdialog'); - dlg.setAttribute('tabindex', '0'); - dlg.focus(); - setTimeout(function() { dlg.classList.add('open'); }, 100); - } - window.closeOverlay = function(dlg, primaryContent, body) { - setTimeout(function() { - body.classList.remove('has-overlay'); - body.removeAttribute('style'); - }, 250); - var type = dlg.getAttribute('data-type'); - if (type) { - body.classList.remove('is-' + type + '-dlg'); - } - primaryContent.removeAttribute('aria-hidden'); - dlg.setAttribute('aria-hidden', 'true'); - dlg.removeAttribute('tabindex'); - dlg.classList.remove('open'); - dlg.removeAttribute('role'); - } + window.onerror = function(message, url, lineNumber) { + //save error and send to server for example. + var request = new XMLHttpRequest(); + request.open('POST', '/js_error', true); + request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); + request.send( + 'message=' + encodeURI(message) + + '&url=' + encodeURI(url) + + '&lineNumber=' + encodeURI(lineNumber) + + '&location=' + encodeURI(window.location.href) + ); + return false; + }; + window.forEach = function(a, f) { Array.prototype.forEach.call(a, f) }; + window.forEachElement = function(s, f, p) { forEach((p || document).querySelectorAll(s), f) }; + + var overlay = document.getElementById('content-overlay'); + if (overlay) { + var body = document.querySelector('body'); + var primaryContent = document.getElementById('primary-content'); + primaryContent.addEventListener('keydown', function(event) { + if (body.classList.contains('has-overlay')) { + event.stopPropagation(); + return false; + } + }); + document.addEventListener('focus', function(event) { + if (overlay.querySelector('.dlg.open') && !overlay.querySelector('.dlg.open :focus')) { + overlay.querySelector('.dlg.open').focus(); + } + }, true); + window.openOverlay = function(dlg, primaryContent, body) { + primaryContent.setAttribute('aria-hidden', 'true'); + body.classList.add('has-overlay'); + var type = dlg.getAttribute('data-type'); + if (type) { + body.classList.add('is-' + type + '-dlg'); + } + dlg.removeAttribute('aria-hidden'); + dlg.setAttribute('role', 'alertdialog'); + dlg.setAttribute('tabindex', '0'); + dlg.focus(); + setTimeout(function() { dlg.classList.add('open'); }, 100); + } + window.closeOverlay = function(dlg, primaryContent, body) { + setTimeout(function() { + body.classList.remove('has-overlay'); + body.removeAttribute('style'); + }, 250); + var type = dlg.getAttribute('data-type'); + if (type) { + body.classList.remove('is-' + type + '-dlg'); + } + primaryContent.removeAttribute('aria-hidden'); + dlg.setAttribute('aria-hidden', 'true'); + dlg.removeAttribute('tabindex'); + dlg.classList.remove('open'); + dlg.removeAttribute('role'); + } - function openDlg(dlg, link) { - document.getElementById('overlay').onclick = - dlg.querySelector('.close').onclick = function() { closeDlg(dlg); }; - body.setAttribute('style', 'width: ' + body.clientWidth + 'px'); - var msg = link.querySelector('.message'); - if (msg) { - dlg.querySelector('.message').innerHTML = msg.innerHTML - } - if (link.dataset.infoTitle) { - dlg.querySelector('.title').innerHTML = decodeURI(link.dataset.infoTitle); - } - confirmBtn = dlg.querySelector('.confirm'); - if (confirmBtn) { - confirmBtn.addEventListener('click', function(event) { - event.preventDefault(); - if (link.tagName == 'BUTTON') { - var form = link.parentElement - while (form && form.tagName != 'FORM') { - var form = form.parentElement - } - if (form) { - var input = document.createElement('input'); - input.type = 'hidden'; - input.name = 'button'; - input.value = link.value; - form.appendChild(input); - form.submit(); - } - } else { - window.location.href = link.getAttribute('href'); - } - }); - } - window.openOverlay(dlg, primaryContent, body); - } - function closeDlg(dlg) { - window.closeOverlay(dlg, primaryContent, body); - } + function openDlg(dlg, link) { + document.getElementById('overlay').onclick = + dlg.querySelector('.close').onclick = function() { closeDlg(dlg); }; + body.setAttribute('style', 'width: ' + body.clientWidth + 'px'); + var msg = link.querySelector('.message'); + if (msg) { + dlg.querySelector('.message').innerHTML = msg.innerHTML + } + if (link.dataset.infoTitle) { + dlg.querySelector('.title').innerHTML = decodeURI(link.dataset.infoTitle); + } + confirmBtn = dlg.querySelector('.confirm'); + if (confirmBtn) { + confirmBtn.addEventListener('click', function(event) { + event.preventDefault(); + if (link.tagName == 'BUTTON') { + var form = link.parentElement + while (form && form.tagName != 'FORM') { + var form = form.parentElement + } + if (form) { + var input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'button'; + input.value = link.value; + form.appendChild(input); + form.submit(); + } + } else { + window.location.href = link.getAttribute('href'); + } + }); + } + window.openOverlay(dlg, primaryContent, body); + } + function closeDlg(dlg) { + window.closeOverlay(dlg, primaryContent, body); + } - var confirmationDlg = document.getElementById('confirmation-dlg'); - forEachElement('[data-confirmation]', function(link) { - link.addEventListener('click', function(event) { - event.preventDefault(); - openDlg(confirmationDlg, link); - return false; - }); - }); - var infoDlg = document.getElementById('info-dlg'); - forEachElement('[data-info-text]', function(link) { - link.addEventListener('click', function(event) { - event.preventDefault(); - openDlg(infoDlg, link); - return false; - }); - }); - var loginDlg = document.getElementById('login-dlg'); - forEachElement('[data-sign-in]', function(link) { - link.addEventListener('click', function(event) { - event.preventDefault(); - openDlg(loginDlg, link); - return false; - }); - }); - var contactDlg = document.getElementById('contact-dlg'); - var contactLink = document.getElementById('contact-link'); - contactLink.addEventListener('click', function(event) { - event.preventDefault(); - openDlg(contactDlg, contactLink); - return false; - }); - } - - var htmlNode = document.documentElement; - document.addEventListener('keydown', function(event) { - if (htmlNode.dataset.input != 'kb' && - ((["input", "textarea", "select", "option"].indexOf(event.target.nodeName.toLowerCase()) < 0 && - !event.target.attributes.contenteditable) || event.key == "Tab")) { - htmlNode.setAttribute('data-input', 'kb'); - } - }); - - document.addEventListener('mousemove', function(event) { - if (htmlNode.dataset.input != 'mouse' && (event.movementX || event.movementY)) { - htmlNode.setAttribute('data-input', 'mouse'); - } - }); + var confirmationDlg = document.getElementById('confirmation-dlg'); + forEachElement('[data-confirmation]', function(link) { + link.addEventListener('click', function(event) { + event.preventDefault(); + openDlg(confirmationDlg, link); + return false; + }); + }); + var infoDlg = document.getElementById('info-dlg'); + forEachElement('[data-info-text]', function(link) { + link.addEventListener('click', function(event) { + event.preventDefault(); + openDlg(infoDlg, link); + return false; + }); + }); + var loginDlg = document.getElementById('login-dlg'); + forEachElement('[data-sign-in]', function(link) { + link.addEventListener('click', function(event) { + event.preventDefault(); + openDlg(loginDlg, link); + return false; + }); + }); + var contactDlg = document.getElementById('contact-dlg'); + var contactLink = document.getElementById('contact-link'); + contactLink.addEventListener('click', function(event) { + event.preventDefault(); + openDlg(contactDlg, contactLink); + return false; + }); + } + + var htmlNode = document.documentElement; + document.addEventListener('keydown', function(event) { + if (htmlNode.dataset.input != 'kb' && + ((["input", "textarea", "select", "option"].indexOf(event.target.nodeName.toLowerCase()) < 0 && + !event.target.attributes.contenteditable) || event.key == "Tab")) { + htmlNode.setAttribute('data-input', 'kb'); + } + }); + + document.addEventListener('mousemove', function(event) { + if (htmlNode.dataset.input != 'mouse' && (event.movementX || event.movementY)) { + htmlNode.setAttribute('data-input', 'mouse'); + } + }); - var errorField = document.querySelector('.input-field.has-error input, .input-field.has-error textarea'); - if (errorField) { - errorField.focus(); - } - - window.initNodeFunctions = [ function(node) { - forEachElement('.number-field,.email-field,.text-field,.password-field,.search-field', function(field) { - var input = field.querySelector('input'); - var positionLabel = function(input) { - field.classList[input.value ? 'remove' : 'add']('empty'); - } - positionLabel(input); - input.addEventListener('keyup', function(event) { - positionLabel(event.target); - }); - input.addEventListener('blur', function(event) { - positionLabel(event.target); - field.classList.remove('focused'); - }); - input.addEventListener('focus', function(event) { - field.classList.add('focused'); - }); - }, node || document); - forEachElement('form.js-xhr', function(form) { - if (form.addEventListener) { - form.addEventListener('submit', function(event) { - event.preventDefault(); - form.classList.add('requesting'); - var data = new FormData(form); - var request = new XMLHttpRequest(); - request.onreadystatechange = function() { - if (request.readyState == 4) { - form.classList.remove('requesting'); - if (request.status == 200) { - var response = JSON.parse(request.responseText); - for (var i = 0; i < response.length; i++) { - var element; - if (response[i].selector) { - element = form.querySelector(response[i].selector); - } - if (response[i].globalSelector) { - element = document.querySelector(response[i].globalSelector); - } + var errorField = document.querySelector('.input-field.has-error input, .input-field.has-error textarea'); + if (errorField) { + errorField.focus(); + } + + window.initNodeFunctions = [ function(node) { + forEachElement('.number-field,.email-field,.text-field,.password-field,.search-field', function(field) { + var input = field.querySelector('input'); + var positionLabel = function(input) { + field.classList[input.value ? 'remove' : 'add']('empty'); + } + positionLabel(input); + input.addEventListener('keyup', function(event) { + positionLabel(event.target); + }); + input.addEventListener('blur', function(event) { + positionLabel(event.target); + field.classList.remove('focused'); + }); + input.addEventListener('focus', function(event) { + field.classList.add('focused'); + }); + }, node || document); + forEachElement('form.js-xhr', function(form) { + if (form.addEventListener) { + form.addEventListener('submit', function(event) { + event.preventDefault(); + form.classList.add('requesting'); + var data = new FormData(form); + var request = new XMLHttpRequest(); + request.onreadystatechange = function() { + if (request.readyState == 4) { + form.classList.remove('requesting'); + if (request.status == 200) { + var response = JSON.parse(request.responseText); + for (var i = 0; i < response.length; i++) { + var element; + if (response[i].selector) { + element = form.querySelector(response[i].selector); + } + if (response[i].globalSelector) { + element = document.querySelector(response[i].globalSelector); + } - if (response[i].html) { - element.innerHTML = response[i].html; - window.initNode(element); - } - if (response[i].className) { - element.className = response[i].className; - } - } - } - } - } - request.open('POST', form.getAttribute('action'), true); - request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - request.send(data); - }, false); - } - }, node || document); - forEachElement('[data-opens]', function(control) { - control.addEventListener('click', function(event) { - var opens = document.querySelector(control.getAttribute('data-opens')); - if (!opens.classList.contains('open')){ - event.preventDefault(); - opens.className += ' open'; - if (control.getAttribute('data-focus')) { - var input = opens.querySelector(control.getAttribute('data-focus')); - if (input) { - input.focus(); - } - } - return false; - } - }); - }); - } ]; - window.initNode = function(node) { - forEach(initNodeFunctions, function(fn) { - fn(node); - }); - }; - initNode(); - document.addEventListener('DOMContentLoaded', function() { - var errors = document.getElementsByClassName('has-error'); - if (errors.length <= 0) { - errors = document.getElementsByClassName('info-message'); - } - - if (errors.length > 0) { - errors[0].scrollIntoView(); - } - }); + if (response[i].html) { + element.innerHTML = response[i].html; + window.initNode(element); + } + if (response[i].className) { + element.className = response[i].className; + } + } + } + } + } + request.open('POST', form.getAttribute('action'), true); + request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + request.send(data); + }, false); + } + }, node || document); + forEachElement('[data-opens]', function(control) { + control.addEventListener('click', function(event) { + var opens = document.querySelector(control.getAttribute('data-opens')); + if (!opens.classList.contains('open')){ + event.preventDefault(); + opens.className += ' open'; + if (control.getAttribute('data-focus')) { + var input = opens.querySelector(control.getAttribute('data-focus')); + if (input) { + input.focus(); + } + } + return false; + } + }); + }); + } ]; + window.initNode = function(node) { + forEach(initNodeFunctions, function(fn) { + fn(node); + }); + }; + initNode(); + document.addEventListener('DOMContentLoaded', function() { + var errors = document.getElementsByClassName('has-error'); + if (errors.length <= 0) { + errors = document.getElementsByClassName('info-message'); + } + + if (errors.length > 0) { + errors[0].scrollIntoView(); + } + }); })(); +function generateScreenshot() { + var css = ''; + var svgs = document.getElementsByTagName('svg'); + for (var i = 0; i < document.styleSheets.length; i++) { + if (document.styleSheets[i].href && !document.styleSheets[i].href.match(/web\-fonts/)) { + var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules; + for (var j = 0; j < rules.length; j++) { + cssText = rules[j].cssText; + if (rules[j].selectorText) { + if (rules[j].selectorText.match(/(^|\s)svg[^\w]/) || cssText.match(/\s(fill|stroke(\-width)?):/)) { + css += cssText; + } + } + } + } + } + for (var i = 0; i < svgs.length; i++) { + var svg = svgs[i]; + // svg.innerHTML = '' + svg.innerHTML; + svg.innerHTML = '' + svg.innerHTML; + svg.setAttribute('height', svg.clientHeight); + svg.setAttribute('width', svg.clientWidth); + var canvas = document.createElement('canvas'); + canvg(canvas, svg.outerHTML); + console.log(svg.outerHTML); + svg.style.backgroundImage = 'url(' + encodeURI(canvas.toDataURL('image/png')) + ')'; + } + + html2canvas(document.body, { + logging: true, + profile: true, + useCORS: true}).then(function(canvas) { + var data = canvas.toDataURL('image/jpeg', 0.9); + var src = encodeURI(data); + + window.open(src, '_blank'); + + // reset the svg height and width + for (var i = 0; i < svgs.length; i++) { + var svg = svgs[i]; + svg.removeAttribute('height'); + svg.removeAttribute('width'); + svg.removeAttribute('style'); + } + }); +} diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss index 4cf5e58..68cb909 100644 --- a/app/assets/stylesheets/_application.scss +++ b/app/assets/stylesheets/_application.scss @@ -12,6 +12,7 @@ html, body { } body { + background-color: $white; padding-bottom: 20vw; } @@ -1807,6 +1808,7 @@ ul.warnings { a { color: inherit; padding: 0 0.333em; + display: inline-block; &:hover { @include after { @@ -2946,6 +2948,9 @@ a.logo { .conference-banner { text-align: center; + padding: 0; + margin: 0 -1em 2em; + width: auto; .title { font-size: 5vw; @@ -2964,6 +2969,10 @@ a.logo { font-size: 0.85em; line-height: 2em; } + + img { + max-width: 100%; + } } .conference-details { @@ -4653,15 +4662,6 @@ html[data-ontop] { } } } - - // .nav a.register, .nav button { - // @include before-and-after { - // background-color: $colour-2; - // } - // &:focus, &:active { - // @include _(transform, none); - // } - // } } #banner { @@ -4674,6 +4674,9 @@ html[data-ontop] { } .conference-banner { + margin: 0 auto; + width: 100%; + .title { font-size: 1.9em; margin: 0 0 1em; diff --git a/app/assets/stylesheets/bumbleberry-settings.json b/app/assets/stylesheets/bumbleberry-settings.json index b434909..c0dab74 100644 --- a/app/assets/stylesheets/bumbleberry-settings.json +++ b/app/assets/stylesheets/bumbleberry-settings.json @@ -8,7 +8,7 @@ "and_chr": ["54"], "chrome": ["54"], "edge": ["13"], - "firefox": ["44"], + "firefox": ["48"], "ie": ["11"], "ios_saf": ["8", "9"] } diff --git a/app/models/city.rb b/app/models/city.rb index d18d47b..154f004 100644 --- a/app/models/city.rb +++ b/app/models/city.rb @@ -18,19 +18,30 @@ class City < ActiveRecord::Base def get_translation(locale) location = Geocoder.search(address, language: locale.to_s).first + # if the service lets us down, return nil + return nil unless location.present? + + searched_component = false location.data['address_components'].each do | component | - # city is usually labelled a 'locality' but sometimes this is missing and only 'colloquial_area' is present - if component['types'].first == 'locality' || component['types'].first == 'colloquial_area' + # city is usually labeled a 'locality' but sometimes this is missing and only 'colloquial_area' is present + if component['types'].first == 'locality' return component['short_name'] end + + if component['types'] == location.data['types'] + searched_component = component['short_name'] + end end - return nil + # return the type we searched for but it's still possible that it will be false + searched_component end + # this method will get called automatically if a translation is asked for but not found def translate_city(locale) translation = get_translation(locale) + # if we found it, set it if translation.present? set_column_for_locale(:city, locale, translation) save! @@ -48,16 +59,20 @@ class City < ActiveRecord::Base # look up the city in the geocoder location = Geocoder.search(str, language: 'en').first + # return nil to indicate that the service is down + return nil unless location.present? # see if the city is already present in our database city = City.find_by_place_id(location.data['place_id']) # return the city if we found it in the db already - return city if city.present? + if city.present? + CityCache.create(city_id: city.id, search: str) + return city + end # otherwise build a new city component_alises = { 'locality' => :city, - 'colloquial_area' => :city, 'administrative_area_level_1' => :territory, 'country' => :country } @@ -67,15 +82,61 @@ class City < ActiveRecord::Base longitude: location.data['geometry']['location']['lng'], place_id: location.data['place_id'] } + + # these things are definitely not cities, make sure we don't think they're one + not_a_city = [ + 'administrative_area_level_1', + 'country', + 'street_address', + 'street_number', + 'postal_code', + 'postal_code_prefix', + 'route', + 'intersection', + 'premise', + 'subpremise', + 'natural_feature', + 'airport', + 'park', + 'point_of_interest', + 'bus_station', + 'train_station', + 'transit_station', + 'room', + 'post_box', + 'parking', + 'establishment', + 'floor' + ] + + searched_component = nil location.data['address_components'].each do | component | property = component_alises[component['types'].first] city_data[property] = component['short_name'] if property.present? + + # ideally we will find the component that is labeled a locality but + # if that fails we will select what was searched for, hopefully they searched for a city + # and not an address or country + # some places are not labeled 'locality', search for 'Halifax NS' for example and you will + # get 'administrative_area_level_2' since Halifax is a municipality + if component['types'] == location.data['types'] && not_a_city.include?(component['types'].first) + searched_component = component['short_name'] + end end + # fall back to the searched component + city_data[:city] ||= searched_component + + # we need to have the city and country at least + return false unless city_data[:city].present? && city_data[:country].present? + # save the new city city = City.new(city_data) city.save! + # save this to our cache + CityCache.create(city_id: city.id, search: str) + # and return it return city end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index f1d5a0f..a06b9c0 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -20,6 +20,8 @@ %meta{property: 'og:image', content: og_image} %meta{name: "theme-color", content: @theme_colour} = yield :head + = javascript_include_tag 'https://rawgit.com/niklasvh/html2canvas/master/dist/html2canvas.js' + = javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/amcharts/3.13.0/exporting/canvg.js' %body{ class: page_style } #primary-content diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb index 49e4b0a..b34ca4a 100644 --- a/config/initializers/geocoder.rb +++ b/config/initializers/geocoder.rb @@ -1,21 +1,24 @@ # config/initializers/geocoder.rb -Geocoder.configure( +config = { # geocoding service (see below for supported options): - :lookup => :google, + lookup: :google, # IP address geocoding service (see below for supported options): - :ip_lookup => :freegeoip, + ip_lookup: :freegeoip, # to use an API key: - # :api_key => "AIzaSyDitM1lyVWkrumteDvSkje6GiIKYyHlAXM", # geocoding service request timeout, in seconds (default 3): - :timeout => 5, + timeout: 5, # set default units to kilometers: - :units => :km, + units: :km +} - # caching (see below for details): - #:cache => Redis.new, - #:cache_prefix => "..." -) +# use our api key on the server +if Rails.env.preview? || Rails.env.production? + config[:api_key] = "AIzaSyDurfjX9f_NgYsJLyUuGqwdKuI745CE_OE" + config[:use_https] = true +end + +Geocoder.configure(config)