Merge branch 'master' into redesign
authorJohn Firebaugh <john.firebaugh@gmail.com>
Tue, 19 Nov 2013 17:42:47 +0000 (09:42 -0800)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Tue, 19 Nov 2013 17:42:47 +0000 (09:42 -0800)
Conflicts:
vendor/assets/leaflet/leaflet.hash.js
vendor/assets/leaflet/leaflet.js

28 files changed:
Vendorfile
app/controllers/application_controller.rb
app/controllers/user_controller.rb
app/views/export/embed.html.erb
app/views/layouts/_head.html.erb
config/locales/de.yml
config/locales/diq.yml
config/locales/el.yml
config/locales/et.yml
config/locales/fi.yml
config/locales/ia.yml
config/locales/it.yml
config/locales/ja.yml
config/locales/ko.yml
config/locales/lv.yml
config/locales/sl.yml
config/locales/sv.yml
test/functional/diary_entry_controller_test.rb
test/functional/message_controller_test.rb
test/functional/redactions_controller_test.rb
test/functional/site_controller_test.rb
test/functional/trace_controller_test.rb
test/functional/user_blocks_controller_test.rb
test/functional/user_controller_test.rb
test/functional/user_roles_controller_test.rb
vendor/assets/leaflet/leaflet.css
vendor/assets/leaflet/leaflet.ie.css [deleted file]
vendor/assets/leaflet/leaflet.js

index 136b47f728d162aaa4a9f4b2fef5b2ecd97c3b86..2d728ab1ebe0ca30c34cb7cd9762c0c6172b824c 100644 (file)
@@ -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
index 97ab5abfc397036304e6e3f73311f80dea2ffa84..67e25c6a7b86520bb1526f0ef8236e897f984bab 100644 (file)
@@ -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
 
index a9006e82b334e6edfe19ae24fad0dcfdbf8d9e78..99777ca2d83c0846c78b69db30fea00d5eaabc48 100644 (file)
@@ -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
index b683c26659599d7eade03532827d4979e50a64d0..6c3ebdd160295fa6ec358e4aad7ec457dabeada9 100644 (file)
@@ -4,9 +4,6 @@
     <meta charset="utf-8">
     <title>OpenStreetMap Embedded</title>
     <%= stylesheet_link_tag "embed", :media=> "screen" %>
-    <!--[if IE]>
-    <%= stylesheet_link_tag "leaflet.ie", :media=> "screen" %>
-    <![endif]-->
     <%= javascript_include_tag "embed" %>
   </head>
   <body>
index 9951737bc1e89ef607942a98373aedb2a2c5b537..1897cfff37ef68f440b39a242aa40c5f0ab61b82 100644 (file)
@@ -8,7 +8,6 @@
   <%= stylesheet_link_tag "print-#{dir}", :media => "print" %>
   <%= stylesheet_link_tag "leaflet-all", :media => "screen, print" %>
   <!--[if IE]>
-    <%= stylesheet_link_tag "leaflet.ie" %>
     <%= stylesheet_link_tag "large-#{dir}", :media => "screen" %>
   <![endif]-->
   <%= favicon_link_tag "favicon.ico" %>
index 7647ac082ce47399aee53c7f88f21f802a6a5dd5..5e0662e9fd9899e2e5eea8953f3d6ef339dd33e5 100644 (file)
@@ -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 <a href='http://help.openstreetmap.org/'>der Hilfe-Seite</a>."
       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 <em>nicht</em> 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."
index c5b6ed3974fd9524662ea1251583e837e0ba3b1a..1f5db228ddbf02844393eeb074eaad39de5ca784 100644 (file)
@@ -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îşî
index 2bcb0dd9adc7fef2d129c94236bfa7d10560ca1f..845de71f34d4620f5949af3ead0274609b8947db 100644 (file)
@@ -51,7 +51,7 @@ el:
         languages: Γλώσσες
         pass_crypt: Κωδικός
     models: 
-      acl: Î Ï\81Ï\8cÏ\83βαÏ\83η Ï\83Ï\84η Î»Î¯Ï\83Ï\84α Îµλέγχου
+      acl: Î Ï\81Ï\8cÏ\83βαÏ\83η Ï\83Ï\84η Î\9bίÏ\83Ï\84α Î\95λέγχου
       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: Î Ï\81οβολή Î¹στορικού
+      show_history: Î Ï\81οβολή Î\99στορικού
       unable_to_load_size: "Δεν είναι δυνατή η φόρτωση: το μέγεθος %{bbox_size} του πλαισίου οριοθέτησης είναι πολύ μεγάλο (πρέπει να είναι μικρότερο από %{max_bbox_size})"
