From: John Firebaugh Date: Tue, 19 Nov 2013 17:42:47 +0000 (-0800) Subject: Merge branch 'master' into redesign X-Git-Tag: live~4685^2~40 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/44629832dd0207d7b0f50b93f1d00b4373dd4d49?hp=fc2351557096588c3763b21544996f7d9c3dae53 Merge branch 'master' into redesign Conflicts: vendor/assets/leaflet/leaflet.hash.js vendor/assets/leaflet/leaflet.js --- diff --git a/Vendorfile b/Vendorfile index 136b47f72..2d728ab1e 100644 --- a/Vendorfile +++ b/Vendorfile @@ -9,14 +9,13 @@ folder 'vendor/assets' do end folder 'leaflet' do - file 'leaflet.js', 'http://cdn.leafletjs.com/leaflet-0.6.3/leaflet-src.js' - file 'leaflet.css', 'http://cdn.leafletjs.com/leaflet-0.6.3/leaflet.css' - file 'leaflet.ie.css', 'http://cdn.leafletjs.com/leaflet-0.6.3/leaflet.ie.css' + file 'leaflet.js', 'http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js' + file 'leaflet.css', 'http://cdn.leafletjs.com/leaflet-0.7/leaflet.css' [ 'layers.png', 'layers-2x.png', 'marker-icon.png', 'marker-icon-2x.png', 'marker-shadow.png' ].each do |image| - file "images/#{image}", "http://cdn.leafletjs.com/leaflet-0.6.3/images/#{image}" + file "images/#{image}", "http://cdn.leafletjs.com/leaflet-0.7/images/#{image}" end from 'git://github.com/kajic/leaflet-locationfilter.git' do diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 97ab5abfc..67e25c6a7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,11 +9,7 @@ class ApplicationController < ActionController::Base if session[:user] @user = User.where(:id => session[:user]).where("status IN ('active', 'confirmed', 'suspended')").first - if @user.display_name != cookies["_osm_username"] - logger.info "Session user '#{@user.display_name}' does not match cookie user '#{cookies['_osm_username']}'" - reset_session - @user = nil - elsif @user.status == "suspended" + if @user.status == "suspended" session.delete(:user) session_expires_automatically diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index a9006e82b..99777ca2d 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -7,6 +7,7 @@ class UserController < ApplicationController before_filter :authorize_web, :except => [:api_read, :api_details, :api_gpx_files] before_filter :set_locale, :except => [:api_read, :api_details, :api_gpx_files] before_filter :require_user, :only => [:account, :go_public, :make_friend, :remove_friend] + before_filter :require_self, :only => [:account] before_filter :check_database_readable, :except => [:login, :api_read, :api_details, :api_gpx_files] before_filter :check_database_writable, :only => [:new, :account, :confirm, :confirm_email, :lost_password, :reset_password, :go_public, :make_friend, :remove_friend] before_filter :check_api_readable, :only => [:api_read, :api_details, :api_gpx_files] @@ -338,7 +339,6 @@ class UserController < ApplicationController token.destroy session[:user] = user.id - cookies.permanent["_osm_username"] = user.display_name redirect_to referer || welcome_path end @@ -377,7 +377,6 @@ class UserController < ApplicationController end token.destroy session[:user] = @user.id - cookies.permanent["_osm_username"] = @user.display_name redirect_to :action => 'account', :display_name => @user.display_name else flash[:error] = t 'user.confirm_email.failure' @@ -638,8 +637,6 @@ private ## # process a successful login def successful_login(user) - cookies.permanent["_osm_username"] = user.display_name - session[:user] = user.id session_expires_after 28.days if session[:remember_me] @@ -727,8 +724,6 @@ private if user.save set_locale - cookies.permanent["_osm_username"] = user.display_name - if user.new_email.blank? or user.new_email == user.email flash.now[:notice] = t 'user.account.flash update success' else @@ -769,6 +764,14 @@ private end end + ## + # require that the user in the URL is the logged in user + def require_self + if params[:display_name] != @user.display_name + render :text => "", :status => :forbidden + end + end + ## # ensure that there is a "this_user" instance variable def lookup_user_by_id diff --git a/app/views/export/embed.html.erb b/app/views/export/embed.html.erb index b683c2665..6c3ebdd16 100644 --- a/app/views/export/embed.html.erb +++ b/app/views/export/embed.html.erb @@ -4,9 +4,6 @@ OpenStreetMap Embedded <%= stylesheet_link_tag "embed", :media=> "screen" %> - <%= javascript_include_tag "embed" %> diff --git a/app/views/layouts/_head.html.erb b/app/views/layouts/_head.html.erb index 9951737bc..1897cfff3 100644 --- a/app/views/layouts/_head.html.erb +++ b/app/views/layouts/_head.html.erb @@ -8,7 +8,6 @@ <%= stylesheet_link_tag "print-#{dir}", :media => "print" %> <%= stylesheet_link_tag "leaflet-all", :media => "screen, print" %> <%= favicon_link_tag "favicon.ico" %> diff --git a/config/locales/de.yml b/config/locales/de.yml index 7647ac082..5e0662e9f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -11,6 +11,7 @@ # Author: CygnusOlor # Author: Daswaldhorn # Author: Diebuche +# Author: Dieterdreist # Author: Fujnky # Author: Grille chompa # Author: Holger @@ -2067,7 +2068,7 @@ de: questions: paragraph_1_html: "Falls Du zusätzliche Hilfe beim Mapping brauchst oder etwas nicht klar ist, gibt es zusätzliche\nHilfe auf der Hilfe-Seite." title: Fragen? - start_mapping: Beginne mit dem Berarbeiten der Karte + start_mapping: Beginne mit dem Bearbeiten der Karte title: Willkommen! whats_on_the_map: off_html: "Was nicht in die Datenbank gehört sind subjektive Daten wie Bewertungen, nicht mehr aktuelle\nhistorische Fakten oder nicht real existierende Dinge. Ausserdem ist es wichtig, nichts von\nanderen Quellen (auch nicht aus Papierkarten) zu kopieren, da OpenStreetMap das Urheberrecht sehr ernst nimmt." diff --git a/config/locales/diq.yml b/config/locales/diq.yml index c5b6ed397..1f5db228d 100644 --- a/config/locales/diq.yml +++ b/config/locales/diq.yml @@ -3,6 +3,7 @@ # Export driver: syck-pecl # Author: Erdemaslancan # Author: Gorizon +# Author: Marmase # Author: Mirzali diq: activerecord: @@ -572,7 +573,7 @@ diq: community_blogs: Blogê Cemaeti documentation: Dokumentasyon edit: Timar ke - help: Peşti + help: Desteg history: Ravêrden intro_1: OpenStreetMap yew xeritey dınyayo u merduma hemın rê belaso. intro_2_download: ronayış @@ -694,6 +695,7 @@ diq: where_am_i: Edo kotideya? sidebar: close: Racnê + search_results: Neticeya geyrayışi time: formats: friendly: "%e %B %Y seate %H:%M" @@ -730,7 +732,7 @@ diq: trackable: SEYRKERDENEN view_map: Xeriti Bımocnê trace_form: - help: Peşti + help: Desteg tags: "Etiketi:" upload_button: Bar ke visibility: Vinayîşî diff --git a/config/locales/el.yml b/config/locales/el.yml index 2bcb0dd9a..845de71f3 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -51,7 +51,7 @@ el: languages: Γλώσσες pass_crypt: Κωδικός models: - acl: Πρόσβαση στη λίστα ελέγχου + acl: Πρόσβαση στη Λίστα Ελέγχου changeset: Ομάδα Αλλαγών changeset_tag: Ετικέτα Ομάδας Αλλαγών country: Χώρα @@ -140,7 +140,7 @@ el: note: Προβολή της σημείωσης σε μεγαλύτερο χάρτη. relation: Προβολή της σχέσης σε μεγαλύτερο χάρτη way: Προβολή της διαδρομής σε μεγαλύτερο χάρτη. - loading: Φόρτωση... + loading: Φόρτωση σε εξέλιξη... navigation: all: next_changeset_tooltip: Επόμενη ομάδα αλλαγών @@ -213,11 +213,11 @@ el: view_history: Προβολή ιστορικού relation_details: members: "Μέλη:" - part_of: "Μέρος του:" + part_of: "Μέλος του:" relation_history: download_xml: Λήψη XML relation_history: Ιστορικό Σχέσης - relation_history_title: "Ιστορικό σχέσης: %{relation_name}" + relation_history_title: "Ιστορικό Σχέσης: %{relation_name}" view_details: Προβολή λεπτομερειών relation_member: entry: "%{type} %{name}" @@ -235,9 +235,9 @@ el: history_for_feature: Ιστορικό για %{feature} load_data: Φόρτωση δεδομένων loaded_an_area_with_num_features: "Έχετε φορτώσει μια περιοχή που περιέχει %{num_features} χαρακτηριστικά. Γενικά, μερικοί browsers μπορεί να μην αντέχουν να δείξουν τόσα πολλά στοιχεία. Γενικά, οι browsers δουλεύουν καλύτερα δείχνοντας λιγότερα από %{max_features} χαρακτηριστικά τη φορά: με οτιδήποτε άλλο ο browser μπορεί να γίνει αργός ή να μην αντιδρά. Αν είστε σίγουρος ότι θέλετε να δείτε αυτά τα δεδομένα, κάντε κλικ στο παρακάτω κουμπί." - loading: Φόρτωση... + loading: Φόρτωση σε εξέλιξη... manually_select: Χειροκίνητη επιλογή διαφορετικής περιοχής - notes_layer_name: Περιήγηση στις Σημειώσεις + notes_layer_name: Περιήγηση Σημειώσεων object_list: api: Ανάκτηση περιοχής από το API back: Πίσω στον κατάλογο αντικειμένων @@ -256,11 +256,11 @@ el: way: Διαδρομή private_user: ιδιωτικός χρήστης show_areas: Εμφάνιση περιοχών - show_history: Προβολή ιστορικού + show_history: Προβολή Ιστορικού unable_to_load_size: "Δεν είναι δυνατή η φόρτωση: το μέγεθος %{bbox_size} του πλαισίου οριοθέτησης είναι πολύ μεγάλο (πρέπει να είναι μικρότερο από %{max_bbox_size})" - view_data: Προβολή δεδομένων στην τρέχουσα προβολή του χάρτη + view_data: Δείτε δεδομένα σχετικά με την τρέχουσα προβολή του χάρτη wait: Αναμονή... - zoom_or_select: Μεγέθυνση ή επιλογή περιοχής του χάρτη προς εμφάνιση + zoom_or_select: Μεγεθυνθείτε ή επιλέξτε την περιοχή του χάρτη προς εμφάνιση tag_details: tags: "Ετικέτες:" wiki_link: @@ -493,9 +493,11 @@ el: search: title: ca_postcode: Αποτελέσματα από Geocoder.CA - geonames: Αποτελέσματα από GeoNames - latlon: Εσωτερικά αποτελέσματα - osm_nominatim: Αποτελέσματα από OpenStreetMap Nominatim + geonames: Αποτελέσματα από το GeoNames + geonames_reverse: Αποτελέσματα από το GeoNames + latlon: Αποτελέσματα από την εσωτερική βάση δεδομένων + osm_nominatim: Αποτελέσματα από το OpenStreetMap Nominatim + osm_nominatim_reverse: Αποτελέσματα από το OpenStreetMap Nominatim uk_postcode: Αποτέλεσμα από NPEMap / FreeThe Postcode us_postcode: Αποτελέσματα από Geocoder.us search_osm_nominatim: @@ -1081,7 +1083,7 @@ el: alt_text: Λογότυπο OpenStreetMap logout: Αποσύνδεση make_a_donation: - text: Κάντε μια Δωρεά + text: Στηρίξτε μας title: Υποστήριξε το OpenStreetMap με δωρεά χρημάτων osm_offline: Η βάση δεδομένων του OpenStreetMap είναι προσωρινά εκτός λειτουργίας λόγω εργασιών συντήρησης της βάσης δεδομένων. osm_read_only: Η βάση δεδομένων του OpenStreetMap είναι προσωρινά σε λειτουργία "μόνο για ανάγνωση" λόγω εργασιών συντήρησης της βάσης δεδομένων. @@ -1453,6 +1455,7 @@ el: track: Χωματόδρομος tram: 1: τραμ + trunk: Αυτοκινητόδρομος tunnel: Διακεκομμένο περίβλημα = σήραγγα unclassified: Αταξινόμητη Οδός unsurfaced: Δρόμος χωρίς Επίστρωση diff --git a/config/locales/et.yml b/config/locales/et.yml index a0e2c17c2..f6d045da7 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -1026,7 +1026,7 @@ et: confirm: "Enne kasutajakonto loomist peame saama kinnituse, et see taotlus tuli sinult ja selleks klõpsa alloleval lingil:" created: Keegi (loodetavasti sina) lõi äsja %{site_url} kasutajakonto. greeting: Tere! - subject: "[OpenStreetMap] Tere tulemast OpenStreetMap-i" + subject: "[OpenStreetMap] Tere tulemast OpenStreetMap'i" welcome: Peale seda, kui oled kinnitanud enda konto pakume sulle lisainfot, et saaksid kasutamist hõlpsalt alustada. oauth: oauthorize: @@ -1352,16 +1352,22 @@ et: failed: Vabandust, kasutaja %{name} sõbraks lisamine ebaõnnestus. success: "%{name} on nüüd sinu sõber." new: + about: + header: Vaba ja muudetav + html: "

Erinevalt teistest kaartidest on OpenStreetMap loodud samade inimeste poolt nagu sina ja igaüks võib seda parandada, täiendada, alla laadida ja kasutada.

\n

Registreeru, et alustada kaardi täiendamist. Me saadama sulle meili, et saaksid kinnitada oma kasutajakonto.