-      view_data: Î Ï\81οβολή Î´ÎµÎ´Î¿Î¼Î­Î½Ï\89ν Ï\83την τρέχουσα προβολή του χάρτη
+      view_data: Î\94είÏ\84ε Î´ÎµÎ´Î¿Î¼Î­Î½Î± Ï\83Ï\87εÏ\84ικά Î¼Îµ την τρέχουσα προβολή του χάρτη
       wait: Αναμονή...
-      zoom_or_select: Î\9cεγέθÏ\85νÏ\83η Î® ÎµÏ\80ιλογή Ï\80εÏ\81ιοÏ\87ήÏ\82 του χάρτη προς εμφάνιση
+      zoom_or_select: Î\9cεγεθÏ\85νθείÏ\84ε Î® ÎµÏ\80ιλέξÏ\84ε Ï\84ην Ï\80εÏ\81ιοÏ\87ή του χάρτη προς εμφάνιση
     tag_details: 
       tags: "Ετικέτες:"
       wiki_link: 
@@ -493,9 +493,11 @@ el:
     search: 
       title: 
         ca_postcode: Αποτελέσματα από <a href="http://geocoder.ca/">Geocoder.CA</a>
-        geonames: Αποτελέσματα από <a href="http://www.geonames.org/">GeoNames</a>
-        latlon: <a href="http://openstreetmap.org/">Εσωτερικά</a> αποτελέσματα
-        osm_nominatim: Αποτελέσματα από <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
+        geonames: Αποτελέσματα από το <a href="http://www.geonames.org/">GeoNames</a>
+        geonames_reverse: Αποτελέσματα από το <a href="http://www.geonames.org/">GeoNames</a>
+        latlon: Αποτελέσματα από την <a href="http://openstreetmap.org/">εσωτερική</a> βάση δεδομένων
+        osm_nominatim: Αποτελέσματα από το <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
+        osm_nominatim_reverse: Αποτελέσματα από το <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
         uk_postcode: Αποτέλεσμα από <a href="http://www.npemap.org.uk/">NPEMap / FreeThe Postcode</a>
         us_postcode: Αποτελέσματα από <a href="http://geocoder.us/">Geocoder.us</a>
     search_osm_nominatim: 
@@ -1081,7 +1083,7 @@ el:
       alt_text: Λογότυπο OpenStreetMap
     logout: Αποσύνδεση
     make_a_donation: 
-      text: Î\9aάνÏ\84ε Î¼Î¹Î± Î\94Ï\89Ï\81εά
+      text: Î£Ï\84ηÏ\81ίξÏ\84ε Î¼Î±Ï\82
       title: Υποστήριξε το OpenStreetMap με δωρεά χρημάτων
     osm_offline: Η βάση δεδομένων του OpenStreetMap είναι προσωρινά εκτός λειτουργίας λόγω εργασιών συντήρησης της βάσης δεδομένων.
     osm_read_only: Η βάση δεδομένων του OpenStreetMap είναι προσωρινά σε λειτουργία "μόνο για ανάγνωση" λόγω εργασιών συντήρησης της βάσης δεδομένων.
@@ -1453,6 +1455,7 @@ el:
           track: Χωματόδρομος
           tram: 
             1: τραμ
+          trunk: Αυτοκινητόδρομος
           tunnel: Διακεκομμένο περίβλημα = σήραγγα
           unclassified: Αταξινόμητη Οδός
           unsurfaced: Δρόμος χωρίς Επίστρωση
index a0e2c17c29721837bbda605b39faca971a09ac20..f6d045da78bd8078ddda3319c7570145188bccee 100644 (file)
@@ -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: "<p>Erinevalt teistest kaartidest on OpenStreetMap loodud samade inimeste poolt nagu sina ja igaüks võib seda parandada, täiendada, alla laadida ja kasutada.</p>\n<p>Registreeru, et alustada kaardi täiendamist. Me saadama sulle meili, et saaksid kinnitada oma kasutajakonto.</p>"
       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 <a href="http://www.osmfoundation.org/wiki/License/Contributor_Terms">kaastöö tingimustega</a>.
       not displayed publicly: Ei näidata avalikult (vaata <a href="http://wiki.openstreetmap.org/wiki/Privacy_Policy" title="viki privaatsusreeglid, sh osa e-posti aadresside käsitlemisest">privaatsusreegleid</a>)
       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 <a href="%{url}">seda wiki lehte</a>.
       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!
index b527352cd1e12138ee2c19ffd17275e395c5c5b8..43f0a94d2cbe0865811489c5313fcb47f14774eb 100644 (file)
@@ -2042,7 +2042,7 @@ fi:
       tag_html: <strong>Tagi</strong> 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 <strong>viiva</strong> (engl. way) on viivamainen kohde, kuten katu tai joki. Suljettu viiva eli <strong>alue</strong> (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 <a href='http://help.openstreetmap.org/'>ohjekeskukseen</a>.
       title: Kysyttävää?
@@ -2050,5 +2050,5 @@ fi:
     title: Tervetuloa!
     whats_on_the_map: 
       off_html: Se <em>ei</em> 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ä <em>todellisia että nykyisiä</em> - 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ä <em>todellisia että nykyisiä</em> - se sisältää miljoonia rakennuksia, teitä ja muita yksityiskohtia paikoista. Voit kartoittaa mitä tahansa reaalimaailman piirteitä, jotka sinua kiinnostavat.
       title: Mitä on kartalla
index 38c80df20d9ec9d71ebffc96dc507cc2ccd9b78f..cf37b41f958d27b455d5596836f0d39e64d7ec9a 100644 (file)
@@ -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 <a href="http://geocoder.ca/">Geocoder.CA</a>
         geonames: Resultatos de <a href="http://www.geonames.org/">GeoNames</a>
+        geonames_reverse: Resultatos ab <a href="http://www.geonames.org/">GeoNames</a>
         latlon: Resultatos <a href="http://openstreetmap.org/">interne</a>
         osm_nominatim: Resultatos de <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
+        osm_nominatim_reverse: Resultatos ab <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
         uk_postcode: Resultatos de <a href="http://www.npemap.org.uk/">NPEMap / FreeThe Postcode</a>
         us_postcode: Resultatos de <a href="http://geocoder.us/">Geocoder.us</a>
     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 <strong>editor</strong> es un programma o sito web que tu pote usar pro modificar le carta.
+      node_html: Un <strong>nodo</strong> 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 <strong>etiquetta</strong> 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 <strong>via</strong> 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 <em>non</em> 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 <em>real e actual</em>; 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?
index 9b8b472bb3ea9e06f1069edfee10c581d4d2b7df..83eafa9f196544b5d36007a7b38ad52e2ad19c1d 100644 (file)
@@ -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
index 917f77e2d259231e4a8a8b70f9037a0a1bcaf01a..9a685238d30d39ba0b50fba2f38ba6f5d8f211f2 100644 (file)
@@ -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: © <a href='%{copyright_url}'>OpenStreetMap への貢献者</a>
+      copyright: © <a href='%{copyright_url}'>OpenStreetMapへの協力者</a>
       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: "<strong>オーストリア</strong>: <a href=\"http://data.wien.gv.at/\">Stadt Wien</a> (ライセンス <a href=\"http://creativecommons.org/licenses/by/3.0/at/deed.de\">CC BY</a>)、<a href=\"http://www.vorarlberg.at/vorarlberg/bauen_wohnen/bauen/vermessung_geoinformation/weitereinformationen/services/wmsdienste.htm\">Land Vorarlberg</a>、Land Tirol (ライセンス <a href=\"http://www.tirol.gv.at/applikationen/e-government/data/nutzungsbedingungen/\">CC BY AT with amendments</a>) によるデータを含みます。"
       contributors_ca_html: "<strong>カナダ</strong>: GeoBase&reg;、GeoGratis (&copy; Department of Natural Resources Canada)、CanVec (&copy; Department of Natural Resources Canada)、StatCan (Geography Division, Statistics Canada) によるデータを含みます。"
+      contributors_footer_1_html: これらの詳細について、またOpenStreetMapの向上に使用されたその他のソースについては、OpenStreetMap Wikiの<a href="http://wiki.openstreetmap.org/wiki/Contributors">協力者ページ</a>をご覧ください。
+      contributors_footer_2_html: OpenStreetMapのデータに含めることは、元データの提供者がOpenStreetMapを支持したり、何かしらの保証を行ったり、何かしらの責任を負ったりすることを意味するものではありません。
       contributors_fr_html: "<strong>フランス</strong>: Direction Générale des Impôts によるデータを含みます。"
       contributors_gb_html: "<strong>イギリス</strong>: 陸地測量データ &copy; Crown copyright and database right 2010-12 を含みます。"
       contributors_intro_html: 協力者は数千人もの個人です。それに加え、国立の地図作成組織や、次のようなその他の情報源による公開できるライセンスによるデータを含みます。
@@ -1125,11 +1131,12 @@ ja:
       contributors_nz_html: "<strong>ニュージーランド</strong>: Land Information New Zealand によるデータを含みます。Crown が著作権を保持します。"
       contributors_title_html: 協力者
       contributors_za_html: "<strong>南アフリカ</strong>: <a href=\"http://www.ngi.gov.za/\">Chief Directorate: National Geo-Spatial Information</a>, State によるデータを含み、著作権を保持します。"
-      credit_1_html: 「&copy; OpenStreetMap への貢献者」のクレジットを必ず使用してください。
+      credit_1_html: 「&copy; OpenStreetMapへの協力者」のクレジットを必ず使用してください。
       credit_2_html: あなたはデータが Open Database License に基づいて提供されていること、そして地図タイルを使用する場合は、地図製作が CC BY-SA としてライセンスされていることを明確にしなければなりません。<a href="http://www.openstreetmap.org/copyright">著作権表示ページ</a>にリンクすることでこれを表現することができます。また、データの形式で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のデータベースや本サイトに不正に追加されたとお考えの場合は、<a href="http://www.osmfoundation.org/wiki/License/Takedown_procedure">却下手順</a>をお読みになるか、<a href="http://dmca.openstreetmap.org/">on-line filing page</a>から直接申し立てを行ってください。
       infringement_title_html: 著作権侵害
       intro_1_html: "OpenStreetMap は<i>オープンデータ</i>であり、<a\nhref=\"http://opendatacommons.org/licenses/odbl/\">Open Data Commons Open Database License</a> (ODbL) の下にライセンスされています。"
       intro_2_html: OpenStreetMapとその協力者をクレジットすれば、データを自由にコピー、配布、送信、利用することができます。変更したり翻案したりしたデータは、同じライセンスに従って提供することができます。あなたの権利と責任は、<a href="http://opendatacommons.org/licenses/odbl/1.0/">ライセンス契約</a>の全文で説明しています。
@@ -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 は<a href="http://www.adobe.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash">Adobe.com</a> でダウンロードできます。OpenStreetMap を編集する<a href="http://wiki.openstreetmap.org/wiki/JA:Editing?uselang=ja">他の方法</a>もあります。
+      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: 確認メールを再送する必要がある場合は、<a href="%{reconfirm}">ここをクリック</a>してください。
       unknown token: この確認コードは期限切れ、または存在しません。
@@ -1763,6 +1792,7 @@ ja:
     new: 
       about: 
         header: フリー、編集可能
+        html: "<p>他の地図とは異なり、OpenStreetMapは完全にあなたのような人々によって作成され、だれでも自由に修正、更新、ダウンロード、利用することができます。</p>\n<p>協力を始めるにはサインアップしてください。あなたのアカウントの確認メールが送信されます。</p>"
       confirm email address: "メール アドレスの確認:"
       confirm password: "パスワードの確認:"
       contact_webmaster: アカウントを作成できるよう、<a href="mailto:webmaster@openstreetmap.org">webmaster</a>に連絡してください。 できるだけ早期に、あなたの希望に対応するように努めます。
@@ -1770,15 +1800,15 @@ ja:
       display name: "表示名:"
       display name description: 自分の公開ユーザー名です。あとで設定ページで変更できます。
       email address: "メール アドレス:"
-      license_agreement: アカウントを確認するときには <a href="http://www.osmfoundation.org/wiki/License/Contributor_Terms">contributor terms</a> に同意する必要があります。
+      license_agreement: アカウントを確認するときには<a href="http://www.osmfoundation.org/wiki/License/Contributor_Terms">協力者規約</a>に同意する必要があります。
       no_auto_account_create: 残念ながら、現在、自動ではアカウントを作成できません。
       not displayed publicly: 非公開です (詳細は <a href="http://wiki.openstreetmap.org/wiki/Privacy_Policy" title="メール アドレスの節を含む、ウィキのプライバシー ポリシー">プライバシー ポリシー</a>を参照してください)
       openid: "%{logo} OpenID:"
       openid association: "<p>あなたの OpenID は、まだ OpenStreetMap のどのアカウントとも関連付けられていません。</p>\n<ul>\n  <li>OpenStreetMap が初めての場合は、下のフォームで新規アカウントを作成してください。</li>\n  <li>\n    既にアカウントを持っている場合は、あなたのユーザー名とパスワードでログインしてください。\n    ログイン後、ユーザー設定であなたの OpenID と関連付けることができます。\n  </li>\n</ul>"
       openid no password: OpenID ではパスワードは不要ですが、特殊なツール/サーバーでは必要な場合があります。
       password: "パスワード:"
-      terms accepted: 新しい投稿規約に同意いただき、ありがとうございます!
-      terms declined: 新しい投稿規約に同意いただけず残念です。詳しい情報は、<a href="%{url}">このウィキページ</a>をご覧ください。
+      terms accepted: 新しい協力者規約に同意いただき、ありがとうございます!
+      terms declined: 新しい協力者規約に同意いただけず残念です。詳しい情報は、<a href="%{url}">このウィキページ</a>をご覧ください。
       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: この規約の理解を助ける情報として、<a href="%{summary}">要約 (英語)</a> や <a href="%{translations}">非公式の翻訳</a> をご覧ください。
-      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: <a href='%{map_url}'>地図</a>に移動してメモアイコン(<span class='icon note'></span>)をクリックするだけです。これで地図上に印が追加され、ドラッグして移動させることができます。あなたのメッセージを追加し、保存をクリックすれば、他のマッパーが調査するでしょう。
       title: 編集する時間がないためメモを残します
     basic_terms: 
       editor_html: <strong>エディター</strong>は、地図を編集できるプログラムやウェブサイトです。
@@ -2005,9 +2039,13 @@ ja:
       tag_html: <strong>タグ</strong>はノードやウェイについて補足するデータです。レストランの名前、道路の制限速度などが該当します。
       title: マッピングのための基本的な用語
       way_html: <strong>ウェイ</strong>は線または領域です。道路、水路、湖、建物などが該当します。
+    introduction_html: フリーで編集可能な世界地図、OpenStreetMapへようこそ。サインアップしたので、マッピングの準備はすべて整いました。知っておかなければならない重要事項を簡単に説明します。
     questions: 
+      paragraph_1_html: マッピングで分からないことがありますか?OpenStreetMapの使い方で不明な点はありますか?<a href='http://help.openstreetmap.org/'>ヘルプのウェブサイト</a>で疑問の回答を得てください。
       title: 何か質問はありますか?
     start_mapping: マッピングを開始
     title: ようこそ!
     whats_on_the_map: 
+      off_html: 含めては<em>いけない</em>ものは、評価のような主観的なデータ、歴史的または仮想的なもの、著作権のある情報源からのデータです。特別な許可がない場合は、オンラインや紙の地図からコピーをしてはいけません。
+      on_html: OpenStreetMapは<em>現実かつ現存</em>のもの――数々の建物、道路、場所に関するその他の詳細をマッピングする場所です。あなたは関心のある現実世界のものを何でもマッピングできます。
       title: 地図上にあるもの
index 3dda0fa40e206cbf74d7190f23fc614f38057672..27327ff57e5babab396c554efe313cb52b73278d 100644 (file)
@@ -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}.
index fb49ebb0a6e4437af860c91c61ea775ce6d80b05..3ff4a9f5f24edc5deb899b976d692dabf41cfa96 100644 (file)
@@ -485,8 +485,10 @@ lv:
       title: 
         ca_postcode: Rezultāti no <a href="http://geocoder.ca/">Geocoder.CA</a>
         geonames: Rezultāti no <a href="http://www.geonames.org/">GeoNames</a>