" confirm email address: "Kinnita e-posti aadress:" confirm password: "Kinnita parool:" continue: Registreeru display name: "Kuvatav nimi:" display name description: Avalikult kuvatud kasutajanimi. Seda saate muuta hiljem eelistustes. email address: "E-posti aadress:" + license_agreement: Oma kasutajakonto kinnitamiseks pead nõustuma kaastöö tingimustega. not displayed publicly: Ei näidata avalikult (vaata privaatsusreegleid) openid: "%{logo} OpenID:" openid no password: OpenID kasutamisel ei ole parool kohustuslik, kuid mõned eritööriistad või serverid nõuavad seda siiski. password: "Uus parool:" + terms accepted: Täname, et nõustusid uute kaastöö tingimustega! + terms declined: Meil on kahju, et otsustasid mitte nõustuda uute kaastöö tingimustega. Lisateabe saamiseks vaata seda wiki lehte. title: Registreerumine use openid: Teise võimalusena kasuta %{logo} OpenID sisselogimist no_such_user: @@ -1481,3 +1487,6 @@ et: confirm: Kinnita revoke: confirm: Kinnita + welcome_page: + introduction_html: Tere tulemast OpenStreetMap'i, vabasse ja muudetavasse maailmakaarti. Registreerumine on nüüd tehtud ja sa võid alustada kaardistamist. Siin on lühiülevaade kõige olulisematest asjadest, mida peaksid teadma. + title: Tere tulemast! diff --git a/config/locales/fi.yml b/config/locales/fi.yml index b527352cd..43f0a94d2 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -2042,7 +2042,7 @@ fi: tag_html: Tagi sisältää tietoja, joilla kerrotaan pisteen, viivan tai alueen käyttötarkoitus. Tällaisia tietoja on esimerkiksi ravintolan nimi tai kadun nopeusrajoitus. title: Käsitteitä ja termistöä way_html: Avoin viiva (engl. way) on viivamainen kohde, kuten katu tai joki. Suljettu viiva eli alue (engl. area) kuvaa aluetta, kuten järveä tai rakennusta. - introduction_html: Tervetuloa OpenStreetMapiin, ilmaiseen ja vapaasti muokattavaan maailmankarttaan. Rekisteröityminen on nyt tehty, joten voit eiköhän aloiteta kartan muokkaaminen. Tutustu ensin kuitenkin näihin perusasioihin. + introduction_html: Tervetuloa OpenStreetMapiin, ilmaiseen ja vapaasti muokattavaan maailmankarttaan. Rekisteröityminen on nyt tehty, joten olet valmis aloittamaan kartan muokkaamisen. Tutustu ensin kuitenkin näihin perusasioihin. questions: paragraph_1_html: Tarvitsetko apua OpenStreetMapin käytössä tai muokkaamisessa? Tutustu OpenStreetMapin ohjekeskukseen. title: Kysyttävää? @@ -2050,5 +2050,5 @@ fi: title: Tervetuloa! whats_on_the_map: off_html: Se ei sisällä mielipiteitä, kuten arvioita, historiallisia tai hypoteettisia ominaisuuksia, eikä tietoja tekijänoikeuksin suojatuista lähteistä. Ellei sinulla ole erityistä lupaa, älä kopioi verkko- tai paperikarttoja. - on_html: OpenStreetMap on paikka kartoittaa asioita, jotka ovat sekä todellisia että nykyisiä - se sisältää miljoonia rakennuksia, teitä ja muita yksityiskohtia paikoista. Voit kartoittaa mitä tahansa reaalimaailman piirteitä, jotka sinua kiinnostavat. + on_html: OpenStreetMapissä voi kartoittaa asioita, jotka ovat sekä todellisia että nykyisiä - se sisältää miljoonia rakennuksia, teitä ja muita yksityiskohtia paikoista. Voit kartoittaa mitä tahansa reaalimaailman piirteitä, jotka sinua kiinnostavat. title: Mitä on kartalla diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 38c80df20..cf37b41f9 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -426,8 +426,20 @@ ia: paste_html: Colla HTML pro incorporar in sito web scale: Scala too_large: - body: Iste area es troppo grande pro esser exportate como datos XML de OpenStreetMap. Per favor face zoom avante o selige un area minor. + body: "Iste area es troppo grande pro esser exportate como datos XML de OpenStreetMap. Per favor face zoom avante, o selige un area minor, o usa un del sequente fontes pro discargamento de datos in massa:" + geofabrik: + description: Extractos regularmente actualisate de continentes, paises, e citates seligite + title: Discargamentos de Geofabrik heading: Area troppo grande + metro: + description: Extractos de metropoles e lor areas circumvicin + title: Extractos de Metro + other: + description: Fontes additional listate in le wiki de OpenStreetMap + title: Altere fontes + planet: + description: Copias regularmente actualisate del base de datos complete de OpenStreetMap + title: Planeta OSM zoom: Zoom start_rjs: add_marker: Adder un marcator al carta @@ -465,8 +477,10 @@ ia: title: ca_postcode: Resultatos de Geocoder.CA geonames: Resultatos de GeoNames + geonames_reverse: Resultatos ab GeoNames latlon: Resultatos interne osm_nominatim: Resultatos de OpenStreetMap Nominatim + osm_nominatim_reverse: Resultatos ab OpenStreetMap Nominatim uk_postcode: Resultatos de NPEMap / FreeThe Postcode us_postcode: Resultatos de Geocoder.us search_osm_nominatim: @@ -479,6 +493,10 @@ ia: level8: Limite de citate level9: Limite de village prefix: + aerialway: + chair_lift: Telesedia + drag_lift: Teleski + station: Station de telecabina aeroway: aerodrome: Aerodromo apron: Pista @@ -572,6 +590,7 @@ ia: shower: Ducha social_centre: Centro social social_club: Club social + social_facility: Servicio social studio: Appartamento de un camera supermarket: Supermercato swimming_pool: Piscina @@ -600,6 +619,9 @@ ia: "yes": Ponte building: "yes": Edificio + emergency: + fire_hydrant: Hydrante de incendio + phone: Telephono de emergentia highway: bridleway: Sentiero pro cavallos bus_guideway: Via guidate de autobus @@ -621,6 +643,7 @@ ia: platform: Platteforma primary: Via principal primary_link: Via principal + proposed: Strata proponite raceway: Circuito residential: Residential rest_area: Area de reposo @@ -632,6 +655,7 @@ ia: speed_camera: Detector de velocitate steps: Scalones stile: Scalon o apertura de passage + street_lamp: Lanterna de strata tertiary: Via tertiari tertiary_link: Via tertiari track: Pista @@ -647,6 +671,7 @@ ia: building: Edificio castle: Castello church: Ecclesia + citywalls: Muro del citate fort: Forte house: Casa icon: Icone @@ -656,6 +681,7 @@ ia: monument: Monumento museum: Museo ruins: Ruinas + tomb: Tumba tower: Turre wayside_cross: Cruce juxta le via wayside_shrine: Reliquario juxta le via @@ -794,6 +820,7 @@ ia: locality: Localitate moor: Landa municipality: Municipalitate + neighbourhood: Quartiero postcode: Codice postal region: Region sea: Mar @@ -819,8 +846,10 @@ ia: narrow_gauge: Ferrovia stricte platform: Platteforma ferroviari preserved: Ferrovia preservate + proposed: Ferrovia proponite spur: Ramification de ferrovia station: Station ferroviari + stop: Halto ferroviari subway: Station de metro subway_entrance: Entrata al metro switch: Agulia @@ -836,6 +865,7 @@ ia: beverages: Boteca de bibitas bicycle: Magazin de bicyclettas books: Libreria + boutique: Boutique butcher: Macelleria car: Magazin de automobiles car_parts: Partes de automobiles @@ -849,6 +879,7 @@ ia: convenience: Magazin de quartiero copyshop: Centro de photocopias cosmetics: Boteca de cosmetica + deli: Boteca de delicatessas fin department_store: Grande magazin discount: Boteca de disconto doityourself: Magazin de bricolage @@ -885,17 +916,21 @@ ia: organic: Boteca de alimentos organic outdoor: Magazin de sport al aere libere pet: Boteca de animales + pharmacy: Pharmacia photo: Magazin de photographia salon: Salon + second_hand: Magazin de secunde mano shoes: Scarperia shopping_centre: Centro commercial sports: Magazin de sport stationery: Papireria supermarket: Supermercato + tailor: Sartor toys: Magazin de joculos travel_agency: Agentia de viages video: Magazin de video wine: Magazin de vinos + "yes": Boteca tourism: alpine_hut: Cabana alpin artwork: Obra de arte @@ -918,6 +953,7 @@ ia: viewpoint: Puncto de vista zoo: Jardin zoologic tunnel: + culvert: Tubo de aqua subterranee "yes": Tunnel waterway: artificial: Via aquatic artificial @@ -980,12 +1016,14 @@ ia: community_blogs: Blogs del communitate community_blogs_title: Blogs de membros del communitate de OpenStreetMap copyright: Copyright & Licentia + data: Datos documentation: Documentation documentation_title: Documentation pro le projecto donate: Supporta OpenStreetMap per %{link} al Fundo de Actualisation de Hardware. donate_link_text: donation edit: Modificar edit_with: Modificar con %{editor} + export_data: Exportar datos foundation: Fundation foundation_title: Le fundation OpenStreetMap gps_traces: Tracias GPS @@ -994,7 +1032,7 @@ ia: help_centre: Centro de adjuta help_title: Sito de adjuta pro le projecto history: Historia - home: initio + home: Vader al position de initio intro_1: OpenStreetMap es un carta libere e modificabile del mundo integre. Illo es facite per gente como tu. intro_2_create_account: Crea un conto de usator intro_2_download: discargar @@ -1006,7 +1044,7 @@ ia: log_in_tooltip: Aperir un session con un conto existente logo: alt_text: Logo de OpenStreetMap - logout: clauder session + logout: Clauder session make_a_donation: text: Facer un donation title: Supporta OpenStreetMap con un donation monetari @@ -1924,3 +1962,19 @@ ia: fail: Impossibile revocar le rolo `%{role}' del usator `%{name}'. Verifica que e le usator e le rolo es valide. heading: Confirmar le revocation del rolo title: Confirmar le revocation del rolo + welcome_page: + basic_terms: + editor_html: Un editor es un programma o sito web que tu pote usar pro modificar le carta. + node_html: Un nodo es un puncto sur le carta, como un restaurante o un arbore individual. + paragraph_1_html: OpenStreetMap ha un jargon proprie. Ecce alcun parolas clave que te essera utile. + tag_html: Un etiquetta es un unitate de information sur un nodo o via, como le nomine de un restaurante o le limite de velocitate de un strata. + title: Vocabulario de base pro le cartographia + way_html: Un via es un linea o area, como un strata, fluxo, laco o edificio. + introduction_html: Bevenite a OpenStreetMap, le carta libere e modificabile del mundo. Ora que tu te ha inscribite, tu es preste a comenciar a cartographiar. Ecce un guida curte con le cosas le plus importante a saper. + questions: + title: Questiones? + title: Benvenite! + whats_on_the_map: + off_html: Illo non include datos subjective como punctos de appreciation, elementos historic o hypothetic. Le datos ab fontes sub copyright es equalmente excludite; non copia cosas ab cartas in linea o de papiro sin permission special! + on_html: OpenStreetMap es un loco pro cartographiar cosas real e actual; illo include milliones de edificios, stratas e altere detalios sur locos. Tu pote cartographiar qualcunque elemento del mundo real que te interessa. + title: Que es sur le carta? diff --git a/config/locales/it.yml b/config/locales/it.yml index 9b8b472bb..83eafa9f1 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1111,7 +1111,7 @@ it: partners_ucl: UCL VR Centre sign_up: iscriviti sign_up_tooltip: Crea un profilo utente per apportare modifiche - tag_line: La wiki-mappa Libera del Mondo + tag_line: La wiki-mappa libera del mondo user_diaries: Diari degli utenti user_diaries_tooltip: Visualizza diari utente view: Visualizza diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 917f77e2d..9a685238d 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -95,7 +95,7 @@ ja: not_a_moderator: モデレーター権限が必要な操作です。 setup_user_auth: blocked: APIへのアクセスがブロックされました。Web インターフェイスからログインし直して詳細を確認してください。 - need_to_see_terms: あなたは一時的に API の利用を停止されています。ウェブ インターフェイスにログインして投稿者規約をご覧ください。同意しなくてもかまいませんが、必ずご覧ください。 + need_to_see_terms: あなたは一時的にAPIの利用を停止されています。ウェブインターフェイスにログインして協力者規約をご覧ください。同意しなくてもかまいませんが、必ずご覧ください。 browse: changeset: changeset: "変更セット: %{id}" @@ -992,13 +992,14 @@ ja: key: title: 凡例 tooltip: 凡例 + tooltip_disabled: 標準レイヤーのみで利用可能な地図キー map: base: cycle_map: サイクリングマップ hot: Humanitarian standard: 標準 transport_map: 交通マップ - copyright: © OpenStreetMap への貢献者 + copyright: © OpenStreetMapへの協力者 layers: data: 地図データ header: 地図のレイヤー @@ -1033,10 +1034,13 @@ ja: resolve: 解決 share: cancel: キャンセル + center_marker: マーカーを地図の中心にする + custom_dimensions: 独自の寸法を設定 download: ダウンロード embed: HTML format: "形式:" image: 画像 + image_size: イメージの表示は標準レイヤー上で include_marker: マーカーを含める link: リンクまたは HTML long_link: リンク @@ -1118,6 +1122,8 @@ ja: title: 権利表示の例 contributors_at_html: "オーストリア: Stadt Wien (ライセンス CC BY)、Land Vorarlberg、Land Tirol (ライセンス CC BY AT with amendments) によるデータを含みます。" contributors_ca_html: "カナダ: GeoBase®ã€GeoGratis (© Department of Natural Resources Canada)、CanVec (© Department of Natural Resources Canada)、StatCan (Geography Division, Statistics Canada) によるデータを含みます。" + contributors_footer_1_html: これらの詳細について、またOpenStreetMapの向上に使用されたその他のソースについては、OpenStreetMap Wikiの協力者ページをご覧ください。 + contributors_footer_2_html: OpenStreetMapのデータに含めることは、元データの提供者がOpenStreetMapを支持したり、何かしらの保証を行ったり、何かしらの責任を負ったりすることを意味するものではありません。 contributors_fr_html: "フランス: Direction Générale des Impôts によるデータを含みます。" contributors_gb_html: "イギリス: 陸地測量データ © Crown copyright and database right 2010-12 を含みます。" contributors_intro_html: 協力者は数千人もの個人です。それに加え、国立の地図作成組織や、次のようなその他の情報源による公開できるライセンスによるデータを含みます。 @@ -1125,11 +1131,12 @@ ja: contributors_nz_html: "ニュージーランド: Land Information New Zealand によるデータを含みます。Crown が著作権を保持します。" contributors_title_html: 協力者 contributors_za_html: "南アフリカ: Chief Directorate: National Geo-Spatial Information, State によるデータを含み、著作権を保持します。" - credit_1_html: 「© OpenStreetMap への貢献者」のクレジットを必ず使用してください。 + credit_1_html: 「© OpenStreetMapへの協力者」のクレジットを必ず使用してください。 credit_2_html: あなたはデータが Open Database License に基づいて提供されていること、そして地図タイルを使用する場合は、地図製作が CC BY-SA としてライセンスされていることを明確にしなければなりません。著作権表示ページにリンクすることでこれを表現することができます。また、データの形式でOSMを配布する場合の要件として、名前を表示の上、ライセンスに直接リンクすることができます。リンクを張れないメディア(印刷など)の場合は、openstreetmap.org(OpenStreetMapをアドレスとして展開した形)、opendatacommons.org、あと関連があれば、creativecommons.orgに誘導することをお勧めします。 credit_3_html: "閲覧可能な電子地図の場合は、地図の隅にクレジットを表示してください。\n例:" credit_title_html: OpenStreetMap のクレジット表記の仕方 - infringement_1_html: "OSM の協力者は、著作権者から明確な許諾を得ずに、著作権のある情報源 (例: Google マップや印刷された地図) からデータを持ち込まないよう注意するものとします。" + infringement_1_html: "OSMの協力者は、著作権者から明確な許諾を得ずに、著作権のある情報源 (例: Google マップや印刷された地図) からデータを持ち込まないよう注意するものとします。" + infringement_2_html: もし著作権のある素材がOpenStreetMapのデータベースや本サイトに不正に追加されたとお考えの場合は、却下手順をお読みになるか、on-line filing pageから直接申し立てを行ってください。 infringement_title_html: 著作権侵害 intro_1_html: "OpenStreetMap はオープンデータであり、Open Data Commons Open Database License (ODbL) の下にライセンスされています。" intro_2_html: OpenStreetMapとその協力者をクレジットすれば、データを自由にコピー、配布、送信、利用することができます。変更したり翻案したりしたデータは、同じライセンスに従って提供することができます。あなたの権利と責任は、ライセンス契約の全文で説明しています。 @@ -1215,6 +1222,8 @@ ja: commented_at_html: "%{when}前に更新" opened_at_by_html: "%{when}前に%{user}が作成" opened_at_html: "%{when}前に作成" + reopened_at_by_html: "%{user}さんが%{when}に再開" + reopened_at_html: "%{when}前に再開" entry: comment: コメント full: メモ全文 @@ -1234,6 +1243,7 @@ ja: description_area: あなたのエリア [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})] でレポート、コメント、またはクローズされたメモの一覧 description_item: メモ %{id} の RSS フィード opened: 新しいメモ (%{place} 付近) + reopened: 再開されたメモ(%{place}付近) title: OpenStreetMap メモ notifier: diary_comment_notification: @@ -1287,13 +1297,24 @@ ja: note_comment_notification: anonymous: 匿名ユーザー closed: + commented_note: "%{commenter}さんが、あなたがコメントした地図上の%{place}付近にあるメモを解決しました。" + subject_other: "[OpenStreetMap] %{commenter}さんが、あなたが関心を持っているメモを解決しました" subject_own: "[OpenStreetMap] %{commenter} さんがあなたのメモを解決しました" - your_note: "%{commenter} さんが %{place} 付近のあなたの地図メモの 1 つにコメントしました。" + your_note: "%{commenter}さんが%{place}付近にあるあなたの地図メモの1つを解決しました。" commented: + commented_note: "%{commenter}さんがあなたがコメントした地図上の%{place}付近にあるメモにコメントしました。" + subject_other: "[OpenStreetMap] %{commenter}さんがあなたが関心を持っているメモにコメントしました" subject_own: "[OpenStreetMap] %{commenter} さんがあなたのメモにコメントしました" + your_note: "%{commenter}さんが%{place}付近にあるあなたの地図メモの1つにコメントを残しました。" details: メモについての詳細は %{url} を参照。 greeting: こんにちは。 + reopened: + commented_note: "%{commenter}さんが、%{place}付近にあるあなたがコメントした地図メモを再開しました。" + subject_other: "[OpenStreetMap] %{commenter}さんがあなたが関心を持っているメモを再開しました" + subject_own: "[OpenStreetMap] %{commenter}さんがあなたのメモの1つを再開しました" + your_note: "%{commenter}さんが%{place}付近にあるあなたの地図メモの1つを再開しました。" signup_confirm: + confirm: 私たちが他に何かする前に、この要求があなたのものであることを確認する必要がありますので、その後であなたのアカウントを確認するために下のリンクをクリックしてください。 created: 誰か (おそらくあなた) が %{site_url} でアカウントを作成しました。 greeting: やあ、皆さん! subject: "[OpenStreetMap] OpenStreetMapへようこそ" @@ -1403,17 +1424,22 @@ ja: flash: 変更を保存しました。 site: edit: + anon_edits_link_text: なぜこれれが事例なのかを見る。 flash_player_required: Flash 版 OpenStreetMap エディターである Potlatch を使用するには、Flash Player が必要です。Flash Player はAdobe.com でダウンロードできます。OpenStreetMap を編集する他の方法もあります。 + id_not_configured: iDが設定されていません。 no_iframe_support: あなたのブラウザーは、この機能に必須の HTML iframe に未対応です。 not_public: あなたの編集結果を公開できません。 + not_public_description: このようなことをしない限り、あなたは地図を編集できます。あなたは%{user_page}から編集内容を公開できます。 potlatch2_not_configured: "Potlatch 2 が設定されていません - 詳細情報はこちらをご覧ください: http://wiki.openstreetmap.org/wiki/The_Rails_Port#Potlatch_2" potlatch2_unsaved_changes: 保存していない変更があります。(Potlatch 2 では、保存をクリックして保存する必要があります。) potlatch_unsaved_changes: 保存していない変更があります。(Potlatch では、一覧モードで編集している場合、ウェイや点の選択を解除する必要があります。または、保存ボタンをクリックして保存してください。) - user_page_link: ユーザー ページ + user_page_link: ユーザーページ index: createnote: メモを追加 js_1: JavaScript に対応していないブラウザーを使用しているか、JavaScript を無効にしているかのどちらかです。 js_2: OpenStreetMap は地図の表示に JavaScript を使用します。 + license: + copyright: Copyright OpenStreetMapおよび協力者、オープンライセンスの下で permalink: 固定リンク remote_failed: 編集に失敗しました - JOSM または Merkaartor が読み込まれていること、遠隔制御オプションが有効になっていることを確認してください shortlink: 短縮リンク @@ -1559,10 +1585,12 @@ ja: message: GPX ファイルのアップロード システムは現在利用できません trace: ago: "%{time_in_words_ago}前" + by: "投稿者:" count_points: "%{count} 個の点" edit: 編集 edit_map: 地図を編集 identifiable: 識別可能 + in: "タグ:" map: 地図 more: 詳細 pending: 処理中 @@ -1621,12 +1649,12 @@ ja: user: account: contributor terms: - agreed: あなたは、新しい投稿規約を承諾しています。 + agreed: あなたは、新しい協力者規約を承諾しています。 agreed_with_pd: また、あなたは、自分の編集結果がパブリックドメインにあるべきだと考えているということも宣言しています。 - heading: "投稿規約:" + heading: "協力者規約:" link text: これは何ですか? not yet agreed: あなたはまだ新しい投稿規約を承諾していません。 - review link text: ご都合の良い時にこのリンクをクリックして新しい投稿規約をレビューの上、同意してください。 + review link text: ご都合の良い時にこのリンクをクリックして新しい協力者規約を確認の上、同意してください。 current email address: "現在のメール アドレス:" delete image: 現在の画像を削除 email never displayed publicly: (非公開) @@ -1674,6 +1702,7 @@ ja: button: 確認 heading: メールを確認してください introduction_1: 確認メールをお送りしました。 + introduction_2: メール内のリンクをクリックしてアカウントの確認をすれば、マッピングを始められます。 press confirm button: アカウントを有効にして良ければ、以下の確認ボタンを押してください。 reconfirm_html: 確認メールを再送する必要がある場合は、ここをクリックしてください。 unknown token: この確認コードは期限切れ、または存在しません。 @@ -1763,6 +1792,7 @@ ja: new: about: header: フリー、編集可能 + html: "