+        geonames_reverse: Rezultāti no <a href="http://www.geonames.org/">GeoNames</a>
         latlon: Rezultāti no <a href="http://openstreetmap.org/">Iekšējās meklēšanas</a>
         osm_nominatim: Rezultāti no <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
+        osm_nominatim_reverse: Rezultāti no <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
         uk_postcode: Rezultāti no <a href="http://www.npemap.org.uk/">NPEMap / FreeThe Postcode</a>
         us_postcode: Rezultāti no <a href="http://geocoder.us/">Geocoder.us</a>
     search_osm_nominatim: 
@@ -993,6 +995,7 @@ lv:
     map: 
       base: 
         cycle_map: Velokarte
+        hot: Humanitārās
         standard: Standarta
         transport_map: Transporta karte
       copyright: © <a href='%{copyright_url}'>OpenStreetMap ieguldītāji</a>
@@ -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. <a href='%{upload_link}'>Augšupielādē jaunu trasi</a> vai uzzini vairāk par GPS trasēm iekš <a href='http://wiki.openstreetmap.org/wiki/Beginners_Guide_1.2'>wiki lapas</a>.
index 450d9ae0891bd5275205391474616700f9aaa8e9..7fe24a9b336ae880afc231cbedf4f4ab62c4dd28 100644 (file)
@@ -486,8 +486,10 @@ sl:
       title: 
         ca_postcode: Zadetki iz <a href="http://geocoder.ca/">Geocoder.CA</a>
         geonames: Zadetki iz <a href="http://www.geonames.org/">GeoNames</a>
+        geonames_reverse: Zadetki iz <a href="http://www.geonames.org/">GeoNames</a>
         latlon: <a href="http://openstreetmap.org/">Interni</a> zadetki
         osm_nominatim: Zadetki iz <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>
+        osm_nominatim_reverse: Zadetki iz <a href="http://nominatim.openstreetmap.org/">OpenStreetMap Nominatim</a>-a
         uk_postcode: Zadetki iz <a href="http://www.npemap.org.uk/">NPEMap / FreeThe Postcode</a>
         us_postcode: Zadetki iz <a href="http://geocoder.us/">Geocoder.us</a>
     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 <a href='%{user_url}'>%{user}</a> ob %{time}
+        closed_by_anonymous: razrešil anonimnež ob %{time}
         comment: Komentar
         comment_and_resolve: Komentiraj in razreši
+        commented_by: komentar uporabnika <a href='%{user_url}'>%{user}</a> ob %{time}
+        commented_by_anonymous: Anonimni komentar ob %{time}
         hide: Skrij
         opened_by: ustvaril/-a <a href='%{user_url}'>%{user}</a> ob %{time}
         opened_by_anonymous: ustvaril anonimni uporabnik %{time}
         permalink: Trajna povezava
         reactivate: Znova aktiviraj
+        reopened_by: ponovno aktiviral <a href='%{user_url}'>%{user}</a> 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
index 5c802bc7486b73173a2560a82be2dcd8563ad2de..ba874d1da17da0d42b98fbc5969c4129ef498a15 100644 (file)
@@ -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.
index 7e0f10790fe771687e6fcba4c9057493403ba63d..8725def3ddf6694ae3440ee86266928e117a66a0 100644 (file)
@@ -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
index f015b1ec33e6467909105150843c46b536128855..77fdfbeb915f20915db2b3ca9de5eb0378d5a732 100644 (file)
@@ -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
index a2bd75b100ebca103c71bc78c22113b1a2b4b4c1..83bb3adc21b8fa0f641f79a83f86cee7d9a5ce7e 100644 (file)
@@ -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)
 