他の地図とは異なり、OpenStreetMapは完全にあなたのような人々によって作成され、だれでも自由に修正、更新、ダウンロード、利用することができます。

\n

協力を始めるにはサインアップしてください。あなたのアカウントの確認メールが送信されます。

" confirm email address: "メール アドレスの確認:" confirm password: "パスワードの確認:" contact_webmaster: アカウントを作成できるよう、webmasterに連絡してください。 できるだけ早期に、あなたの希望に対応するように努めます。 @@ -1770,15 +1800,15 @@ ja: display name: "表示名:" display name description: 自分の公開ユーザー名です。あとで設定ページで変更できます。 email address: "メール アドレス:" - license_agreement: アカウントを確認するときには contributor terms に同意する必要があります。 + license_agreement: アカウントを確認するときには協力者規約に同意する必要があります。 no_auto_account_create: 残念ながら、現在、自動ではアカウントを作成できません。 not displayed publicly: 非公開です (詳細は プライバシー ポリシーを参照してください) openid: "%{logo} OpenID:" openid association: "

あなたの OpenID は、まだ OpenStreetMap のどのアカウントとも関連付けられていません。

\n" openid no password: OpenID ではパスワードは不要ですが、特殊なツール/サーバーでは必要な場合があります。 password: "パスワード:" - terms accepted: 新しい投稿規約に同意いただき、ありがとうございます! - terms declined: 新しい投稿規約に同意いただけず残念です。詳しい情報は、このウィキページをご覧ください。 + terms accepted: 新しい協力者規約に同意いただき、ありがとうございます! + terms declined: 新しい協力者規約に同意いただけず残念です。詳しい情報は、このウィキページをご覧ください。 terms declined url: http://wiki.openstreetmap.org/wiki/JA:Contributor_Terms_Declined title: ユーザー登録 use openid: 代わりに、%{logo} OpenID を使用してログイン @@ -1817,15 +1847,15 @@ ja: decline: 拒否 declined: http://wiki.openstreetmap.org/wiki/JA:Contributor_Terms_Declined guidance: この規約の理解を助ける情報として、要約 (英語) や 非公式の翻訳 をご覧ください。 - heading: 投稿規約 + heading: 協力者規約 legale_names: france: フランス italy: イタリア rest_of_world: それ以外の国 legale_select: "お住まいの国:" - read and accept: 同意書を読み、あなたの既存および将来の投稿のために本同意書の条項を承諾することを確認するために同意ボタンを押してください。 - title: 投稿規約 - you need to accept or decline: 続行するには新しい投稿規約を読んで同意または拒否してください。 + read and accept: 同意書を読み、あなたの既存および将来の協力のために本同意書の条項を承諾することを確認するために同意ボタンを押してください。 + title: 協力者規約 + you need to accept or decline: 続行するには新しい協力者規約を読んで同意または拒否してください。 view: activate_user: このユーザーを有効にする add as friend: 友達として追加 @@ -1840,7 +1870,7 @@ ja: created from: "作成日:" ct accepted: "%{ago}前に承認" ct declined: 拒否 - ct status: "投稿規約:" + ct status: "協力者規約:" ct undecided: 未決定 deactivate_user: このユーザーを無効にする delete_user: このユーザーを削除 @@ -1906,6 +1936,7 @@ ja: edit: back: すべてのブロックを閲覧 heading: "%{name} のブロックの編集" + needs_view: このブロックを解消する前に、ユーザーがログインする必要がありますか? period: ユーザーが今からどれくらいの間、APIからブロックされるか。 reason: "%{name} さんがブロックされている理由です。できるだけ冷静かつ理性的になり、できるだけ丁寧に状況を説明するようにしてください。すべてのユーザーがコミュニティ内での隠語を理解しているわけではないため、分かりやすい用語を使うように努めてください。" show: このブロックを閲覧 @@ -1934,6 +1965,7 @@ ja: submit: ブロックを作成 title: "%{name} のブロックの作成" tried_contacting: ユーザーに連絡して、それらをやめるよう依頼しました。 + tried_waiting: 私はユーザーとの対話に応じるために、合理的な時間を費やしました。 not_found: back: 索引に戻る sorry: 申し訳ありませんが、ID %{id} のユーザー ブロックは見つかりませんでした。 @@ -1997,6 +2029,8 @@ ja: title: 権限取り消しの確認 welcome_page: add_a_note: + paragraph_1_html: "些細な修正だけをしたいが、サインアップしたり編集の仕方を勉強する時間がない場合は、\n簡単にメモを追加できます。" + paragraph_2_html: 地図に移動してメモアイコン()をクリックするだけです。これで地図上に印が追加され、ドラッグして移動させることができます。あなたのメッセージを追加し、保存をクリックすれば、他のマッパーが調査するでしょう。 title: 編集する時間がないためメモを残します basic_terms: editor_html: エディターは、地図を編集できるプログラムやウェブサイトです。 @@ -2005,9 +2039,13 @@ ja: tag_html: タグはノードやウェイについて補足するデータです。レストランの名前、道路の制限速度などが該当します。 title: マッピングのための基本的な用語 way_html: ウェイは線または領域です。道路、水路、湖、建物などが該当します。 + introduction_html: フリーで編集可能な世界地図、OpenStreetMapへようこそ。サインアップしたので、マッピングの準備はすべて整いました。知っておかなければならない重要事項を簡単に説明します。 questions: + paragraph_1_html: マッピングで分からないことがありますか?OpenStreetMapの使い方で不明な点はありますか?ヘルプのウェブサイトで疑問の回答を得てください。 title: 何か質問はありますか? start_mapping: マッピングを開始 title: ようこそ! whats_on_the_map: + off_html: 含めてはいけないものは、評価のような主観的なデータ、歴史的または仮想的なもの、著作権のある情報源からのデータです。特別な許可がない場合は、オンラインや紙の地図からコピーをしてはいけません。 + on_html: OpenStreetMapは現実かつ現存のもの――数々の建物、道路、場所に関するその他の詳細をマッピングする場所です。あなたは関心のある現実世界のものを何でもマッピングできます。 title: 地図上にあるもの diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3dda0fa40..27327ff57 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1069,7 +1069,7 @@ ko: help_title: 프로젝트에 대한 도움말 사이트 history: 역사 home: 집 위치로 가기 - intro_1: OpenStreetMap은 여러분같은 사람에 의해 만들어진 우리 모두의 전세계 지도입니다. + intro_1: OpenStreetMap은 여러분 같은 사람에 의해 만들어진 자유로운 전세계 지도입니다. intro_2_create_account: 사용자 계정을 만드세요 intro_2_download: 다운로드 intro_2_html: 데이터는 %{license}에 따라 자유롭게 %{download}와 %{use}할 수 있습니다. 지도를 개선하려면 %{create_account}. diff --git a/config/locales/lv.yml b/config/locales/lv.yml index fb49ebb0a..3ff4a9f5f 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -485,8 +485,10 @@ lv: title: ca_postcode: Rezultāti no Geocoder.CA geonames: Rezultāti no GeoNames + geonames_reverse: Rezultāti no GeoNames latlon: Rezultāti no Iekšējās meklēšanas osm_nominatim: Rezultāti no OpenStreetMap Nominatim + osm_nominatim_reverse: Rezultāti no OpenStreetMap Nominatim uk_postcode: Rezultāti no NPEMap / FreeThe Postcode us_postcode: Rezultāti no Geocoder.us search_osm_nominatim: @@ -993,6 +995,7 @@ lv: map: base: cycle_map: Velokarte + hot: Humanitārās standard: Standarta transport_map: Transporta karte copyright: © OpenStreetMap ieguldītāji @@ -1545,6 +1548,9 @@ lv: delete: scheduled_for_deletion: Trase atzīmēta dzēšanai description: + description_with_count: + one: GPX fails ar %{count} punktu no %{user} + other: GPX fails ar %{count} punktiem no %{user} description_without_count: GPX fails no %{user} edit: description: "Apraksts:" @@ -1563,6 +1569,8 @@ lv: uploaded_at: "Augšupielādēts:" visibility: "Redzamība:" visibility_help: ko tas nozīmē? + georss: + title: OpenStreetMap GPS Trases list: description: Pārlūkot jaunākas GPS trases augšupielādes. empty_html: Pašlaik šeit nekā nav. Augšupielādē jaunu trasi vai uzzini vairāk par GPS trasēm iekš wiki lapas. diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 450d9ae08..7fe24a9b3 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -486,8 +486,10 @@ sl: title: ca_postcode: Zadetki iz Geocoder.CA geonames: Zadetki iz GeoNames + geonames_reverse: Zadetki iz GeoNames latlon: Interni zadetki osm_nominatim: Zadetki iz OpenStreetMap Nominatim + osm_nominatim_reverse: Zadetki iz OpenStreetMap Nominatim-a uk_postcode: Zadetki iz NPEMap / FreeThe Postcode us_postcode: Zadetki iz Geocoder.us search_osm_nominatim: @@ -687,6 +689,7 @@ sl: monument: Spomenik museum: Muzej ruins: Ruševine + tomb: Grobnica tower: Stolp wayside_cross: Križ wayside_shrine: Kapelica @@ -999,6 +1002,7 @@ sl: header: Plasti zemljevida notes: Opombe na zemljevidu overlays: Omogočite prekrivke za odpravljanje težav na zemljevidu + title: Plasti locate: popup: Ste v razdalji {distance} {unit} od te točke title: Pokaži mojo lokacijo @@ -1010,13 +1014,19 @@ sl: add: Dodaj opombo show: anonymous_warning: To opomba vključuje pripombe anonimnih uporabnikov, ki bi morale biti posamezno preverjene. + closed_by: razrešil %{user} ob %{time} + closed_by_anonymous: razrešil anonimnež ob %{time} comment: Komentar comment_and_resolve: Komentiraj in razreši + commented_by: komentar uporabnika %{user} ob %{time} + commented_by_anonymous: Anonimni komentar ob %{time} hide: Skrij opened_by: ustvaril/-a %{user} ob %{time} opened_by_anonymous: ustvaril anonimni uporabnik %{time} permalink: Trajna povezava reactivate: Znova aktiviraj + reopened_by: ponovno aktiviral %{user} ob %{time} + reopened_by_anonymous: ponovno aktiviral anonimnež ob %{time} resolve: Razreši share: cancel: Prekliči @@ -1857,6 +1867,7 @@ sl: my diary: Moj dnevnik my edits: Moji prispevki my notes: Moje beležke + my profile: Moj profil my settings: Moje nastavitve my traces: Moje sledi nearby users: Drugi bližnji uporabniki diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 5c802bc74..ba874d1da 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -96,7 +96,7 @@ sv: user_token: Användarnyckel way: Sträcka way_node: Sträcknod - way_tag: Sträcktagg + way_tag: Vägtagg application: require_cookies: cookies_needed: Du verkar ha inaktiverat cookies - aktivera cookies i din webbläsare innan du fortsätter. diff --git a/test/functional/diary_entry_controller_test.rb b/test/functional/diary_entry_controller_test.rb index 7e0f10790..8725def3d 100644 --- a/test/functional/diary_entry_controller_test.rb +++ b/test/functional/diary_entry_controller_test.rb @@ -86,8 +86,6 @@ class DiaryEntryControllerTest < ActionController::TestCase end def test_showing_new_diary_entry - @request.cookies["_osm_username"] = users(:normal_user).display_name - get :new assert_response :redirect assert_redirected_to :controller => :user, :action => "login", :referer => "/diary/new" @@ -125,7 +123,6 @@ class DiaryEntryControllerTest < ActionController::TestCase end def test_editing_diary_entry - @request.cookies["_osm_username"] = users(:normal_user).display_name entry = diary_entries(:normal_user_entry_1) # Make sure that you are redirected to the login page when you are @@ -217,8 +214,6 @@ class DiaryEntryControllerTest < ActionController::TestCase end end - @request.cookies["_osm_username"] = users(:public_user).display_name - # and when not logged in as the user who wrote the entry get :view, {:display_name => entry.user.display_name, :id => entry.id}, {'user' => entry.user.id} assert_response :success @@ -251,16 +246,12 @@ class DiaryEntryControllerTest < ActionController::TestCase end def test_edit_diary_entry_i18n - @request.cookies["_osm_username"] = users(:normal_user).display_name - get :edit, {:display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_entry_1).id}, {'user' => users(:normal_user).id} assert_response :success assert_select "span[class=translation_missing]", false, "Missing translation in edit diary entry" end def test_create_diary_entry - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Make sure that you are redirected to the login page when you # are not logged in get :new @@ -320,7 +311,6 @@ class DiaryEntryControllerTest < ActionController::TestCase end def test_creating_diary_comment - @request.cookies["_osm_username"] = users(:public_user).display_name entry = diary_entries(:normal_user_entry_1) # Make sure that you are denied when you are not logged in @@ -472,16 +462,12 @@ class DiaryEntryControllerTest < ActionController::TestCase assert_response :forbidden assert_equal true, DiaryEntry.find(diary_entries(:normal_user_entry_1).id).visible - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Now try as a normal user post :hide, {:display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_entry_1).id}, {:user => users(:normal_user).id} assert_response :redirect assert_redirected_to :action => :view, :display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_entry_1).id assert_equal true, DiaryEntry.find(diary_entries(:normal_user_entry_1).id).visible - @request.cookies["_osm_username"] = users(:administrator_user).display_name - # Finally try as an administrator post :hide, {:display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_entry_1).id}, {:user => users(:administrator_user).id} assert_response :redirect @@ -495,16 +481,12 @@ class DiaryEntryControllerTest < ActionController::TestCase assert_response :forbidden assert_equal true, DiaryComment.find(diary_comments(:comment_for_geo_post).id).visible - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Now try as a normal user post :hidecomment, {:display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_geo_entry).id, :comment => diary_comments(:comment_for_geo_post).id}, {:user => users(:normal_user).id} assert_response :redirect assert_redirected_to :action => :view, :display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_geo_entry).id assert_equal true, DiaryComment.find(diary_comments(:comment_for_geo_post).id).visible - @request.cookies["_osm_username"] = users(:administrator_user).display_name - # Finally try as an administrator post :hidecomment, {:display_name => users(:normal_user).display_name, :id => diary_entries(:normal_user_geo_entry).id, :comment => diary_comments(:comment_for_geo_post).id}, {:user => users(:administrator_user).id} assert_response :redirect diff --git a/test/functional/message_controller_test.rb b/test/functional/message_controller_test.rb index f015b1ec3..77fdfbeb9 100644 --- a/test/functional/message_controller_test.rb +++ b/test/functional/message_controller_test.rb @@ -53,7 +53,6 @@ class MessageControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:normal_user).id - cookies["_osm_username"] = users(:normal_user).display_name # Check that the new message page loads get :new, :display_name => users(:public_user).display_name @@ -106,7 +105,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the wrong user session[:user] = users(:second_public_user).id - cookies["_osm_username"] = users(:second_public_user).display_name # Check that we can't reply to somebody else's message get :reply, :message_id => messages(:unread_message).id @@ -115,7 +113,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the right user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that the message reply page loads get :reply, :message_id => messages(:unread_message).id @@ -149,7 +146,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the wrong user session[:user] = users(:second_public_user).id - cookies["_osm_username"] = users(:second_public_user).display_name # Check that we can't read the message get :read, :message_id => messages(:unread_message).id @@ -158,7 +154,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the message sender session[:user] = users(:normal_user).id - cookies["_osm_username"] = users(:normal_user).display_name # Check that the message sender can read the message get :read, :message_id => messages(:unread_message).id @@ -168,7 +163,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the message recipient session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that the message recipient can read the message get :read, :message_id => messages(:unread_message).id @@ -196,7 +190,6 @@ class MessageControllerTest < ActionController::TestCase # Login session[:user] = users(:normal_user).id - cookies["_osm_username"] = users(:normal_user).display_name # Check that we can view our inbox when logged in get :inbox, :display_name => users(:normal_user).display_name @@ -221,7 +214,6 @@ class MessageControllerTest < ActionController::TestCase # Login session[:user] = users(:normal_user).id - cookies["_osm_username"] = users(:normal_user).display_name # Check that we can view our outbox when logged in get :outbox, :display_name => users(:normal_user).display_name @@ -246,7 +238,6 @@ class MessageControllerTest < ActionController::TestCase # Login as a user with no messages session[:user] = users(:second_public_user).id - cookies["_osm_username"] = users(:second_public_user).display_name # Check that marking a message we didn't send or receive fails post :mark, :message_id => messages(:read_message).id @@ -255,7 +246,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the message recipient session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that the marking a message read works post :mark, :message_id => messages(:unread_message).id, :mark => "read" @@ -299,7 +289,6 @@ class MessageControllerTest < ActionController::TestCase # Login as a user with no messages session[:user] = users(:second_public_user).id - cookies["_osm_username"] = users(:second_public_user).display_name # Check that deleting a message we didn't send or receive fails post :delete, :message_id => messages(:read_message).id @@ -308,7 +297,6 @@ class MessageControllerTest < ActionController::TestCase # Login as the message recipient session[:user] = users(:normal_user).id - cookies["_osm_username"] = users(:normal_user).display_name # Check that the deleting a received message works post :delete, :message_id => messages(:read_message).id diff --git a/test/functional/redactions_controller_test.rb b/test/functional/redactions_controller_test.rb index a2bd75b10..83bb3adc2 100644 --- a/test/functional/redactions_controller_test.rb +++ b/test/functional/redactions_controller_test.rb @@ -39,7 +39,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_moderators_can_create session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name post :create, :redaction => { :title => "Foo", :description => "Description here." } assert_response :redirect @@ -48,7 +47,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_non_moderators_cant_create session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name post :create, :redaction => { :title => "Foo", :description => "Description here." } assert_response :forbidden @@ -56,7 +54,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_moderators_can_delete_empty session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # remove all elements from the redaction redaction = redactions(:example) @@ -71,7 +68,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_moderators_cant_delete_nonempty session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # leave elements in the redaction redaction = redactions(:example) @@ -84,7 +80,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_non_moderators_cant_delete session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name delete :destroy, :id => redactions(:example).id assert_response :forbidden @@ -92,7 +87,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_moderators_can_edit session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name get :edit, :id => redactions(:example).id assert_response :success @@ -100,7 +94,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_non_moderators_cant_edit session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name get :edit, :id => redactions(:example).id assert_response :redirect @@ -109,7 +102,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_moderators_can_update session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name redaction = redactions(:example) @@ -120,7 +112,6 @@ class RedactionsControllerTest < ActionController::TestCase def test_non_moderators_cant_update session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name redaction = redactions(:example) diff --git a/test/functional/site_controller_test.rb b/test/functional/site_controller_test.rb index 00a184ba8..2db756ad1 100644 --- a/test/functional/site_controller_test.rb +++ b/test/functional/site_controller_test.rb @@ -132,8 +132,6 @@ class SiteControllerTest < ActionController::TestCase # test the right editor gets used when the user hasn't set a preference def test_edit_without_preference - @request.cookies["_osm_username"] = users(:public_user).display_name - get(:edit, nil, { 'user' => users(:public_user).id }) assert_response :success assert_template :partial => "_#{DEFAULT_EDITOR}", :count => 1 @@ -141,8 +139,6 @@ class SiteControllerTest < ActionController::TestCase # and when they have... def test_edit_with_preference - @request.cookies["_osm_username"] = users(:public_user).display_name - user = users(:public_user) user.preferred_editor = "potlatch" user.save! @@ -161,8 +157,6 @@ class SiteControllerTest < ActionController::TestCase end def test_edit_with_node - @request.cookies["_osm_username"] = users(:public_user).display_name - user = users(:public_user) node = current_nodes(:visible_node) @@ -172,8 +166,6 @@ class SiteControllerTest < ActionController::TestCase end def test_edit_with_way - @request.cookies["_osm_username"] = users(:public_user).display_name - user = users(:public_user) way = current_ways(:visible_way) @@ -183,8 +175,6 @@ class SiteControllerTest < ActionController::TestCase end def test_edit_with_gpx - @request.cookies["_osm_username"] = users(:public_user).display_name - user = users(:public_user) gpx = gpx_files(:public_trace_file) diff --git a/test/functional/trace_controller_test.rb b/test/functional/trace_controller_test.rb index 57d6f4916..1a074082b 100644 --- a/test/functional/trace_controller_test.rb +++ b/test/functional/trace_controller_test.rb @@ -171,8 +171,6 @@ class TraceControllerTest < ActionController::TestCase # Check that I can get mine def test_list_mine - @request.cookies["_osm_username"] = users(:public_user).display_name - # First try to get it when not logged in get :mine assert_redirected_to :controller => 'user', :action => 'login', :referer => '/traces/mine' @@ -196,14 +194,10 @@ class TraceControllerTest < ActionController::TestCase get :list, :display_name => users(:public_user).display_name check_trace_list users(:public_user).traces.public - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Should still see only public ones when authenticated as another user get :list, {:display_name => users(:public_user).display_name}, {:user => users(:normal_user).id} check_trace_list users(:public_user).traces.public - @request.cookies["_osm_username"] = users(:public_user).display_name - # Should see all traces when authenticated as the target user get :list, {:display_name => users(:public_user).display_name}, {:user => users(:public_user).id} check_trace_list users(:public_user).traces @@ -234,14 +228,10 @@ class TraceControllerTest < ActionController::TestCase get :view, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id} check_trace_view gpx_files(:public_trace_file) - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should work since the trace is public get :view, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:public_user).id} check_trace_view gpx_files(:public_trace_file) - @request.cookies["_osm_username"] = users(:normal_user).display_name - # And finally we should be able to do it with the owner of the trace get :view, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:normal_user).id} check_trace_view gpx_files(:public_trace_file) @@ -254,15 +244,11 @@ class TraceControllerTest < ActionController::TestCase assert_response :redirect assert_redirected_to :action => :list - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Now with some other user, which should work since the trace is anon get :view, {:display_name => users(:public_user).display_name, :id => gpx_files(:anon_trace_file).id}, {:user => users(:normal_user).id} assert_response :redirect assert_redirected_to :action => :list - @request.cookies["_osm_username"] = users(:public_user).display_name - # And finally we should be able to do it with the owner of the trace get :view, {:display_name => users(:public_user).display_name, :id => gpx_files(:anon_trace_file).id}, {:user => users(:public_user).id} check_trace_view gpx_files(:anon_trace_file) @@ -275,8 +261,6 @@ class TraceControllerTest < ActionController::TestCase assert_response :redirect assert_redirected_to :action => :list - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should work since the trace is public get :view, {:display_name => users(:public_user).display_name, :id => 0}, {:user => users(:public_user).id} assert_response :redirect @@ -294,14 +278,10 @@ class TraceControllerTest < ActionController::TestCase get :data, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id} check_trace_data gpx_files(:public_trace_file) - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should work since the trace is public get :data, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:public_user).id} check_trace_data gpx_files(:public_trace_file) - @request.cookies["_osm_username"] = users(:normal_user).display_name - # And finally we should be able to do it with the owner of the trace get :data, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:normal_user).id} check_trace_data gpx_files(:public_trace_file) @@ -328,14 +308,10 @@ class TraceControllerTest < ActionController::TestCase get :data, {:display_name => users(:public_user).display_name, :id => gpx_files(:anon_trace_file).id} assert_response :not_found - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Now with some other user, which should work since the trace is anon get :data, {:display_name => users(:public_user).display_name, :id => gpx_files(:anon_trace_file).id}, {:user => users(:normal_user).id} assert_response :not_found - @request.cookies["_osm_username"] = users(:public_user).display_name - # And finally we should be able to do it with the owner of the trace get :data, {:display_name => users(:public_user).display_name, :id => gpx_files(:anon_trace_file).id}, {:user => users(:public_user).id} check_trace_data gpx_files(:anon_trace_file) @@ -347,8 +323,6 @@ class TraceControllerTest < ActionController::TestCase get :data, {:display_name => users(:public_user).display_name, :id => 0} assert_response :not_found - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should work since the trace is public get :data, {:display_name => users(:public_user).display_name, :id => 0}, {:user => users(:public_user).id} assert_response :not_found @@ -365,8 +339,6 @@ class TraceControllerTest < ActionController::TestCase assert_response :redirect assert_redirected_to :controller => :user, :action => :login, :referer => trace_edit_path(:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id) - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should fail get :edit, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:public_user).id} assert_response :forbidden @@ -379,8 +351,6 @@ class TraceControllerTest < ActionController::TestCase get :edit, {:display_name => users(:public_user).display_name, :id => gpx_files(:deleted_trace_file).id}, {:user => users(:public_user).id} assert_response :not_found - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Finally with a trace that we are allowed to edit get :edit, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:normal_user).id} assert_response :success @@ -395,8 +365,6 @@ class TraceControllerTest < ActionController::TestCase post :edit, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id, :trace => new_details} assert_response :forbidden - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should fail post :edit, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id, :trace => new_details}, {:user => users(:public_user).id} assert_response :forbidden @@ -409,8 +377,6 @@ class TraceControllerTest < ActionController::TestCase post :edit, {:display_name => users(:public_user).display_name, :id => gpx_files(:deleted_trace_file).id, :trace => new_details}, {:user => users(:public_user).id} assert_response :not_found - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Finally with a trace that we are allowed to edit post :edit, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id, :trace => new_details}, {:user => users(:normal_user).id} assert_response :redirect @@ -427,8 +393,6 @@ class TraceControllerTest < ActionController::TestCase post :delete, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id,} assert_response :forbidden - @request.cookies["_osm_username"] = users(:public_user).display_name - # Now with some other user, which should fail post :delete, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:public_user).id} assert_response :forbidden @@ -441,8 +405,6 @@ class TraceControllerTest < ActionController::TestCase post :delete, {:display_name => users(:public_user).display_name, :id => gpx_files(:deleted_trace_file).id}, {:user => users(:public_user).id} assert_response :not_found - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Finally with a trace that we are allowed to delete post :delete, {:display_name => users(:normal_user).display_name, :id => gpx_files(:public_trace_file).id}, {:user => users(:normal_user).id} assert_response :redirect diff --git a/test/functional/user_blocks_controller_test.rb b/test/functional/user_blocks_controller_test.rb index 39ebfd28e..49fb6552f 100644 --- a/test/functional/user_blocks_controller_test.rb +++ b/test/functional/user_blocks_controller_test.rb @@ -101,7 +101,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as the blocked user session[:user] = users(:blocked_user).id - cookies["_osm_username"] = users(:blocked_user).display_name # Now viewing it should mark it as seen get :show, :id => user_blocks(:active_block) @@ -118,7 +117,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that normal users can't load the block creation page get :new, :display_name => users(:normal_user).display_name @@ -127,7 +125,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a moderator session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # Check that the block creation page loads for moderators get :new, :display_name => users(:normal_user).display_name @@ -162,7 +159,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that normal users can't load the block edit page get :edit, :id => user_blocks(:active_block).id @@ -171,7 +167,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a moderator session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # Check that the block edit page loads for moderators get :edit, :id => user_blocks(:active_block).id @@ -204,7 +199,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that normal users can't create blocks post :create @@ -212,7 +206,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a moderator session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # A bogus block period should result in an error assert_no_difference "UserBlock.count" do @@ -263,7 +256,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that normal users can't update blocks put :update, :id => user_blocks(:active_block).id @@ -271,7 +263,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as the wrong moderator session[:user] = users(:second_moderator_user).id - cookies["_osm_username"] = users(:second_moderator_user).display_name # Check that only the person who created a block can update it assert_no_difference "UserBlock.count" do @@ -285,7 +276,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as the correct moderator session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # A bogus block period should result in an error assert_no_difference "UserBlock.count" do @@ -331,7 +321,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Check that normal users can't load the block revoke page get :revoke, :id => user_blocks(:active_block).id @@ -340,7 +329,6 @@ class UserBlocksControllerTest < ActionController::TestCase # Login as a moderator session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # Check that the block revoke page loads for moderators get :revoke, :id => user_blocks(:active_block).id diff --git a/test/functional/user_controller_test.rb b/test/functional/user_controller_test.rb index cba27f852..66fcef785 100644 --- a/test/functional/user_controller_test.rb +++ b/test/functional/user_controller_test.rb @@ -351,17 +351,12 @@ class UserControllerTest < ActionController::TestCase def test_user_terms_seen user = users(:normal_user) - # Set the username cookie - @request.cookies["_osm_username"] = user.display_name - get :terms, {}, { "user" => user } assert_response :redirect assert_redirected_to :action => :account, :display_name => user.display_name end def test_user_go_public - @request.cookies["_osm_username"] = users(:normal_user).display_name - post :go_public, {}, { :user => users(:normal_user) } assert_response :redirect assert_redirected_to :action => :account, :display_name => users(:normal_user).display_name @@ -460,20 +455,15 @@ class UserControllerTest < ActionController::TestCase # validation errors being reported user = users(:normal_user) - # Set the username cookie - @request.cookies["_osm_username"] = user.display_name - # Make sure that you are redirected to the login page when # you are not logged in get :account, { :display_name => user.display_name } assert_response :redirect assert_redirected_to :controller => :user, :action => "login", :referer => "/user/test/account" - # Make sure that you are redirected to the login page when - # you are not logged in as the right user + # Make sure that you are blocked when not logged in as the right user get :account, { :display_name => user.display_name }, { "user" => users(:public_user).id } - assert_response :redirect - assert_redirected_to :controller => :user, :action => "login", :referer => "/user/test/account" + assert_response :forbidden # Make sure we get the page when we are logged in as the right user get :account, { :display_name => user.display_name }, { "user" => user } @@ -490,8 +480,8 @@ class UserControllerTest < ActionController::TestCase assert_select "form#accountForm > fieldset > div.form-row > div#user_description_container > div#user_description_content > textarea#user_description", user.description # Changing name to one that exists should fail - user.display_name = users(:public_user).display_name - post :account, { :display_name => user.display_name, :user => user.attributes }, { "user" => user.id } + new_attributes = user.attributes.dup.merge(:display_name => users(:public_user).display_name) + post :account, { :display_name => user.display_name, :user => new_attributes }, { "user" => user.id } assert_response :success assert_template :account assert_select "div#notice", false @@ -499,8 +489,8 @@ class UserControllerTest < ActionController::TestCase assert_select "form#accountForm > fieldset > div.form-row > div.field_with_errors > input#user_display_name" # Changing name to one that exists should fail, regardless of case - user.display_name = users(:public_user).display_name.upcase - post :account, { :display_name => user.display_name, :user => user.attributes }, { "user" => user.id } + new_attributes = user.attributes.dup.merge(:display_name => users(:public_user).display_name.upcase) + post :account, { :display_name => user.display_name, :user => new_attributes }, { "user" => user.id } assert_response :success assert_template :account assert_select "div#notice", false @@ -508,16 +498,16 @@ class UserControllerTest < ActionController::TestCase assert_select "form#accountForm > fieldset > div.form-row > div.field_with_errors > input#user_display_name" # Changing name to one that doesn't exist should work - user.display_name = "new tester" - post :account, { :display_name => user.display_name, :user => user.attributes }, { "user" => user.id } + new_attributes = user.attributes.dup.merge(:display_name => "new tester") + post :account, { :display_name => user.display_name, :user => new_attributes }, { "user" => user.id } assert_response :success assert_template :account assert_select "div#errorExplanation", false assert_select "div#notice", /^User information updated successfully/ - assert_select "form#accountForm > fieldset > div.form-row > input#user_display_name[value=?]", user.display_name + assert_select "form#accountForm > fieldset > div.form-row > input#user_display_name[value=?]", "new tester" - # Need to update cookies now to stay valid - @request.cookies["_osm_username"] = user.display_name + # Record the change of name + user.display_name = "new tester" # Changing email to one that exists should fail user.new_email = users(:public_user).email @@ -598,7 +588,6 @@ class UserControllerTest < ActionController::TestCase # Login as a normal user session[:user] = users(:normal_user).id - cookies["_osm_username"] = users(:normal_user).display_name # Test the normal user get :view, {:display_name => "test"} @@ -616,7 +605,6 @@ class UserControllerTest < ActionController::TestCase # Login as a moderator session[:user] = users(:moderator_user).id - cookies["_osm_username"] = users(:moderator_user).display_name # Test the normal user get :view, {:display_name => "test"} @@ -734,9 +722,6 @@ class UserControllerTest < ActionController::TestCase # Check that the users aren't already friends assert_nil Friend.where(:user_id => user.id, :friend_user_id => friend.id).first - # Set the username cookie - @request.cookies["_osm_username"] = user.display_name - # When not logged in a GET should ask us to login get :make_friend, {:display_name => friend.display_name} assert_redirected_to :controller => :user, :action => "login", :referer => make_friend_path(:display_name => friend.display_name) @@ -787,9 +772,6 @@ class UserControllerTest < ActionController::TestCase # Check that the users are friends assert Friend.where(:user_id => user.id, :friend_user_id => friend.id).first - # Set the username cookie - @request.cookies["_osm_username"] = user.display_name - # When not logged in a GET should ask us to login get :remove_friend, {:display_name => friend.display_name} assert_redirected_to :controller => :user, :action => "login", :referer => remove_friend_path(:display_name => friend.display_name) @@ -838,15 +820,11 @@ class UserControllerTest < ActionController::TestCase assert_response :redirect assert_redirected_to :action => :login, :referer => set_status_user_path(:status => "suspended") - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Now try as a normal user get :set_status, {:display_name => users(:normal_user).display_name, :status => "suspended"}, {:user => users(:normal_user).id} assert_response :redirect assert_redirected_to :action => :view, :display_name => users(:normal_user).display_name - @request.cookies["_osm_username"] = users(:administrator_user).display_name - # Finally try as an administrator get :set_status, {:display_name => users(:normal_user).display_name, :status => "suspended"}, {:user => users(:administrator_user).id} assert_response :redirect @@ -860,15 +838,11 @@ class UserControllerTest < ActionController::TestCase assert_response :redirect assert_redirected_to :action => :login, :referer => delete_user_path(:status => "suspended") - @request.cookies["_osm_username"] = users(:normal_user).display_name - # Now try as a normal user get :delete, {:display_name => users(:normal_user).display_name, :status => "suspended"}, {:user => users(:normal_user).id} assert_response :redirect assert_redirected_to :action => :view, :display_name => users(:normal_user).display_name - @request.cookies["_osm_username"] = users(:administrator_user).display_name - # Finally try as an administrator get :delete, {:display_name => users(:normal_user).display_name, :status => "suspended"}, {:user => users(:administrator_user).id} assert_response :redirect diff --git a/test/functional/user_roles_controller_test.rb b/test/functional/user_roles_controller_test.rb index ed5d1a17d..e942cd0fa 100644 --- a/test/functional/user_roles_controller_test.rb +++ b/test/functional/user_roles_controller_test.rb @@ -25,7 +25,6 @@ class UserRolesControllerTest < ActionController::TestCase # Login as an unprivileged user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Granting should still fail post :grant, :display_name => users(:normal_user).display_name, :role => "moderator" @@ -34,7 +33,6 @@ class UserRolesControllerTest < ActionController::TestCase # Login as an administrator session[:user] = users(:administrator_user).id - cookies["_osm_username"] = users(:administrator_user).display_name UserRole::ALL_ROLES.each do |role| @@ -85,7 +83,6 @@ class UserRolesControllerTest < ActionController::TestCase # Login as an unprivileged user session[:user] = users(:public_user).id - cookies["_osm_username"] = users(:public_user).display_name # Revoking should still fail post :revoke, :display_name => users(:normal_user).display_name, :role => "moderator" @@ -94,7 +91,6 @@ class UserRolesControllerTest < ActionController::TestCase # Login as an administrator session[:user] = users(:administrator_user).id - cookies["_osm_username"] = users(:administrator_user).display_name UserRole::ALL_ROLES.each do |role| diff --git a/vendor/assets/leaflet/leaflet.css b/vendor/assets/leaflet/leaflet.css index e6fb4908e..ac0cd174d 100644 --- a/vendor/assets/leaflet/leaflet.css +++ b/vendor/assets/leaflet/leaflet.css @@ -65,6 +65,16 @@ .leaflet-marker-pane { z-index: 6; } .leaflet-popup-pane { z-index: 7; } +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + /* control positioning */ @@ -160,9 +170,8 @@ .leaflet-control { cursor: auto; } -.leaflet-dragging, -.leaflet-dragging .leaflet-clickable, -.leaflet-dragging .leaflet-container { +.leaflet-dragging .leaflet-container, +.leaflet-dragging .leaflet-clickable { cursor: move; cursor: -webkit-grabbing; cursor: -moz-grabbing; @@ -182,9 +191,8 @@ outline: 2px solid orange; } .leaflet-zoom-box { - border: 2px dotted #05f; - background: white; - opacity: 0.5; + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); } @@ -197,11 +205,11 @@ /* general toolbar styles */ .leaflet-bar { - box-shadow: 0 1px 7px rgba(0,0,0,0.65); - -webkit-border-radius: 4px; - border-radius: 4px; + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; } -.leaflet-bar a, .leaflet-bar a:hover { +.leaflet-bar a, +.leaflet-bar a:hover { background-color: #fff; border-bottom: 1px solid #ccc; width: 26px; @@ -222,16 +230,12 @@ background-color: #f4f4f4; } .leaflet-bar a:first-child { - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; } .leaflet-bar a:last-child { - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; border-bottom: none; } .leaflet-bar a.leaflet-disabled { @@ -240,55 +244,38 @@ color: #bbb; } -.leaflet-touch .leaflet-bar { - -webkit-border-radius: 10px; - border-radius: 10px; - } .leaflet-touch .leaflet-bar a { width: 30px; height: 30px; - } -.leaflet-touch .leaflet-bar a:first-child { - -webkit-border-top-left-radius: 7px; - border-top-left-radius: 7px; - -webkit-border-top-right-radius: 7px; - border-top-right-radius: 7px; - } -.leaflet-touch .leaflet-bar a:last-child { - -webkit-border-bottom-left-radius: 7px; - border-bottom-left-radius: 7px; - -webkit-border-bottom-right-radius: 7px; - border-bottom-right-radius: 7px; - border-bottom: none; + line-height: 30px; } /* zoom control */ -.leaflet-control-zoom-in { +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; } .leaflet-control-zoom-out { - font: bold 22px 'Lucida Console', Monaco, monospace; + font-size: 20px; } .leaflet-touch .leaflet-control-zoom-in { font-size: 22px; - line-height: 30px; } .leaflet-touch .leaflet-control-zoom-out { - font-size: 28px; - line-height: 30px; + font-size: 24px; } /* layers control */ .leaflet-control-layers { - box-shadow: 0 1px 7px rgba(0,0,0,0.4); - background: #f8f8f9; - -webkit-border-radius: 5px; - border-radius: 5px; + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; } .leaflet-control-layers-toggle { background-image: url(images/layers.png); @@ -334,8 +321,8 @@ /* attribution and scale controls */ .leaflet-container .leaflet-control-attribution { - background-color: rgba(255, 255, 255, 0.7); - box-shadow: 0 0 5px #bbb; + background: #fff; + background: rgba(255, 255, 255, 0.7); margin: 0; } .leaflet-control-attribution, @@ -343,6 +330,12 @@ padding: 0 5px; color: #333; } +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover { + text-decoration: underline; + } .leaflet-container .leaflet-control-attribution, .leaflet-container .leaflet-control-scale { font-size: 11px; @@ -356,23 +349,21 @@ .leaflet-control-scale-line { border: 2px solid #777; border-top: none; - color: black; line-height: 1.1; padding: 2px 5px 1px; font-size: 11px; - text-shadow: 1px 1px 1px #fff; - background-color: rgba(255, 255, 255, 0.5); - box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2); white-space: nowrap; overflow: hidden; -moz-box-sizing: content-box; box-sizing: content-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; border-bottom: none; margin-top: -2px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } .leaflet-control-scale-line:not(:first-child):not(:last-child) { border-bottom: 2px solid #777; @@ -385,7 +376,8 @@ } .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { - border: 4px solid rgba(0,0,0,0.3); + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; } @@ -398,8 +390,7 @@ .leaflet-popup-content-wrapper { padding: 1px; text-align: left; - -webkit-border-radius: 12px; - border-radius: 12px; + border-radius: 12px; } .leaflet-popup-content { margin: 13px 19px; @@ -428,7 +419,8 @@ -o-transform: rotate(45deg); transform: rotate(45deg); } -.leaflet-popup-content-wrapper, .leaflet-popup-tip { +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { background: white; box-shadow: 0 3px 14px rgba(0,0,0,0.4); @@ -456,6 +448,27 @@ border-top: 1px solid #ddd; } +.leaflet-oldie .leaflet-popup-content-wrapper { + zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + } +.leaflet-oldie .leaflet-popup-tip-container { + margin-top: -1px; + } + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; + } + /* div icon */ @@ -463,7 +476,3 @@ background: #fff; border: 1px solid #666; } -.leaflet-editing-icon { - -webkit-border-radius: 2px; - border-radius: 2px; - } diff --git a/vendor/assets/leaflet/leaflet.ie.css b/vendor/assets/leaflet/leaflet.ie.css deleted file mode 100644 index 14b84b691..000000000 --- a/vendor/assets/leaflet/leaflet.ie.css +++ /dev/null @@ -1,51 +0,0 @@ -.leaflet-vml-shape { - width: 1px; - height: 1px; - } -.lvml { - behavior: url(#default#VML); - display: inline-block; - position: absolute; - } - -.leaflet-control { - display: inline; - } - -.leaflet-popup-tip { - width: 21px; - _width: 27px; - margin: 0 auto; - _margin-top: -3px; - - filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); - -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; - } -.leaflet-popup-tip-container { - margin-top: -1px; - } -.leaflet-popup-content-wrapper, .leaflet-popup-tip { - border: 1px solid #999; - } -.leaflet-popup-content-wrapper { - zoom: 1; - } - -.leaflet-control-zoom, -.leaflet-control-layers { - border: 3px solid #999; - } -.leaflet-control-layers-toggle { - } -.leaflet-control-attribution, -.leaflet-control-layers, -.leaflet-control-scale-line { - background: white; - } -.leaflet-zoom-box { - filter: alpha(opacity=50); - } -.leaflet-control-attribution { - border-top: 1px solid #bbb; - border-left: 1px solid #bbb; - } diff --git a/vendor/assets/leaflet/leaflet.js b/vendor/assets/leaflet/leaflet.js index 0f0b01be2..2b66295fc 100644 --- a/vendor/assets/leaflet/leaflet.js +++ b/vendor/assets/leaflet/leaflet.js @@ -7,7 +7,7 @@ var oldL = window.L, L = {}; -L.version = '0.6.3'; +L.version = '0.7'; // define Leaflet for Node module pattern loaders, including Browserify if (typeof module === 'object' && typeof module.exports === 'object') { @@ -135,19 +135,23 @@ L.Util = { return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); }, - template: function (str, data) { - return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) { - var value = data[key]; - if (value === undefined) { - throw new Error('No value provided for variable ' + str); - } else if (typeof value === 'function') { - value = value(data); - } - return value; + compileTemplate: function (str, data) { + // based on https://gist.github.com/padolsey/6008842 + str = str.replace(/"/g, '\\\"'); + str = str.replace(/\{ *([\w_]+) *\}/g, function (str, key) { + return '" + o["' + key + '"]' + (typeof data[key] === 'function' ? '(o)' : '') + ' + "'; }); + // jshint evil: true + return new Function('o', 'return "' + str + '";'); + }, + + template: function (str, data) { + var cache = L.Util._templateCache = L.Util._templateCache || {}; + cache[str] = cache[str] || L.Util.compileTemplate(str, data); + return cache[str](data); }, - isArray: function (obj) { + isArray: Array.isArray || function (obj) { return (Object.prototype.toString.call(obj) === '[object Array]'); }, @@ -337,7 +341,7 @@ L.Mixin.Events = { if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; } var events = this[eventsKey] = this[eventsKey] || {}, - contextId = context && L.stamp(context), + contextId = context && context !== this && L.stamp(context), i, len, event, type, indexKey, indexLenKey, typeIndex; // types can be a string of space-separated words @@ -350,7 +354,7 @@ L.Mixin.Events = { }; type = types[i]; - if (context) { + if (contextId) { // store listeners of a particular context in a separate hash (if it has an id) // gives a major performance boost when removing thousands of map layers @@ -397,7 +401,7 @@ L.Mixin.Events = { if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; } var events = this[eventsKey], - contextId = context && L.stamp(context), + contextId = context && context !== this && L.stamp(context), i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed; types = L.Util.splitWords(types); @@ -413,9 +417,10 @@ L.Mixin.Events = { // clear all listeners for a type if function isn't specified delete events[type]; delete events[indexKey]; + delete events[indexLenKey]; } else { - listeners = context && typeIndex ? typeIndex[contextId] : events[type]; + listeners = contextId && typeIndex ? typeIndex[contextId] : events[type]; if (listeners) { for (j = listeners.length - 1; j >= 0; j--) { @@ -458,7 +463,7 @@ L.Mixin.Events = { listeners = events[type].slice(); for (i = 0, len = listeners.length; i < len; i++) { - listeners[i].action.call(listeners[i].context || this, event); + listeners[i].action.call(listeners[i].context, event); } } @@ -470,7 +475,7 @@ L.Mixin.Events = { if (listeners) { for (i = 0, len = listeners.length; i < len; i++) { - listeners[i].action.call(listeners[i].context || this, event); + listeners[i].action.call(listeners[i].context, event); } } } @@ -506,9 +511,7 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent; (function () { - var ie = !!window.ActiveXObject, - ie6 = ie && !window.XMLHttpRequest, - ie7 = ie && !document.querySelector, + var ie = 'ActiveXObject' in window, ielt9 = ie && !document.addEventListener, // terrible browser detection to work around Safari / iOS / Android browser bugs @@ -518,10 +521,13 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent; phantomjs = ua.indexOf('phantom') !== -1, android = ua.indexOf('android') !== -1, android23 = ua.search('android [23]') !== -1, + gecko = ua.indexOf('gecko') !== -1, mobile = typeof orientation !== undefined + '', - msTouch = window.navigator && window.navigator.msPointerEnabled && - window.navigator.msMaxTouchPoints, + msPointer = window.navigator && window.navigator.msPointerEnabled && + window.navigator.msMaxTouchPoints && !window.PointerEvent, + pointer = (window.PointerEvent && window.navigator.pointerEnabled && window.navigator.maxTouchPoints) || + msPointer, retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) || ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') && window.matchMedia('(min-resolution:144dpi)').matches), @@ -541,8 +547,8 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent; var startName = 'ontouchstart'; - // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.MsTouch) or WebKit, etc. - if (msTouch || (startName in doc)) { + // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.Pointer) or WebKit, etc. + if (pointer || (startName in doc)) { return true; } @@ -568,10 +574,9 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent; L.Browser = { ie: ie, - ie6: ie6, - ie7: ie7, ielt9: ielt9, webkit: webkit, + gecko: gecko && !webkit && !window.opera && !ie, android: android, android23: android23, @@ -590,7 +595,8 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent; mobileOpera: mobile && window.opera, touch: touch, - msTouch: msTouch, + msPointer: msPointer, + pointer: pointer, retina: retina }; @@ -881,8 +887,7 @@ L.DomUtil = { el = element, docBody = document.body, docEl = document.documentElement, - pos, - ie7 = L.Browser.ie7; + pos; do { top += el.offsetTop || 0; @@ -929,19 +934,6 @@ L.DomUtil = { top -= el.scrollTop || 0; left -= el.scrollLeft || 0; - // webkit (and ie <= 7) handles RTL scrollLeft different to everyone else - // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/style/bidi.js - if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) { - left += el.scrollWidth - el.clientWidth; - - // ie7 shows the scrollbar by default and provides clientWidth counting it, so we - // need to add it back in if it is visible; scrollbar is on the left as we are RTL - if (ie7 && L.DomUtil.getStyle(el, 'overflow-y') !== 'hidden' && - L.DomUtil.getStyle(el, 'overflow') !== 'hidden') { - left += 17; - } - } - el = el.parentNode; } while (el); @@ -969,18 +961,44 @@ L.DomUtil = { }, hasClass: function (el, name) { - return (el.className.length > 0) && - new RegExp('(^|\\s)' + name + '(\\s|$)').test(el.className); + if (el.classList !== undefined) { + return el.classList.contains(name); + } + var className = L.DomUtil._getClass(el); + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); }, addClass: function (el, name) { - if (!L.DomUtil.hasClass(el, name)) { - el.className += (el.className ? ' ' : '') + name; + if (el.classList !== undefined) { + var classes = L.Util.splitWords(name); + for (var i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!L.DomUtil.hasClass(el, name)) { + var className = L.DomUtil._getClass(el); + L.DomUtil._setClass(el, (className ? className + ' ' : '') + name); } }, removeClass: function (el, name) { - el.className = L.Util.trim((' ' + el.className + ' ').replace(' ' + name + ' ', ' ')); + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + L.DomUtil._setClass(el, L.Util.trim((' ' + L.DomUtil._getClass(el) + ' ').replace(' ' + name + ' ', ' '))); + } + }, + + _setClass: function (el, name) { + if (el.className.baseVal === undefined) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } + }, + + _getClass: function (el) { + return el.className.baseVal === undefined ? el.className : el.className.baseVal; }, setOpacity: function (el, value) { @@ -1089,27 +1107,39 @@ L.DomUtil.TRANSITION_END = L.DomUtil.TRANSITION + 'End' : 'transitionend'; (function () { - var userSelectProperty = L.DomUtil.testProp( - ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); + if ('onselectstart' in document) { + L.extend(L.DomUtil, { + disableTextSelection: function () { + L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault); + }, + + enableTextSelection: function () { + L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault); + } + }); + } else { + var userSelectProperty = L.DomUtil.testProp( + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); + + L.extend(L.DomUtil, { + disableTextSelection: function () { + if (userSelectProperty) { + var style = document.documentElement.style; + this._userSelect = style[userSelectProperty]; + style[userSelectProperty] = 'none'; + } + }, + + enableTextSelection: function () { + if (userSelectProperty) { + document.documentElement.style[userSelectProperty] = this._userSelect; + delete this._userSelect; + } + } + }); + } L.extend(L.DomUtil, { - disableTextSelection: function () { - L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault); - if (userSelectProperty) { - var style = document.documentElement.style; - this._userSelect = style[userSelectProperty]; - style[userSelectProperty] = 'none'; - } - }, - - enableTextSelection: function () { - L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault); - if (userSelectProperty) { - document.documentElement.style[userSelectProperty] = this._userSelect; - delete this._userSelect; - } - }, - disableImageDrag: function () { L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault); }, @@ -1125,16 +1155,20 @@ L.DomUtil.TRANSITION_END = * L.LatLng represents a geographical point with latitude and longitude coordinates. */ -L.LatLng = function (rawLat, rawLng) { // (Number, Number) - var lat = parseFloat(rawLat), - lng = parseFloat(rawLng); +L.LatLng = function (lat, lng, alt) { // (Number, Number, Number) + lat = parseFloat(lat); + lng = parseFloat(lng); if (isNaN(lat) || isNaN(lng)) { - throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')'); + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); } this.lat = lat; this.lng = lng; + + if (alt !== undefined) { + this.alt = parseFloat(alt); + } }; L.extend(L.LatLng, { @@ -1198,7 +1232,11 @@ L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Numbe return a; } if (L.Util.isArray(a)) { - return new L.LatLng(a[0], a[1]); + if (typeof a[0] === 'number' || typeof a[0] === 'string') { + return new L.LatLng(a[0], a[1], a[2]); + } else { + return null; + } } if (a === undefined || a === null) { return a; @@ -1206,6 +1244,9 @@ L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Numbe if (typeof a === 'object' && 'lat' in a) { return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon); } + if (b === undefined) { + return null; + } return new L.LatLng(a, b); }; @@ -1230,8 +1271,9 @@ L.LatLngBounds.prototype = { extend: function (obj) { // (LatLng) or (LatLngBounds) if (!obj) { return this; } - if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) { - obj = L.latLng(obj); + var latLng = L.latLng(obj); + if (latLng !== null) { + obj = latLng; } else { obj = L.latLngBounds(obj); } @@ -1444,6 +1486,11 @@ L.CRS = { scale: function (zoom) { return 256 * Math.pow(2, zoom); + }, + + getSize: function (zoom) { + var s = this.scale(zoom); + return L.point(s, s); } }; @@ -1522,8 +1569,13 @@ L.Map = L.Class.extend({ initialize: function (id, options) { // (HTMLElement or String, Object) options = L.setOptions(this, options); + this._initContainer(id); this._initLayout(); + + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = L.bind(this._onResize, this); + this._initEvents(); if (options.maxBounds) { @@ -1550,11 +1602,16 @@ L.Map = L.Class.extend({ // replaced by animation-powered implementation in Map.PanAnimation.js setView: function (center, zoom) { + zoom = zoom === undefined ? this.getZoom() : zoom; this._resetView(L.latLng(center), this._limitZoom(zoom)); return this; }, setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = this._limitZoom(zoom); + return this; + } return this.setView(this.getCenter(), zoom, {zoom: options}); }, @@ -1592,6 +1649,8 @@ L.Map = L.Class.extend({ nePoint = this.project(bounds.getNorthEast(), zoom), center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); + zoom = options && options.maxZoom ? Math.min(options.maxZoom, zoom) : zoom; + return this.setView(center, zoom, options); }, @@ -1604,7 +1663,7 @@ L.Map = L.Class.extend({ }, panBy: function (offset) { // (Point) - // replaced with animated panBy in Map.Animation.js + // replaced with animated panBy in Map.PanAnimation.js this.fire('movestart'); this._rawPanBy(L.point(offset)); @@ -1613,63 +1672,29 @@ L.Map = L.Class.extend({ return this.fire('moveend'); }, - setMaxBounds: function (bounds, options) { + setMaxBounds: function (bounds) { bounds = L.latLngBounds(bounds); this.options.maxBounds = bounds; if (!bounds) { - this._boundsMinZoom = null; - this.off('moveend', this._panInsideMaxBounds, this); - return this; + return this.off('moveend', this._panInsideMaxBounds, this); } - var minZoom = this.getBoundsZoom(bounds, true); - - this._boundsMinZoom = minZoom; - if (this._loaded) { - if (this._zoom < minZoom) { - this.setView(bounds.getCenter(), minZoom, options); - } else { - this.panInsideBounds(bounds); - } + this._panInsideMaxBounds(); } - this.on('moveend', this._panInsideMaxBounds, this); - - return this; + return this.on('moveend', this._panInsideMaxBounds, this); }, - panInsideBounds: function (bounds) { - bounds = L.latLngBounds(bounds); + panInsideBounds: function (bounds, options) { + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, bounds); - var viewBounds = this.getPixelBounds(), - viewSw = viewBounds.getBottomLeft(), - viewNe = viewBounds.getTopRight(), - sw = this.project(bounds.getSouthWest()), - ne = this.project(bounds.getNorthEast()), - dx = 0, - dy = 0; + if (center.equals(newCenter)) { return this; } - if (viewNe.y < ne.y) { // north - dy = Math.ceil(ne.y - viewNe.y); - } - if (viewNe.x > ne.x) { // east - dx = Math.floor(ne.x - viewNe.x); - } - if (viewSw.y > sw.y) { // south - dy = Math.floor(sw.y - viewSw.y); - } - if (viewSw.x < sw.x) { // west - dx = Math.ceil(sw.x - viewSw.x); - } - - if (dx || dy) { - return this.panBy([dx, dy]); - } - - return this; + return this.panTo(newCenter, options); }, addLayer: function (layer) { @@ -1754,14 +1779,12 @@ L.Map = L.Class.extend({ this._sizeChanged = true; this._initialCenter = null; - if (this.options.maxBounds) { - this.setMaxBounds(this.options.maxBounds); - } - if (!this._loaded) { return this; } var newSize = this.getSize(), - offset = oldSize.subtract(newSize).divideBy(2).round(); + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); if (!offset.x && !offset.y) { return this; } @@ -1773,7 +1796,14 @@ L.Map = L.Class.extend({ this._rawPanBy(offset); } - this.fire('move').fire('moveend'); + this.fire('move'); + + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } } return this.fire('resize', { @@ -1784,7 +1814,7 @@ L.Map = L.Class.extend({ // TODO handler.addTo addHandler: function (name, HandlerClass) { - if (!HandlerClass) { return; } + if (!HandlerClass) { return this; } var handler = this[name] = new HandlerClass(this); @@ -1804,7 +1834,12 @@ L.Map = L.Class.extend({ this._initEvents('off'); - delete this._container._leaflet; + try { + // throws error in IE6-8 + delete this._container._leaflet; + } catch (e) { + this._container._leaflet = undefined; + } this._clearPanes(); if (this._clearControlPos) { @@ -1841,9 +1876,9 @@ L.Map = L.Class.extend({ }, getMinZoom: function () { - var z1 = this._layersMinZoom === undefined ? -Infinity : this._layersMinZoom, - z2 = this._boundsMinZoom === undefined ? -Infinity : this._boundsMinZoom; - return this.options.minZoom === undefined ? Math.max(z1, z2) : this.options.minZoom; + return this.options.minZoom === undefined ? + (this._layersMinZoom === undefined ? 0 : this._layersMinZoom) : + this.options.minZoom; }, getMaxZoom: function () { @@ -1995,6 +2030,7 @@ L.Map = L.Class.extend({ L.DomUtil.addClass(container, 'leaflet-container' + (L.Browser.touch ? ' leaflet-touch' : '') + (L.Browser.retina ? ' leaflet-retina' : '') + + (L.Browser.ielt9 ? ' leaflet-oldie' : '') + (this.options.fadeAnimation ? ' leaflet-fade-anim' : '')); var position = L.DomUtil.getStyle(container, 'position'); @@ -2165,12 +2201,14 @@ L.Map = L.Class.extend({ _onResize: function () { L.Util.cancelAnimFrame(this._resizeRequest); this._resizeRequest = L.Util.requestAnimFrame( - this.invalidateSize, this, false, this._container); + function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container); }, _onMouseClick: function (e) { - if (!this._loaded || (!e._simulated && this.dragging && this.dragging.moved()) || - L.DomEvent._skipped(e)) { return; } + if (!this._loaded || (!e._simulated && + ((this.dragging && this.dragging.moved()) || + (this.boxZoom && this.boxZoom.moved()))) || + L.DomEvent._skipped(e)) { return; } this.fire('preclick'); this._fireMouseEvent(e); @@ -2265,6 +2303,46 @@ L.Map = L.Class.extend({ return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); }, + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { + + if (!bounds) { return center; } + + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); + + return this.unproject(centerPoint.add(offset), zoom); + }, + + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } + + var viewBounds = this.getPixelBounds(), + newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var nwOffset = this.project(maxBounds.getNorthWest(), zoom).subtract(pxBounds.min), + seOffset = this.project(maxBounds.getSouthEast(), zoom).subtract(pxBounds.max), + + dx = this._rebound(nwOffset.x, -seOffset.x), + dy = this._rebound(nwOffset.y, -seOffset.y); + + return new L.Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + _limitZoom: function (zoom) { var min = this.getMinZoom(), max = this.getMaxZoom(); @@ -2345,9 +2423,9 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS, { transformation: (function () { var m = L.Projection.Mercator, r = m.R_MAJOR, - r2 = m.R_MINOR; + scale = 0.5 / (Math.PI * r); - return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5); + return new L.Transformation(scale, 0.5, -scale, 0.5); }()) }); @@ -2368,7 +2446,8 @@ L.TileLayer = L.Class.extend({ attribution: '', zoomOffset: 0, opacity: 1, - /* (undefined works too) + /* + maxNativeZoom: null, zIndex: null, tms: false, continuousWorld: false, @@ -2417,9 +2496,6 @@ L.TileLayer = L.Class.extend({ // create a container div for tiles this._initContainer(); - // create an image to clone for tiles - this._createTileProto(); - // set up events map.on({ 'viewreset': this._reset, @@ -2584,7 +2660,7 @@ L.TileLayer = L.Class.extend({ this._updateZIndex(); if (this._animated) { - var className = 'leaflet-tile-container leaflet-zoom-animated'; + var className = 'leaflet-tile-container'; this._bgBuffer = L.DomUtil.create('div', className, this._container); this._tileContainer = L.DomUtil.create('div', className, this._container); @@ -2622,13 +2698,27 @@ L.TileLayer = L.Class.extend({ this._initContainer(); }, + _getTileSize: function () { + var map = this._map, + zoom = map.getZoom() + this.options.zoomOffset, + zoomN = this.options.maxNativeZoom, + tileSize = this.options.tileSize; + + if (zoomN && zoom > zoomN) { + tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize); + } + + return tileSize; + }, + _update: function () { if (!this._map) { return; } - var bounds = this._map.getPixelBounds(), - zoom = this._map.getZoom(), - tileSize = this.options.tileSize; + var map = this._map, + bounds = map.getPixelBounds(), + zoom = map.getZoom(), + tileSize = this._getTileSize(); if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { return; @@ -2697,8 +2787,8 @@ L.TileLayer = L.Class.extend({ var limit = this._getWrapTileNum(); // don't load if exceeds world bounds - if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit)) || - tilePoint.y < 0 || tilePoint.y >= limit) { return false; } + if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit.x)) || + tilePoint.y < 0 || tilePoint.y >= limit.y) { return false; } } if (options.bounds) { @@ -2791,12 +2881,14 @@ L.TileLayer = L.Class.extend({ zoom = options.maxZoom - zoom; } - return zoom + options.zoomOffset; + zoom += options.zoomOffset; + + return options.maxNativeZoom ? Math.min(zoom, options.maxNativeZoom) : zoom; }, _getTilePos: function (tilePoint) { var origin = this._map.getPixelOrigin(), - tileSize = this.options.tileSize; + tileSize = this._getTileSize(); return tilePoint.multiplyBy(tileSize).subtract(origin); }, @@ -2813,8 +2905,9 @@ L.TileLayer = L.Class.extend({ }, _getWrapTileNum: function () { - // TODO refactor, limit is not valid for non-standard projections - return Math.pow(2, this._getZoomForUrl()); + var crs = this._map.options.crs, + size = crs.getSize(this._map.getZoom()); + return size.divideBy(this.options.tileSize); }, _adjustTilePoint: function (tilePoint) { @@ -2823,11 +2916,11 @@ L.TileLayer = L.Class.extend({ // wrap tile coordinates if (!this.options.continuousWorld && !this.options.noWrap) { - tilePoint.x = ((tilePoint.x % limit) + limit) % limit; + tilePoint.x = ((tilePoint.x % limit.x) + limit.x) % limit.x; } if (this.options.tms) { - tilePoint.y = limit - tilePoint.y - 1; + tilePoint.y = limit.y - tilePoint.y - 1; } tilePoint.z = this._getZoomForUrl(); @@ -2838,12 +2931,6 @@ L.TileLayer = L.Class.extend({ return this.options.subdomains[index]; }, - _createTileProto: function () { - var img = this._tileImg = L.DomUtil.create('img', 'leaflet-tile'); - img.style.width = img.style.height = this.options.tileSize + 'px'; - img.galleryimg = 'no'; - }, - _getTile: function () { if (this.options.reuseTiles && this._unusedTiles.length > 0) { var tile = this._unusedTiles.pop(); @@ -2857,12 +2944,20 @@ L.TileLayer = L.Class.extend({ _resetTile: function (/*tile*/) {}, _createTile: function () { - var tile = this._tileImg.cloneNode(false); + var tile = L.DomUtil.create('img', 'leaflet-tile'); + tile.style.width = tile.style.height = this._getTileSize() + 'px'; + tile.galleryimg = 'no'; + tile.onselectstart = tile.onmousemove = L.Util.falseFn; if (L.Browser.ielt9 && this.options.opacity !== undefined) { L.DomUtil.setOpacity(tile, this.options.opacity); } + // without this hack, tiles disappear after zoom on Chrome for Android + // https://github.com/Leaflet/Leaflet/issues/2078 + if (L.Browser.mobileWebkit3d) { + tile.style.WebkitBackfaceVisibility = 'hidden'; + } return tile; }, @@ -2873,10 +2968,20 @@ L.TileLayer = L.Class.extend({ this._adjustTilePoint(tilePoint); tile.src = this.getTileUrl(tilePoint); + + this.fire('tileloadstart', { + tile: tile, + url: tile.src + }); }, _tileLoaded: function () { this._tilesToLoad--; + + if (this._animated) { + L.DomUtil.addClass(this._tileContainer, 'leaflet-zoom-animated'); + } + if (!this._tilesToLoad) { this.fire('load'); @@ -2971,13 +3076,15 @@ L.TileLayer.WMS = L.TileLayer.extend({ this._crs = this.options.crs || map.options.crs; - var projectionKey = parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs'; + this._wmsVersion = parseFloat(this.wmsParams.version); + + var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; this.wmsParams[projectionKey] = this._crs.code; L.TileLayer.prototype.onAdd.call(this, map); }, - getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String + getTileUrl: function (tilePoint) { // (Point, Number) -> String var map = this._map, tileSize = this.options.tileSize, @@ -2985,10 +3092,11 @@ L.TileLayer.WMS = L.TileLayer.extend({ nwPoint = tilePoint.multiplyBy(tileSize), sePoint = nwPoint.add([tileSize, tileSize]), - nw = this._crs.project(map.unproject(nwPoint, zoom)), - se = this._crs.project(map.unproject(sePoint, zoom)), - - bbox = [nw.x, se.y, se.x, nw.y].join(','), + nw = this._crs.project(map.unproject(nwPoint, tilePoint.z)), + se = this._crs.project(map.unproject(sePoint, tilePoint.z)), + bbox = this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ? + [se.y, nw.x, nw.y, se.x].join(',') : + [nw.x, se.y, se.x, nw.y].join(','), url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)}); @@ -3031,7 +3139,7 @@ L.TileLayer.Canvas = L.TileLayer.extend({ this._reset({hard: true}); this._update(); } - + for (var i in this._tiles) { this._redrawTile(this._tiles[i]); } @@ -3042,13 +3150,9 @@ L.TileLayer.Canvas = L.TileLayer.extend({ this.drawTile(tile, tile._tilePoint, this._map._zoom); }, - _createTileProto: function () { - var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile'); - proto.width = proto.height = this.options.tileSize; - }, - _createTile: function () { - var tile = this._canvasProto.cloneNode(false); + var tile = L.DomUtil.create('canvas', 'leaflet-tile'); + tile.width = tile.height = this.options.tileSize; tile.onselectstart = tile.onmousemove = L.Util.falseFn; return tile; }, @@ -3152,6 +3256,15 @@ L.ImageOverlay = L.Class.extend({ return this; }, + setUrl: function (url) { + this._url = url; + this._image.src = this._url; + }, + + getAttribution: function () { + return this.options.attribution; + }, + _initImage: function () { this._image = L.DomUtil.create('img', 'leaflet-image-layer'); @@ -3295,19 +3408,8 @@ L.Icon = L.Class.extend({ }, _createImg: function (src, el) { - - if (!L.Browser.ie6) { - if (!el) { - el = document.createElement('img'); - } - el.src = src; - } else { - if (!el) { - el = document.createElement('div'); - } - el.style.filter = - 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")'; - } + el = el || document.createElement('img'); + el.src = src; return el; }, @@ -3388,6 +3490,7 @@ L.Marker = L.Class.extend({ options: { icon: new L.Icon.Default(), title: '', + alt: '', clickable: true, draggable: false, keyboard: true, @@ -3409,6 +3512,7 @@ L.Marker = L.Class.extend({ this._initIcon(); this.update(); + this.fire('add'); if (map.options.zoomAnimation && map.options.markerZoomAnimation) { map.on('zoomanim', this._animateZoom, this); @@ -3466,6 +3570,10 @@ L.Marker = L.Class.extend({ this.update(); } + if (this._popup) { + this.bindPopup(this._popup); + } + return this; }, @@ -3497,6 +3605,10 @@ L.Marker = L.Class.extend({ if (options.title) { icon.title = options.title; } + + if (options.alt) { + icon.alt = options.alt; + } } L.DomUtil.addClass(icon, classToAdd); @@ -3581,7 +3693,7 @@ L.Marker = L.Class.extend({ }, _animateZoom: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); this._setPos(pos); }, @@ -3662,7 +3774,7 @@ L.Marker = L.Class.extend({ if (this._map) { this._updateOpacity(); } - + return this; }, @@ -3748,11 +3860,13 @@ L.Popup = L.Class.extend({ options: { minWidth: 50, maxWidth: 300, - maxHeight: null, + // maxHeight: null, autoPan: true, closeButton: true, offset: [0, 7], autoPanPadding: [5, 5], + // autoPanPaddingTopLeft: null, + // autoPanPaddingBottomRight: null, keepInView: false, className: '', zoomAnimation: true @@ -3772,7 +3886,6 @@ L.Popup = L.Class.extend({ if (!this._container) { this._initLayout(); } - this._updateContent(); var animFade = map.options.fadeAnimation; @@ -3783,7 +3896,7 @@ L.Popup = L.Class.extend({ map.on(this._getEvents(), this); - this._update(); + this.update(); if (animFade) { L.DomUtil.setOpacity(this._container, 1); @@ -3830,18 +3943,43 @@ L.Popup = L.Class.extend({ } }, + getLatLng: function () { + return this._latlng; + }, + setLatLng: function (latlng) { this._latlng = L.latLng(latlng); - this._update(); + if (this._map) { + this._updatePosition(); + this._adjustPan(); + } return this; }, + getContent: function () { + return this._content; + }, + setContent: function (content) { this._content = content; - this._update(); + this.update(); return this; }, + update: function () { + if (!this._map) { return; } + + this._container.style.visibility = 'hidden'; + + this._updateContent(); + this._updateLayout(); + this._updatePosition(); + + this._container.style.visibility = ''; + + this._adjustPan(); + }, + _getEvents: function () { var events = { viewreset: this._updatePosition @@ -3888,27 +4026,14 @@ L.Popup = L.Class.extend({ L.DomEvent.disableClickPropagation(wrapper); this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper); - L.DomEvent.on(this._contentNode, 'mousewheel', L.DomEvent.stopPropagation); - L.DomEvent.on(this._contentNode, 'MozMousePixelScroll', L.DomEvent.stopPropagation); + + L.DomEvent.disableScrollPropagation(this._contentNode); L.DomEvent.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation); + this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container); this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer); }, - _update: function () { - if (!this._map) { return; } - - this._container.style.visibility = 'hidden'; - - this._updateContent(); - this._updateLayout(); - this._updatePosition(); - - this._container.style.visibility = ''; - - this._adjustPan(); - }, - _updateContent: function () { if (!this._content) { return; } @@ -3993,21 +4118,23 @@ L.Popup = L.Class.extend({ var containerPos = map.layerPointToContainerPoint(layerPos), padding = L.point(this.options.autoPanPadding), + paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding), + paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding), size = map.getSize(), dx = 0, dy = 0; - if (containerPos.x + containerWidth > size.x) { // right - dx = containerPos.x + containerWidth - size.x + padding.x; + if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right + dx = containerPos.x + containerWidth - size.x + paddingBR.x; } - if (containerPos.x - dx < 0) { // left - dx = containerPos.x - padding.x; + if (containerPos.x - dx - paddingTL.x < 0) { // left + dx = containerPos.x - paddingTL.x; } - if (containerPos.y + containerHeight > size.y) { // bottom - dy = containerPos.y + containerHeight - size.y + padding.y; + if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom + dy = containerPos.y + containerHeight - size.y + paddingBR.y; } - if (containerPos.y - dy < 0) { // top - dy = containerPos.y - padding.y; + if (containerPos.y - dy - paddingTL.y < 0) { // top + dy = containerPos.y - paddingTL.y; } if (dx || dy) { @@ -4102,11 +4229,12 @@ L.Marker.include({ options = L.extend({offset: anchor}, options); - if (!this._popup) { + if (!this._popupHandlersAdded) { this .on('click', this.togglePopup, this) .on('remove', this.closePopup, this) .on('move', this._movePopup, this); + this._popupHandlersAdded = true; } if (content instanceof L.Popup) { @@ -4131,13 +4259,18 @@ L.Marker.include({ if (this._popup) { this._popup = null; this - .off('click', this.togglePopup) - .off('remove', this.closePopup) - .off('move', this._movePopup); + .off('click', this.togglePopup, this) + .off('remove', this.closePopup, this) + .off('move', this._movePopup, this); + this._popupHandlersAdded = false; } return this; }, + getPopup: function () { + return this._popup; + }, + _movePopup: function (e) { this._popup.setLatLng(e.latlng); } @@ -4278,7 +4411,9 @@ L.FeatureGroup = L.LayerGroup.extend({ return this; } - layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this); + if ('on' in layer) { + layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this); + } L.LayerGroup.prototype.addLayer.call(this, layer); @@ -4314,6 +4449,15 @@ L.FeatureGroup = L.LayerGroup.extend({ return this.invoke('bindPopup', content, options); }, + openPopup: function (latlng) { + // open popup on the first layer + for (var id in this._layers) { + this._layers[id].openPopup(latlng); + break; + } + return this; + }, + setStyle: function (style) { return this.invoke('setStyle', style); }, @@ -4337,11 +4481,10 @@ L.FeatureGroup = L.LayerGroup.extend({ }, _propagateEvent: function (e) { - if (!e.layer) { - e.layer = e.target; - } - e.target = this; - + e = L.extend({}, e, { + layer: e.target, + target: this + }); this.fire(e.type, e); } }); @@ -4373,6 +4516,8 @@ L.Path = L.Class.extend({ stroke: true, color: '#0033ff', dashArray: null, + lineCap: null, + lineJoin: null, weight: 5, opacity: 0.5, @@ -4522,6 +4667,11 @@ L.Path = L.Path.extend({ this._container = this._createElement('g'); this._path = this._createElement('path'); + + if (this.options.className) { + L.DomUtil.addClass(this._path, this.options.className); + } + this._container.appendChild(this._path); }, @@ -4552,6 +4702,12 @@ L.Path = L.Path.extend({ } else { this._path.removeAttribute('stroke-dasharray'); } + if (this.options.lineCap) { + this._path.setAttribute('stroke-linecap', this.options.lineCap); + } + if (this.options.lineJoin) { + this._path.setAttribute('stroke-linejoin', this.options.lineJoin); + } } else { this._path.setAttribute('stroke', 'none'); } @@ -4576,7 +4732,7 @@ L.Path = L.Path.extend({ _initEvents: function () { if (this.options.clickable) { if (L.Browser.svg || !L.Browser.vml) { - this._path.setAttribute('class', 'leaflet-clickable'); + L.DomUtil.addClass(this._path, 'leaflet-clickable'); } L.DomEvent.on(this._container, 'click', this._onMouseClick, this); @@ -4626,14 +4782,14 @@ L.Map.include({ this._panes.overlayPane.appendChild(this._pathRoot); if (this.options.zoomAnimation && L.Browser.any3d) { - this._pathRoot.setAttribute('class', ' leaflet-zoom-animated'); + L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-animated'); this.on({ 'zoomanim': this._animatePathZoom, 'zoomend': this._endPathZoom }); } else { - this._pathRoot.setAttribute('class', ' leaflet-zoom-hide'); + L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-hide'); } this.on('moveend', this._updateSvgViewport); @@ -4800,10 +4956,14 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ _initPath: function () { var container = this._container = this._createElement('shape'); - L.DomUtil.addClass(container, 'leaflet-vml-shape'); + + L.DomUtil.addClass(container, 'leaflet-vml-shape' + + (this.options.className ? ' ' + this.options.className : '')); + if (this.options.clickable) { L.DomUtil.addClass(container, 'leaflet-clickable'); } + container.coordsize = '1 1'; this._path = this._createElement('path'); @@ -4836,12 +4996,18 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ stroke.opacity = options.opacity; if (options.dashArray) { - stroke.dashStyle = options.dashArray instanceof Array ? + stroke.dashStyle = L.Util.isArray(options.dashArray) ? options.dashArray.join(' ') : options.dashArray.replace(/( *, *)/g, ' '); } else { stroke.dashStyle = ''; } + if (options.lineCap) { + stroke.endcap = options.lineCap.replace('butt', 'flat'); + } + if (options.lineJoin) { + stroke.joinstyle = options.lineJoin; + } } else if (stroke) { container.removeChild(stroke); @@ -5521,10 +5687,12 @@ L.Polygon = L.Polyline.extend({ }, initialize: function (latlngs, options) { - var i, len, hole; - L.Polyline.prototype.initialize.call(this, latlngs, options); + this._initWithHoles(latlngs); + }, + _initWithHoles: function (latlngs) { + var i, len, hole; if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { this._latlngs = this._convertLatLngs(latlngs[0]); this._holes = latlngs.slice(1); @@ -5565,6 +5733,15 @@ L.Polygon = L.Polyline.extend({ } }, + setLatLngs: function (latlngs) { + if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { + this._initWithHoles(latlngs); + return this.redraw(); + } else { + return L.Polyline.prototype.setLatLngs.call(this, latlngs); + } + }, + _clipPoints: function () { var points = this._originalPoints, newParts = []; @@ -5806,9 +5983,20 @@ L.CircleMarker = L.Circle.extend({ this.setRadius(this.options.radius); }, + setLatLng: function (latlng) { + L.Circle.prototype.setLatLng.call(this, latlng); + if (this._popup && this._popup._isOpen) { + this._popup.setLatLng(latlng); + } + }, + setRadius: function (radius) { this.options.radius = this._radius = radius; return this.redraw(); + }, + + getRadius: function () { + return this._radius; } }); @@ -5954,7 +6142,7 @@ L.GeoJSON = L.FeatureGroup.extend({ if (options.filter && !options.filter(geojson)) { return; } - var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng); + var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng, options); layer.feature = L.GeoJSON.asFeature(geojson); layer.defaultOptions = layer.options; @@ -5994,11 +6182,11 @@ L.GeoJSON = L.FeatureGroup.extend({ }); L.extend(L.GeoJSON, { - geometryToLayer: function (geojson, pointToLayer, coordsToLatLng) { + geometryToLayer: function (geojson, pointToLayer, coordsToLatLng, vectorOptions) { var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, coords = geometry.coordinates, layers = [], - latlng, latlngs, i, len, layer; + latlng, latlngs, i, len; coordsToLatLng = coordsToLatLng || this.coordsToLatLng; @@ -6010,37 +6198,37 @@ L.extend(L.GeoJSON, { case 'MultiPoint': for (i = 0, len = coords.length; i < len; i++) { latlng = coordsToLatLng(coords[i]); - layer = pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng); - layers.push(layer); + layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng)); } return new L.FeatureGroup(layers); case 'LineString': latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng); - return new L.Polyline(latlngs); + return new L.Polyline(latlngs, vectorOptions); case 'Polygon': + if (coords.length === 2 && !coords[1].length) { + throw new Error('Invalid GeoJSON object.'); + } latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); - return new L.Polygon(latlngs); + return new L.Polygon(latlngs, vectorOptions); case 'MultiLineString': latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); - return new L.MultiPolyline(latlngs); + return new L.MultiPolyline(latlngs, vectorOptions); case 'MultiPolygon': latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng); - return new L.MultiPolygon(latlngs); + return new L.MultiPolygon(latlngs, vectorOptions); case 'GeometryCollection': for (i = 0, len = geometry.geometries.length; i < len; i++) { - layer = this.geometryToLayer({ + layers.push(this.geometryToLayer({ geometry: geometry.geometries[i], type: 'Feature', properties: geojson.properties - }, pointToLayer, coordsToLatLng); - - layers.push(layer); + }, pointToLayer, coordsToLatLng, vectorOptions)); } return new L.FeatureGroup(layers); @@ -6050,7 +6238,7 @@ L.extend(L.GeoJSON, { }, coordsToLatLng: function (coords) { // (Array[, Boolean]) -> LatLng - return new L.LatLng(coords[1], coords[0]); + return new L.LatLng(coords[1], coords[0], coords[2]); }, coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) { // (Array[, Number, Function]) -> Array @@ -6068,8 +6256,13 @@ L.extend(L.GeoJSON, { return latlngs; }, - latLngToCoords: function (latLng) { - return [latLng.lng, latLng.lat]; + latLngToCoords: function (latlng) { + var coords = [latlng.lng, latlng.lat]; + + if (latlng.alt !== undefined) { + coords.push(latlng.alt); + } + return coords; }, latLngsToCoords: function (latLngs) { @@ -6144,43 +6337,58 @@ L.Polygon.include({ }); (function () { - function includeMulti(Klass, type) { - Klass.include({ - toGeoJSON: function () { - var coords = []; + function multiToGeoJSON(type) { + return function () { + var coords = []; - this.eachLayer(function (layer) { - coords.push(layer.toGeoJSON().geometry.coordinates); - }); + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON().geometry.coordinates); + }); - return L.GeoJSON.getFeature(this, { - type: type, - coordinates: coords - }); - } - }); + return L.GeoJSON.getFeature(this, { + type: type, + coordinates: coords + }); + }; } - includeMulti(L.MultiPolyline, 'MultiLineString'); - includeMulti(L.MultiPolygon, 'MultiPolygon'); -}()); + L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')}); + L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')}); -L.LayerGroup.include({ - toGeoJSON: function () { - var features = []; + L.LayerGroup.include({ + toGeoJSON: function () { - this.eachLayer(function (layer) { - if (layer.toGeoJSON) { - features.push(L.GeoJSON.asFeature(layer.toGeoJSON())); + var geometry = this.feature && this.feature.geometry, + jsons = [], + json; + + if (geometry && geometry.type === 'MultiPoint') { + return multiToGeoJSON('MultiPoint').call(this); } - }); - return { - type: 'FeatureCollection', - features: features - }; - } -}); + var isGeometryCollection = geometry && geometry.type === 'GeometryCollection'; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + json = layer.toGeoJSON(); + jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json)); + } + }); + + if (isGeometryCollection) { + return L.GeoJSON.getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } + }); +}()); L.geoJson = function (geojson, options) { return new L.GeoJSON(geojson, options); @@ -6205,8 +6413,8 @@ L.DomEvent = { return fn.call(context || obj, e || L.DomEvent._getEvent()); }; - if (L.Browser.msTouch && type.indexOf('touch') === 0) { - return this.addMsTouchListener(obj, type, handler, id); + if (L.Browser.pointer && type.indexOf('touch') === 0) { + return this.addPointerListener(obj, type, handler, id); } if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { this.addDoubleTapListener(obj, handler, id); @@ -6258,8 +6466,8 @@ L.DomEvent = { if (!handler) { return this; } - if (L.Browser.msTouch && type.indexOf('touch') === 0) { - this.removeMsTouchListener(obj, type, id); + if (L.Browser.pointer && type.indexOf('touch') === 0) { + this.removePointerListener(obj, type, id); } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) { this.removeDoubleTapListener(obj, id); @@ -6290,19 +6498,29 @@ L.DomEvent = { } else { e.cancelBubble = true; } + L.DomEvent._skipped(e); + return this; }, + disableScrollPropagation: function (el) { + var stop = L.DomEvent.stopPropagation; + + return L.DomEvent + .on(el, 'mousewheel', stop) + .on(el, 'MozMousePixelScroll', stop); + }, + disableClickPropagation: function (el) { var stop = L.DomEvent.stopPropagation; for (var i = L.Draggable.START.length - 1; i >= 0; i--) { - L.DomEvent.addListener(el, L.Draggable.START[i], stop); + L.DomEvent.on(el, L.Draggable.START[i], stop); } return L.DomEvent - .addListener(el, 'click', L.DomEvent._fakeStop) - .addListener(el, 'dblclick', stop); + .on(el, 'click', L.DomEvent._fakeStop) + .on(el, 'dblclick', stop); }, preventDefault: function (e) { @@ -6316,34 +6534,31 @@ L.DomEvent = { }, stop: function (e) { - return L.DomEvent.preventDefault(e).stopPropagation(e); + return L.DomEvent + .preventDefault(e) + .stopPropagation(e); }, getMousePosition: function (e, container) { - - var ie7 = L.Browser.ie7, - body = document.body, + var body = document.body, docEl = document.documentElement, - x = e.pageX ? e.pageX - body.scrollLeft - docEl.scrollLeft: e.clientX, + //gecko makes scrollLeft more negative as you scroll in rtl, other browsers don't + //ref: https://code.google.com/p/closure-library/source/browse/closure/goog/style/bidi.js + x = L.DomUtil.documentIsLtr() ? + (e.pageX ? e.pageX - body.scrollLeft - docEl.scrollLeft : e.clientX) : + (L.Browser.gecko ? e.pageX - body.scrollLeft - docEl.scrollLeft : + e.pageX ? e.pageX - body.scrollLeft + docEl.scrollLeft : e.clientX), y = e.pageY ? e.pageY - body.scrollTop - docEl.scrollTop: e.clientY, - pos = new L.Point(x, y), - rect = container.getBoundingClientRect(), - left = rect.left - container.clientLeft, - top = rect.top - container.clientTop; - - // webkit (and ie <= 7) handles RTL scrollLeft different to everyone else - // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/style/bidi.js - if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) { - left += container.scrollWidth - container.clientWidth; + pos = new L.Point(x, y); - // ie7 shows the scrollbar by default and provides clientWidth counting it, so we - // need to add it back in if it is visible; scrollbar is on the left as we are RTL - if (ie7 && L.DomUtil.getStyle(container, 'overflow-y') !== 'hidden' && - L.DomUtil.getStyle(container, 'overflow') !== 'hidden') { - left += 17; - } + if (!container) { + return pos; } + var rect = container.getBoundingClientRect(), + left = rect.left - container.clientLeft, + top = rect.top - container.clientTop; + return pos._subtract(new L.Point(left, top)); }, @@ -6443,11 +6658,13 @@ L.Draggable = L.Class.extend({ END: { mousedown: 'mouseup', touchstart: 'touchend', + pointerdown: 'touchend', MSPointerDown: 'touchend' }, MOVE: { mousedown: 'mousemove', touchstart: 'touchmove', + pointerdown: 'touchmove', MSPointerDown: 'touchmove' } }, @@ -6479,28 +6696,21 @@ L.Draggable = L.Class.extend({ }, _onDown: function (e) { + this._moved = false; + if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } - L.DomEvent - .stopPropagation(e); + L.DomEvent.stopPropagation(e); if (L.Draggable._disabled) { return; } L.DomUtil.disableImageDrag(); L.DomUtil.disableTextSelection(); - var first = e.touches ? e.touches[0] : e, - el = first.target; - - // if touching a link, highlight it - if (L.Browser.touch && el.tagName.toLowerCase() === 'a') { - L.DomUtil.addClass(el, 'leaflet-active'); - } - - this._moved = false; - if (this._moving) { return; } + var first = e.touches ? e.touches[0] : e; + this._startPoint = new L.Point(first.clientX, first.clientY); this._startPos = this._newPos = L.DomUtil.getPosition(this._element); @@ -6510,7 +6720,10 @@ L.Draggable = L.Class.extend({ }, _onMove: function (e) { - if (e.touches && e.touches.length > 1) { return; } + if (e.touches && e.touches.length > 1) { + this._moved = true; + return; + } var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), newPoint = new L.Point(first.clientX, first.clientY), @@ -6526,9 +6739,8 @@ L.Draggable = L.Class.extend({ this._moved = true; this._startPos = L.DomUtil.getPosition(this._element).subtract(offset); - if (!L.Browser.touch) { - L.DomUtil.addClass(document.body, 'leaflet-dragging'); - } + L.DomUtil.addClass(document.body, 'leaflet-dragging'); + L.DomUtil.addClass((e.target || e.srcElement), 'leaflet-drag-target'); } this._newPos = this._startPos.add(offset); @@ -6544,10 +6756,9 @@ L.Draggable = L.Class.extend({ this.fire('drag'); }, - _onUp: function () { - if (!L.Browser.touch) { - L.DomUtil.removeClass(document.body, 'leaflet-dragging'); - } + _onUp: function (e) { + L.DomUtil.removeClass(document.body, 'leaflet-dragging'); + L.DomUtil.removeClass((e.target || e.srcElement), 'leaflet-drag-target'); for (var i in L.Draggable.MOVE) { L.DomEvent @@ -6562,7 +6773,9 @@ L.Draggable = L.Class.extend({ // ensure drag is not fired after dragend L.Util.cancelAnimFrame(this._animRequest); - this.fire('dragend'); + this.fire('dragend', { + distance: this._newPos.distanceTo(this._startPos) + }); } this._moving = false; @@ -6634,7 +6847,7 @@ L.Map.Drag = L.Handler.extend({ this._draggable.on('predrag', this._onPreDrag, this); map.on('viewreset', this._onViewReset, this); - this._onViewReset(); + map.whenReady(this._onViewReset, this); } } this._draggable.enable(); @@ -6706,14 +6919,14 @@ L.Map.Drag = L.Handler.extend({ this._draggable._newPos.x = newX; }, - _onDragEnd: function () { + _onDragEnd: function (e) { var map = this._map, options = map.options, delay = +new Date() - this._lastTime, noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; - map.fire('dragend'); + map.fire('dragend', e); if (noInertia) { map.fire('moveend'); @@ -6737,6 +6950,8 @@ L.Map.Drag = L.Handler.extend({ map.fire('moveend'); } else { + offset = map._limitOffset(offset, map.options.maxBounds); + L.Util.requestAnimFrame(function () { map.panBy(offset, { duration: decelerationDuration, @@ -6771,7 +6986,7 @@ L.Map.DoubleClickZoom = L.Handler.extend({ _onDoubleClick: function (e) { var map = this._map, - zoom = map.getZoom() + 1; + zoom = map.getZoom() + (e.originalEvent.shiftKey ? -1 : 1); if (map.options.doubleClickZoom === 'center') { map.setZoom(zoom); @@ -6854,8 +7069,8 @@ L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); L.extend(L.DomEvent, { - _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart', - _touchend: L.Browser.msTouch ? 'MSPointerUp' : 'touchend', + _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart', + _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend', // inspired by Zepto touch code by Thomas Fuchs addDoubleTapListener: function (obj, handler, id) { @@ -6871,7 +7086,7 @@ L.extend(L.DomEvent, { function onTouchStart(e) { var count; - if (L.Browser.msTouch) { + if (L.Browser.pointer) { trackedTouches.push(e.pointerId); count = trackedTouches.length; } else { @@ -6890,7 +7105,7 @@ L.extend(L.DomEvent, { } function onTouchEnd(e) { - if (L.Browser.msTouch) { + if (L.Browser.pointer) { var idx = trackedTouches.indexOf(e.pointerId); if (idx === -1) { return; @@ -6899,7 +7114,7 @@ L.extend(L.DomEvent, { } if (doubleTap) { - if (L.Browser.msTouch) { + if (L.Browser.pointer) { // work around .type being readonly with MSPointer* events var newTouch = { }, prop; @@ -6923,15 +7138,15 @@ L.extend(L.DomEvent, { obj[pre + touchstart + id] = onTouchStart; obj[pre + touchend + id] = onTouchEnd; - // on msTouch we need to listen on the document, otherwise a drag starting on the map and moving off screen + // on pointer we need to listen on the document, otherwise a drag starting on the map and moving off screen // will not come through to us, so we will lose track of how many touches are ongoing - var endElement = L.Browser.msTouch ? document.documentElement : obj; + var endElement = L.Browser.pointer ? document.documentElement : obj; obj.addEventListener(touchstart, onTouchStart, false); endElement.addEventListener(touchend, onTouchEnd, false); - if (L.Browser.msTouch) { - endElement.addEventListener('MSPointerCancel', onTouchEnd, false); + if (L.Browser.pointer) { + endElement.addEventListener(L.DomEvent.POINTER_CANCEL, onTouchEnd, false); } return this; @@ -6941,11 +7156,12 @@ L.extend(L.DomEvent, { var pre = '_leaflet_'; obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false); - (L.Browser.msTouch ? document.documentElement : obj).removeEventListener( + (L.Browser.pointer ? document.documentElement : obj).removeEventListener( this._touchend, obj[pre + this._touchend + id], false); - if (L.Browser.msTouch) { - document.documentElement.removeEventListener('MSPointerCancel', obj[pre + this._touchend + id], false); + if (L.Browser.pointer) { + document.documentElement.removeEventListener(L.DomEvent.POINTER_CANCEL, obj[pre + this._touchend + id], + false); } return this; @@ -6959,81 +7175,90 @@ L.extend(L.DomEvent, { L.extend(L.DomEvent, { - _msTouches: [], - _msDocumentListener: false, + //static + POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown', + POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove', + POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup', + POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel', + + _pointers: [], + _pointerDocumentListener: false, - // Provides a touch events wrapper for msPointer events. + // Provides a touch events wrapper for (ms)pointer events. // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019 + //ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 - addMsTouchListener: function (obj, type, handler, id) { + addPointerListener: function (obj, type, handler, id) { switch (type) { case 'touchstart': - return this.addMsTouchListenerStart(obj, type, handler, id); + return this.addPointerListenerStart(obj, type, handler, id); case 'touchend': - return this.addMsTouchListenerEnd(obj, type, handler, id); + return this.addPointerListenerEnd(obj, type, handler, id); case 'touchmove': - return this.addMsTouchListenerMove(obj, type, handler, id); + return this.addPointerListenerMove(obj, type, handler, id); default: throw 'Unknown touch event type'; } }, - addMsTouchListenerStart: function (obj, type, handler, id) { + addPointerListenerStart: function (obj, type, handler, id) { var pre = '_leaflet_', - touches = this._msTouches; + pointers = this._pointers; var cb = function (e) { + L.DomEvent.preventDefault(e); + var alreadyInArray = false; - for (var i = 0; i < touches.length; i++) { - if (touches[i].pointerId === e.pointerId) { + for (var i = 0; i < pointers.length; i++) { + if (pointers[i].pointerId === e.pointerId) { alreadyInArray = true; break; } } if (!alreadyInArray) { - touches.push(e); + pointers.push(e); } - e.touches = touches.slice(); + e.touches = pointers.slice(); e.changedTouches = [e]; handler(e); }; obj[pre + 'touchstart' + id] = cb; - obj.addEventListener('MSPointerDown', cb, false); + obj.addEventListener(this.POINTER_DOWN, cb, false); - // need to also listen for end events to keep the _msTouches list accurate + // need to also listen for end events to keep the _pointers list accurate // this needs to be on the body and never go away - if (!this._msDocumentListener) { + if (!this._pointerDocumentListener) { var internalCb = function (e) { - for (var i = 0; i < touches.length; i++) { - if (touches[i].pointerId === e.pointerId) { - touches.splice(i, 1); + for (var i = 0; i < pointers.length; i++) { + if (pointers[i].pointerId === e.pointerId) { + pointers.splice(i, 1); break; } } }; //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there - document.documentElement.addEventListener('MSPointerUp', internalCb, false); - document.documentElement.addEventListener('MSPointerCancel', internalCb, false); + document.documentElement.addEventListener(this.POINTER_UP, internalCb, false); + document.documentElement.addEventListener(this.POINTER_CANCEL, internalCb, false); - this._msDocumentListener = true; + this._pointerDocumentListener = true; } return this; }, - addMsTouchListenerMove: function (obj, type, handler, id) { + addPointerListenerMove: function (obj, type, handler, id) { var pre = '_leaflet_', - touches = this._msTouches; + touches = this._pointers; function cb(e) { // don't fire touch moves when mouse isn't down - if (e.pointerType === e.MSPOINTER_TYPE_MOUSE && e.buttons === 0) { return; } + if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } for (var i = 0; i < touches.length; i++) { if (touches[i].pointerId === e.pointerId) { @@ -7049,14 +7274,14 @@ L.extend(L.DomEvent, { } obj[pre + 'touchmove' + id] = cb; - obj.addEventListener('MSPointerMove', cb, false); + obj.addEventListener(this.POINTER_MOVE, cb, false); return this; }, - addMsTouchListenerEnd: function (obj, type, handler, id) { + addPointerListenerEnd: function (obj, type, handler, id) { var pre = '_leaflet_', - touches = this._msTouches; + touches = this._pointers; var cb = function (e) { for (var i = 0; i < touches.length; i++) { @@ -7073,26 +7298,26 @@ L.extend(L.DomEvent, { }; obj[pre + 'touchend' + id] = cb; - obj.addEventListener('MSPointerUp', cb, false); - obj.addEventListener('MSPointerCancel', cb, false); + obj.addEventListener(this.POINTER_UP, cb, false); + obj.addEventListener(this.POINTER_CANCEL, cb, false); return this; }, - removeMsTouchListener: function (obj, type, id) { + removePointerListener: function (obj, type, id) { var pre = '_leaflet_', cb = obj[pre + type + id]; switch (type) { case 'touchstart': - obj.removeEventListener('MSPointerDown', cb, false); + obj.removeEventListener(this.POINTER_DOWN, cb, false); break; case 'touchmove': - obj.removeEventListener('MSPointerMove', cb, false); + obj.removeEventListener(this.POINTER_MOVE, cb, false); break; case 'touchend': - obj.removeEventListener('MSPointerUp', cb, false); - obj.removeEventListener('MSPointerCancel', cb, false); + obj.removeEventListener(this.POINTER_UP, cb, false); + obj.removeEventListener(this.POINTER_CANCEL, cb, false); break; } @@ -7106,7 +7331,8 @@ L.extend(L.DomEvent, { */ L.Map.mergeOptions({ - touchZoom: L.Browser.touch && !L.Browser.android23 + touchZoom: L.Browser.touch && !L.Browser.android23, + bounceAtZoomLimits: true }); L.Map.TouchZoom = L.Handler.extend({ @@ -7159,6 +7385,11 @@ L.Map.TouchZoom = L.Handler.extend({ if (this._scale === 1) { return; } + if (!map.options.bounceAtZoomLimits) { + if ((map.getZoom() === map.getMinZoom() && this._scale < 1) || + (map.getZoom() === map.getMaxZoom() && this._scale > 1)) { return; } + } + if (!this._moved) { L.DomUtil.addClass(map._mapPane, 'leaflet-touching'); @@ -7262,7 +7493,7 @@ L.Map.Tap = L.Handler.extend({ this._startPos = this._newPos = new L.Point(first.clientX, first.clientY); // if touching a link, highlight it - if (el.tagName.toLowerCase() === 'a') { + if (el.tagName && el.tagName.toLowerCase() === 'a') { L.DomUtil.addClass(el, 'leaflet-active'); } @@ -7292,7 +7523,7 @@ L.Map.Tap = L.Handler.extend({ var first = e.changedTouches[0], el = first.target; - if (el.tagName.toLowerCase() === 'a') { + if (el && el.tagName && el.tagName.toLowerCase() === 'a') { L.DomUtil.removeClass(el, 'leaflet-active'); } @@ -7328,7 +7559,7 @@ L.Map.Tap = L.Handler.extend({ } }); -if (L.Browser.touch && !L.Browser.msTouch) { +if (L.Browser.touch && !L.Browser.pointer) { L.Map.addInitHook('addHandler', 'tap', L.Map.Tap); } @@ -7347,6 +7578,7 @@ L.Map.BoxZoom = L.Handler.extend({ this._map = map; this._container = map._container; this._pane = map._panes.overlayPane; + this._moved = false; }, addHooks: function () { @@ -7355,9 +7587,16 @@ L.Map.BoxZoom = L.Handler.extend({ removeHooks: function () { L.DomEvent.off(this._container, 'mousedown', this._onMouseDown); + this._moved = false; + }, + + moved: function () { + return this._moved; }, _onMouseDown: function (e) { + this._moved = false; + if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } L.DomUtil.disableTextSelection(); @@ -7365,21 +7604,22 @@ L.Map.BoxZoom = L.Handler.extend({ this._startLayerPoint = this._map.mouseEventToLayerPoint(e); - this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane); - L.DomUtil.setPosition(this._box, this._startLayerPoint); - - //TODO refactor: move cursor to styles - this._container.style.cursor = 'crosshair'; - L.DomEvent .on(document, 'mousemove', this._onMouseMove, this) .on(document, 'mouseup', this._onMouseUp, this) .on(document, 'keydown', this._onKeyDown, this); - - this._map.fire('boxzoomstart'); }, _onMouseMove: function (e) { + if (!this._moved) { + this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane); + L.DomUtil.setPosition(this._box, this._startLayerPoint); + + //TODO refactor: move cursor to styles + this._container.style.cursor = 'crosshair'; + this._map.fire('boxzoomstart'); + } + var startPoint = this._startLayerPoint, box = this._box, @@ -7392,14 +7632,18 @@ L.Map.BoxZoom = L.Handler.extend({ L.DomUtil.setPosition(box, newPos); + this._moved = true; + // TODO refactor: remove hardcoded 4 pixels box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px'; box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px'; }, _finish: function () { - this._pane.removeChild(this._box); - this._container.style.cursor = ''; + if (this._moved) { + this._pane.removeChild(this._box); + this._container.style.cursor = ''; + } L.DomUtil.enableTextSelection(); L.DomUtil.enableImageDrag(); @@ -7457,7 +7701,7 @@ L.Map.Keyboard = L.Handler.extend({ right: [39], down: [40], up: [38], - zoomIn: [187, 107, 61], + zoomIn: [187, 107, 61, 171], zoomOut: [189, 109, 173] }, @@ -7507,7 +7751,7 @@ L.Map.Keyboard = L.Handler.extend({ var body = document.body, docEl = document.documentElement, top = body.scrollTop || docEl.scrollTop, - left = body.scrollTop || docEl.scrollLeft; + left = body.scrollLeft || docEl.scrollLeft; this._map._container.focus(); @@ -7612,6 +7856,7 @@ L.Handler.MarkerDrag = L.Handler.extend({ .on('drag', this._onDrag, this) .on('dragend', this._onDragEnd, this); this._draggable.enable(); + L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-draggable'); }, removeHooks: function () { @@ -7621,6 +7866,7 @@ L.Handler.MarkerDrag = L.Handler.extend({ .off('dragend', this._onDragEnd, this); this._draggable.disable(); + L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); }, moved: function () { @@ -7652,10 +7898,10 @@ L.Handler.MarkerDrag = L.Handler.extend({ .fire('drag'); }, - _onDragEnd: function () { + _onDragEnd: function (e) { this._marker .fire('moveend') - .fire('dragend'); + .fire('dragend', e); } }); @@ -7728,6 +7974,12 @@ L.Control = L.Class.extend({ } return this; + }, + + _refocusOnMap: function () { + if (this._map) { + this._map.getContainer().focus(); + } } }); @@ -7779,7 +8031,11 @@ L.Map.include({ L.Control.Zoom = L.Control.extend({ options: { - position: 'topleft' + position: 'topleft', + zoomInText: '+', + zoomInTitle: 'Zoom in', + zoomOutText: '-', + zoomOutTitle: 'Zoom out' }, onAdd: function (map) { @@ -7789,10 +8045,13 @@ L.Control.Zoom = L.Control.extend({ this._map = map; this._zoomInButton = this._createButton( - '+', 'Zoom in', zoomName + '-in', container, this._zoomIn, this); + this.options.zoomInText, this.options.zoomInTitle, + zoomName + '-in', container, this._zoomIn, this); this._zoomOutButton = this._createButton( - '-', 'Zoom out', zoomName + '-out', container, this._zoomOut, this); + this.options.zoomOutText, this.options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut, this); + this._updateDisabled(); map.on('zoomend zoomlevelschange', this._updateDisabled, this); return container; @@ -7823,7 +8082,8 @@ L.Control.Zoom = L.Control.extend({ .on(link, 'mousedown', stop) .on(link, 'dblclick', stop) .on(link, 'click', L.DomEvent.preventDefault) - .on(link, 'click', fn, context); + .on(link, 'click', fn, context) + .on(link, 'click', this._refocusOnMap, context); return link; }, @@ -7881,6 +8141,12 @@ L.Control.Attribution = L.Control.extend({ this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); L.DomEvent.disableClickPropagation(this._container); + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + map .on('layeradd', this._onLayerAdd, this) .on('layerremove', this._onLayerRemove, this); @@ -8163,8 +8429,9 @@ L.Control.Layers = L.Control.extend({ container.setAttribute('aria-haspopup', true); if (!L.Browser.touch) { - L.DomEvent.disableClickPropagation(container); - L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation); + L.DomEvent + .disableClickPropagation(container) + .disableScrollPropagation(container); } else { L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); } @@ -8189,6 +8456,10 @@ L.Control.Layers = L.Control.extend({ else { L.DomEvent.on(link, 'focus', this._expand, this); } + //Work around for Firefox android issue https://github.com/Leaflet/Leaflet/issues/2033 + L.DomEvent.on(form, 'click', function () { + setTimeout(L.bind(this._onInputClick, this), 0); + }, this); this._map.on('click', this._collapse, this); // TODO keyboard accessibility @@ -8323,6 +8594,8 @@ L.Control.Layers = L.Control.extend({ } this._handlingClick = false; + + this._refocusOnMap(); }, _expand: function () { @@ -8443,8 +8716,8 @@ L.Map.include({ setView: function (center, zoom, options) { - zoom = this._limitZoom(zoom); - center = L.latLng(center); + zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); + center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds); options = options || {}; if (this._panAnim) { @@ -8900,7 +9173,8 @@ L.Map.include({ var data = { latlng: latlng, - bounds: bounds + bounds: bounds, + timestamp: pos.timestamp }; for (var i in pos.coords) { @@ -8914,4 +9188,4 @@ L.Map.include({ }); -}(window, document)); \ No newline at end of file +}(window, document));