index 00a184ba8b8fe11a13c21fcf3fb2f6f60e809e8c..2db756ad125164cd79b92662300eca33028303d1 100644 (file)
@@ -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)
 
index 57d6f4916ed0a445e65a7aef871bdfab6732049a..1a074082b848e7f167a6cdcd989684440686f394 100644 (file)
@@ -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
index 39ebfd28e462d2639ef9c16daf234cacededaf5d..49fb6552ff179ec61128ab3c50aa6daae76790a1 100644 (file)
@@ -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
index cba27f85277803945e759c7913ce7f0e426e86cb..66fcef78509ff78e0efd5795fb8c157228f5ff35 100644 (file)
@@ -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
index ed5d1a17d0302826b8d8dd3fee5e0f8cfbdd7fcf..e942cd0fafc1305eb3d8ecbee85f270c7ee4c63f 100644 (file)
@@ -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|
 
index e6fb4908eb8e872ad35b424a195d878cb277ebfd..ac0cd174df7fac89d74525d1b06dc79b38af7964 100644 (file)
 .leaflet-marker-pane  { z-index: 6; }\r
 .leaflet-popup-pane   { z-index: 7; }\r
 \r
+.leaflet-vml-shape {\r
+       width: 1px;\r
+       height: 1px;\r
+       }\r
+.lvml {\r
+       behavior: url(#default#VML);\r
+       display: inline-block;\r
+       position: absolute;\r
+       }\r
+\r
 \r
 /* control positioning */\r
 \r
 .leaflet-control {\r
        cursor: auto;\r
        }\r
-.leaflet-dragging,\r
-.leaflet-dragging .leaflet-clickable,\r
-.leaflet-dragging .leaflet-container {\r
+.leaflet-dragging .leaflet-container,\r
+.leaflet-dragging .leaflet-clickable {\r
        cursor: move;\r
        cursor: -webkit-grabbing;\r
        cursor:    -moz-grabbing;\r
        outline: 2px solid orange;\r
        }\r
 .leaflet-zoom-box {\r
-       border: 2px dotted #05f;\r
-       background: white;\r
-       opacity: 0.5;\r
+       border: 2px dotted #38f;\r
+       background: rgba(255,255,255,0.5);\r
        }\r
 \r
 \r
 /* general toolbar styles */\r
 \r
 .leaflet-bar {\r
-       box-shadow: 0 1px 7px rgba(0,0,0,0.65);\r
-       -webkit-border-radius: 4px;\r
-               border-radius: 4px;\r
+       box-shadow: 0 1px 5px rgba(0,0,0,0.65);\r
+       border-radius: 4px;\r
        }\r
-.leaflet-bar a, .leaflet-bar a:hover {\r
+.leaflet-bar a,\r
+.leaflet-bar a:hover {\r
        background-color: #fff;\r
        border-bottom: 1px solid #ccc;\r
        width: 26px;\r
        background-color: #f4f4f4;\r
        }\r
 .leaflet-bar a:first-child {\r
-       -webkit-border-top-left-radius: 4px;\r
-               border-top-left-radius: 4px;\r
-       -webkit-border-top-right-radius: 4px;\r
-               border-top-right-radius: 4px;\r
+       border-top-left-radius: 4px;\r
+       border-top-right-radius: 4px;\r
        }\r
 .leaflet-bar a:last-child {\r
-       -webkit-border-bottom-left-radius: 4px;\r
-               border-bottom-left-radius: 4px;\r
-       -webkit-border-bottom-right-radius: 4px;\r
-               border-bottom-right-radius: 4px;\r
+       border-bottom-left-radius: 4px;\r
+       border-bottom-right-radius: 4px;\r
        border-bottom: none;\r
        }\r
 .leaflet-bar a.leaflet-disabled {\r
        color: #bbb;\r
        }\r
 \r
-.leaflet-touch .leaflet-bar {\r
-       -webkit-border-radius: 10px;\r
-               border-radius: 10px;\r
-       }\r
 .leaflet-touch .leaflet-bar a {\r
        width: 30px;\r
        height: 30px;\r
-       }\r
-.leaflet-touch .leaflet-bar a:first-child {\r
-       -webkit-border-top-left-radius: 7px;\r
-               border-top-left-radius: 7px;\r
-       -webkit-border-top-right-radius: 7px;\r
-               border-top-right-radius: 7px;\r
-       }\r
-.leaflet-touch .leaflet-bar a:last-child {\r
-       -webkit-border-bottom-left-radius: 7px;\r
-               border-bottom-left-radius: 7px;\r
-       -webkit-border-bottom-right-radius: 7px;\r
-               border-bottom-right-radius: 7px;\r
-       border-bottom: none;\r
+       line-height: 30px;\r
        }\r
 \r
 \r
 /* zoom control */\r
 \r
-.leaflet-control-zoom-in {\r
+.leaflet-control-zoom-in,\r
+.leaflet-control-zoom-out {\r
        font: bold 18px 'Lucida Console', Monaco, monospace;\r
+       text-indent: 1px;\r
        }\r
 .leaflet-control-zoom-out {\r
-       font: bold 22px 'Lucida Console', Monaco, monospace;\r
+       font-size: 20px;\r
        }\r
 \r
 .leaflet-touch .leaflet-control-zoom-in {\r
        font-size: 22px;\r
-       line-height: 30px;\r
        }\r
 .leaflet-touch .leaflet-control-zoom-out {\r
-       font-size: 28px;\r
-       line-height: 30px;\r
+       font-size: 24px;\r
        }\r
 \r
 \r
 /* layers control */\r
 \r
 .leaflet-control-layers {\r
-       box-shadow: 0 1px 7px rgba(0,0,0,0.4);\r
-       background: #f8f8f9;\r
-       -webkit-border-radius: 5px;\r
-               border-radius: 5px;\r
+       box-shadow: 0 1px 5px rgba(0,0,0,0.4);\r
+       background: #fff;\r
+       border-radius: 5px;\r
        }\r
 .leaflet-control-layers-toggle {\r
        background-image: url(images/layers.png);\r
 /* attribution and scale controls */\r
 \r
 .leaflet-container .leaflet-control-attribution {\r
-       background-color: rgba(255, 255, 255, 0.7);\r
-       box-shadow: 0 0 5px #bbb;\r
+       background: #fff;\r
+       background: rgba(255, 255, 255, 0.7);\r
        margin: 0;\r
        }\r
 .leaflet-control-attribution,\r
        padding: 0 5px;\r
        color: #333;\r
        }\r
+.leaflet-control-attribution a {\r
+       text-decoration: none;\r
+       }\r
+.leaflet-control-attribution a:hover {\r
+       text-decoration: underline;\r
+       }\r
 .leaflet-container .leaflet-control-attribution,\r
 .leaflet-container .leaflet-control-scale {\r
        font-size: 11px;\r
 .leaflet-control-scale-line {\r
        border: 2px solid #777;\r
        border-top: none;\r
-       color: black;\r
        line-height: 1.1;\r
        padding: 2px 5px 1px;\r
        font-size: 11px;\r
-       text-shadow: 1px 1px 1px #fff;\r
-       background-color: rgba(255, 255, 255, 0.5);\r
-       box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2);\r
        white-space: nowrap;\r
        overflow: hidden;\r
        -moz-box-sizing: content-box;\r
             box-sizing: content-box;\r
+\r
+       background: #fff;\r
+       background: rgba(255, 255, 255, 0.5);\r
        }\r
 .leaflet-control-scale-line:not(:first-child) {\r
        border-top: 2px solid #777;\r
        border-bottom: none;\r
        margin-top: -2px;\r
-       box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\r
        }\r
 .leaflet-control-scale-line:not(:first-child):not(:last-child) {\r
        border-bottom: 2px solid #777;\r
        }\r
 .leaflet-touch .leaflet-control-layers,\r
 .leaflet-touch .leaflet-bar {\r
-       border: 4px solid rgba(0,0,0,0.3);\r
+       border: 2px solid rgba(0,0,0,0.2);\r
+       background-clip: padding-box;\r
        }\r
 \r
 \r
 .leaflet-popup-content-wrapper {\r
        padding: 1px;\r
        text-align: left;\r
-       -webkit-border-radius: 12px;\r
-               border-radius: 12px;\r
+       border-radius: 12px;\r
        }\r
 .leaflet-popup-content {\r
        margin: 13px 19px;\r
             -o-transform: rotate(45deg);\r
                transform: rotate(45deg);\r
        }\r
-.leaflet-popup-content-wrapper, .leaflet-popup-tip {\r
+.leaflet-popup-content-wrapper,\r
+.leaflet-popup-tip {\r
        background: white;\r
 \r
        box-shadow: 0 3px 14px rgba(0,0,0,0.4);\r
        border-top: 1px solid #ddd;\r
        }\r
 \r
+.leaflet-oldie .leaflet-popup-content-wrapper {\r
+       zoom: 1;\r
+       }\r
+.leaflet-oldie .leaflet-popup-tip {\r
+       width: 24px;\r
+       margin: 0 auto;\r
+\r
+       -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";\r
+       filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);\r
+       }\r
+.leaflet-oldie .leaflet-popup-tip-container {\r
+       margin-top: -1px;\r
+       }\r
+\r
+.leaflet-oldie .leaflet-control-zoom,\r
+.leaflet-oldie .leaflet-control-layers,\r
+.leaflet-oldie .leaflet-popup-content-wrapper,\r
+.leaflet-oldie .leaflet-popup-tip {\r
+       border: 1px solid #999;\r
+       }\r
+\r
 \r
 /* div icon */\r
 \r
        background: #fff;\r
        border: 1px solid #666;\r
        }\r
-.leaflet-editing-icon {\r
-       -webkit-border-radius: 2px;\r
-               border-radius: 2px;\r
-       }\r
diff --git a/vendor/assets/leaflet/leaflet.ie.css b/vendor/assets/leaflet/leaflet.ie.css
deleted file mode 100644 (file)
index 14b84b6..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-.leaflet-vml-shape {\r
-       width: 1px;\r
-       height: 1px;\r
-       }\r
-.lvml {\r
-       behavior: url(#default#VML);\r
-       display: inline-block;\r
-       position: absolute;\r
-       }\r
-\r
-.leaflet-control {\r
-       display: inline;\r
-       }\r
-\r
-.leaflet-popup-tip {\r
-       width: 21px;\r
-       _width: 27px;\r
-       margin: 0 auto;\r
-       _margin-top: -3px;\r
-\r
-       filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);\r
-       -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";\r
-       }\r
-.leaflet-popup-tip-container {\r
-       margin-top: -1px;\r
-       }\r
-.leaflet-popup-content-wrapper, .leaflet-popup-tip {\r
-       border: 1px solid #999;\r
-       }\r
-.leaflet-popup-content-wrapper {\r
-       zoom: 1;\r
-       }\r
-\r
-.leaflet-control-zoom,\r
-.leaflet-control-layers {\r
-       border: 3px solid #999;\r
-       }\r
-.leaflet-control-layers-toggle {\r
-       }\r
-.leaflet-control-attribution,\r
-.leaflet-control-layers,\r
-.leaflet-control-scale-line {\r
-       background: white;\r
-       }\r
-.leaflet-zoom-box {\r
-       filter: alpha(opacity=50);\r
-       }\r
-.leaflet-control-attribution {\r
-       border-top: 1px solid #bbb;\r
-       border-left: 1px solid #bbb;\r
-       }\r
index 0f0b01be2a587f901b9464cce071d2cc191529ba..2b66295fc11d550ff12ac9f474b6684a0b635689 100644 (file)
@@ -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));