├── .gitattributes ├── .github └── stale.yml ├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── leaflet-rails.gemspec ├── lib ├── leaflet-rails.rb └── leaflet-rails │ ├── version.rb │ └── view_helpers.rb ├── spec ├── spec_helper.rb └── view_helpers_spec.rb └── vendor └── assets ├── images ├── layers-2x.png ├── layers.png ├── marker-icon-2x.png ├── marker-icon.png └── marker-shadow.png ├── javascripts ├── leaflet-src.js.erb ├── leaflet-src.js.map └── leaflet.js.erb └── stylesheets └── leaflet.css.erb /.gitattributes: -------------------------------------------------------------------------------- 1 | *.map binary 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | 6 | /.rspec 7 | /coverage/ 8 | /log/ 9 | .idea/* 10 | .ruby-gemset 11 | .ruby-version 12 | .DS_Store 13 | 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | cache: bundler 4 | matrix: 5 | include: 6 | - rvm: 2.3.0 7 | gemfile: Gemfile 8 | - rvm: 2.4.0 9 | gemfile: Gemfile 10 | - rvm: 2.5.0 11 | gemfile: Gemfile 12 | - rvm: 2.6.0 13 | gemfile: Gemfile 14 | - rvm: ruby-head 15 | gemfile: Gemfile 16 | - rvm: jruby-head 17 | gemfile: Gemfile 18 | allow_failures: 19 | - rvm: ruby-head 20 | - rvm: jruby-head 21 | before_install: 22 | - gem update --system 23 | - gem install bundler 24 | - gem --version 25 | notifications: 26 | email: false 27 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in leaflet-rails.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2013, Vladimir Agafonkin 2 | Copyright (c) 2010-2011, CloudMade 3 | Copyright (c) 2012-2013, Akshay Joshi 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are 7 | permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of 10 | conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 13 | of conditions and the following disclaimer in the documentation and/or other materials 14 | provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/axyjo/leaflet-rails.png?branch=master)](https://travis-ci.org/axyjo/leaflet-rails) 2 | [![Gem Version](https://badge.fury.io/rb/leaflet-rails.png)](http://badge.fury.io/rb/leaflet-rails) 3 | 4 | Note: Intent to Deprecate 5 | ================ 6 | As of 2024-07-14, Rails 5 is long past its EOL. Rails 6+ support alternative Javascript bundling solutions, which work a lot better than this approach does. As such, I intend on marking this project as deprecated on or after 2024-10-01. Any Leaflet upgrades prior to that date will still be honoured. 7 | 8 | 9 | Quickstart Guide 10 | ================ 11 | 12 | To start using the leaflet-rails gem, follow the steps below (assuming you use the default asset pipeline): 13 | 14 | First, add the following code to your `Gemfile`. 15 | 16 | ```ruby 17 | gem 'leaflet-rails' 18 | ``` 19 | 20 | Then, run `bundle install` from within your project to download the necessary files. Following that, open your application-wide CSS file (`app/assets/stylesheets/application.css`) and add the following line as a comment: 21 | 22 | ``` 23 | = require leaflet 24 | ``` 25 | 26 | After that, open your application-wide Javascript file (typically `app/assets/javascripts/application.js`) and add the following line before requiring files which depend on Leaflet: 27 | 28 | ``` 29 | = require leaflet 30 | ``` 31 | 32 | At this point, you may skip the first two steps of the [Leaflet Quick Start guide](http://leafletjs.com/examples/quick-start/) and start at the third step (adding the map `div` to a view). 33 | 34 | 35 | Version Parity 36 | ============== 37 | 38 | `leaflet-rails` keeps version parity with the upstream `leaflet.js` library. Before v0.7.7 the versions were not always in sync, as noted in the table below. 39 | 40 | | leaflet-rails | leaflet.js | Reason | 41 | | ------------- | ------------- | ------| 42 | | 0.7.4 | 0.7.3 | Requested in #33 because of large gap between master and rubygems.org.| 43 | | 0.7.5 | 0.7.5 | leaflet.js 0.7.4 was reverted. | 44 | | 0.7.6 | ---- | Skipped to sync with upstream. | 45 | | 0.7.7 | 0.7.7 | Sync version numbers with upstream. | 46 | | 1.9.5 | 1.9.4 | Adding intent to deprecate post-install message. | 47 | 48 | Helpers 49 | ======= 50 | 51 | To get you up and running quickly, you can also use the gem's helper. To get started, add the following lines to a file called `leaflet.rb` in `config/initializers`: 52 | 53 | ```ruby 54 | Leaflet.tile_layer = "http://{s}.tile.cloudmade.com/YOUR-CLOUDMADE-API-KEY/997/256/{z}/{x}/{y}.png" 55 | # You can also use any other tile layer here if you don't want to use Cloudmade - see http://leafletjs.com/reference.html#tilelayer for more 56 | Leaflet.attribution = "Your attribution statement" 57 | Leaflet.max_zoom = 18 58 | ``` 59 | 60 | If you are using a tile layer which requires non-default subdomains such as [MapQuest-OSM Tiles](http://developer.mapquest.com/web/products/open/map), you can set the subdomains like this: 61 | 62 | ```ruby 63 | Leaflet.tile_layer = "http://{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png" 64 | Leaflet.subdomains = ['otile1', 'otile2', 'otile3', 'otile4'] 65 | ``` 66 | 67 | You will then be able to call the ```#map``` helper method in a view, and make sure that the helper method is inside an erb tag like so: 68 | ```ruby 69 | <%= map(:center => { 70 | :latlng => [51.52238797921441, -0.08366235665359283], 71 | :zoom => 18 72 | }) %> 73 | ``` 74 | 75 | You can also add any number of markers like so: 76 | ```ruby 77 | map(:center => { 78 | :latlng => [51.52238797921441, -0.08366235665359283], 79 | :zoom => 18 80 | }, 81 | :markers => [ 82 | { 83 | :latlng => [51.52238797921441, -0.08366235665359283], 84 | } 85 | ] 86 | ) 87 | ``` 88 | 89 | Adding a `:popup` element to a marker hash will also generate a popup for a maker: 90 | 91 | ```ruby 92 | map(:center => { 93 | :latlng => [51.52238797921441, -0.08366235665359283], 94 | :zoom => 18 95 | }, 96 | :markers => [ 97 | { 98 | :latlng => [51.52238797921441, -0.08366235665359283], 99 | :popup => "Hello!" 100 | } 101 | ] 102 | ) 103 | ``` 104 | 105 | If you want to override the map settings you have set in the initializer, you can also add them to the helper method: 106 | 107 | ```ruby 108 | map(:center => { 109 | :latlng => [51.52238797921441, -0.08366235665359283], 110 | :zoom => 18 111 | }, 112 | :tile_layer => "http://{s}.somedomain.com/somepath/{z}/{x}/{y}.png", 113 | :attribution => "Some other attribution text", 114 | :max_zoom => 4 115 | ) 116 | ``` 117 | 118 | If you want to have multiple maps on same page , you should add unique container_id in helper method for each map: 119 | 120 | ```ruby 121 | map(:container_id => "first_map", :center => { 122 | :latlng => [51.52238797921441, -0.08366235665359283], 123 | :zoom => 18 124 | }) 125 | 126 | map(:container_id => "second_map", :center => { 127 | :latlng => [51.52238797921441, -0.08366235665359283], 128 | :zoom => 18 129 | }) 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | require 'rspec/core/rake_task' 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec -------------------------------------------------------------------------------- /leaflet-rails.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/leaflet-rails/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'leaflet-rails' 7 | s.version = Leaflet::Rails::VERSION 8 | s.authors = ['Akshay Joshi'] 9 | s.email = ['leaflet_rails@akshayjoshi.com'] 10 | s.license = 'BSD' 11 | s.homepage = '' 12 | s.summary = 'Use leaflet.js with Rails 4/5.' 13 | s.description = 'This gem provides the leaflet.js map display library for your Rails 4+ application.' 14 | 15 | s.files = Dir['lib/**/*', 'vendor/**/*'] 16 | s.test_files = Dir['spec/**/*'] 17 | s.executables = [] 18 | s.require_paths = ['lib'] 19 | 20 | s.post_install_message = "leaflet-rails: Intent to deprecate on or after 2024-10-01. See repo for details" 21 | 22 | s.add_dependency 'railties', '>= 4.2.0' 23 | s.add_dependency 'actionpack', '>= 4.2.0' 24 | s.add_development_dependency 'rspec', '<= 3.4.0' 25 | s.add_development_dependency 'simplecov-rcov' 26 | end 27 | -------------------------------------------------------------------------------- /lib/leaflet-rails.rb: -------------------------------------------------------------------------------- 1 | require "leaflet-rails/version" 2 | require "leaflet-rails/view_helpers" 3 | require "rails" 4 | 5 | module Leaflet 6 | mattr_accessor :tile_layer 7 | mattr_accessor :attribution 8 | mattr_accessor :max_zoom 9 | mattr_accessor :subdomains 10 | 11 | module Rails 12 | class Engine < ::Rails::Engine 13 | initializer 'leaflet-rails.precompile' do |app| 14 | if app.config.respond_to? (:assets) 15 | app.config.assets.precompile += %w(layers-2x.png layers.png marker-icon-2x.png marker-icon.png marker-shadow.png) 16 | end 17 | end 18 | 19 | initializer 'leaflet-rails.helpers' do 20 | ActionView::Base.send :include, Leaflet::ViewHelpers 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/leaflet-rails/version.rb: -------------------------------------------------------------------------------- 1 | module Leaflet 2 | module Rails 3 | VERSION = "1.9.5" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/leaflet-rails/view_helpers.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/inflector' 2 | module Leaflet 3 | module ViewHelpers 4 | 5 | def map(options) 6 | 7 | options[:tile_layer] ||= Leaflet.tile_layer 8 | options[:attribution] ||= Leaflet.attribution 9 | options[:max_zoom] ||= Leaflet.max_zoom 10 | options[:subdomains] ||= Leaflet.subdomains 11 | options[:container_id] ||= 'map' 12 | 13 | tile_layer = options.delete(:tile_layer) || Leaflet.tile_layer 14 | attribution = options.delete(:attribution) || Leaflet.attribution 15 | max_zoom = options.delete(:max_zoom) || Leaflet.max_zoom 16 | container_id = options.delete(:container_id) || 'map' 17 | no_container = options.delete(:no_container) 18 | center = options.delete(:center) 19 | markers = options.delete(:markers) 20 | circles = options.delete(:circles) 21 | polylines = options.delete(:polylines) 22 | geojsons = options.delete(:geojsons) 23 | fitbounds = options.delete(:fitbounds) 24 | subdomains = options.delete(:subdomains) 25 | 26 | output = [] 27 | output << "
" unless no_container 28 | output << "" 103 | output.join("\n").html_safe 104 | end 105 | 106 | private 107 | 108 | def prep_icon_settings(settings) 109 | settings[:name] = 'icon' if settings[:name].nil? or settings[:name].blank? 110 | settings[:shadow_url] = '' if settings[:shadow_url].nil? 111 | settings[:icon_size] = [] if settings[:icon_size].nil? 112 | settings[:shadow_size] = [] if settings[:shadow_size].nil? 113 | settings[:icon_anchor] = [0, 0] if settings[:icon_anchor].nil? 114 | settings[:shadow_anchor] = [0, 0] if settings[:shadow_anchor].nil? 115 | settings[:popup_anchor] = [0, 0] if settings[:popup_anchor].nil? 116 | return settings 117 | end 118 | end 119 | 120 | end 121 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # Require this file using `require "spec_helper"` to ensure that it is only 4 | # loaded once. 5 | # 6 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 7 | 8 | require 'simplecov' 9 | require 'simplecov-rcov' 10 | 11 | SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter 12 | SimpleCov.start 13 | 14 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 15 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 16 | 17 | ENV["RAILS_ENV"] ||= 'test' 18 | 19 | require "action_controller/railtie" 20 | require "rails/test_unit/railtie" 21 | 22 | require "leaflet-rails" 23 | 24 | module Test 25 | class Application < ::Rails::Application 26 | # configuration here if needed 27 | config.active_support.deprecation = :stderr 28 | end 29 | end 30 | 31 | Test::Application.initialize! 32 | 33 | RSpec.configure do |config| 34 | config.run_all_when_everything_filtered = true 35 | config.filter_run :focus 36 | 37 | # Run specs in random order to surface order dependencies. If you find an 38 | # order dependency and want to debug it, you can fix the order by providing 39 | # the seed, which is printed after each run. 40 | # --seed 1234 41 | config.order = 'random' 42 | end 43 | -------------------------------------------------------------------------------- /spec/view_helpers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Leaflet::ViewHelpers do 4 | 5 | class TestView < ActionView::Base 6 | end 7 | 8 | before :all do 9 | Leaflet.tile_layer = "http://{s}.somedomain.com/blabla/{z}/{x}/{y}.png" 10 | Leaflet.attribution = "Some attribution statement" 11 | Leaflet.max_zoom = 18 12 | 13 | @view = TestView.new 14 | end 15 | 16 | it 'should mix in view helpers on initialization' do 17 | expect(@view).to respond_to(:map) 18 | end 19 | 20 | it 'should set the method configuration options' do 21 | result = @view.map(:center => { 22 | :latlng => [51.52238797921441, -0.08366235665359283], 23 | :zoom => 18 24 | }) 25 | 26 | expect(result).to match(/L.tileLayer\('http:\/\/{s}.somedomain\.com\/blabla\/{z}\/{x}\/{y}\.png'/) 27 | expect(result).to match(/attribution: 'Some attribution statement'/) 28 | expect(result).to match(/maxZoom: 18/) 29 | end 30 | 31 | it 'should set subdomains if present' do 32 | result = @view.map(:center => { 33 | :latlng => [51.52238797921441, -0.08366235665359283], 34 | :zoom => 18 35 | }, :subdomains => ['otile1', 'otile2']) 36 | expect(result).to match(/subdomains: \["otile1", "otile2"\]/) 37 | end 38 | 39 | it 'should not set subdomains if nil' do 40 | result = @view.map(:center => { 41 | :latlng => [51.52238797921441, -0.08366235665359283], 42 | :zoom => 18 43 | }, :subdomains => nil) 44 | expect(result).not_to match(/subdomains:/) 45 | end 46 | 47 | it 'should generate a basic map with the correct latitude, longitude and zoom' do 48 | result = @view.map(:center => { 49 | :latlng => [51.52238797921441, -0.08366235665359283], 50 | :zoom => 18 51 | }) 52 | expect(result).to match(/map\.setView\(\[51.52238797921441, -0.08366235665359283\], 18\)/) 53 | end 54 | 55 | it 'should generate a marker' do 56 | result = @view.map(:center => { 57 | :latlng => [51.52238797921441, -0.08366235665359283], 58 | :zoom => 18 59 | }, 60 | :markers => [ 61 | { 62 | :latlng => [51.52238797921441, -0.08366235665359283], 63 | } 64 | ]) 65 | expect(result).to match(/marker = L\.marker\(\[51.52238797921441, -0.08366235665359283\]\).addTo\(map\)/) 66 | end 67 | 68 | it 'should generate a marker with a popup' do 69 | result = @view.map(:center => { 70 | :latlng => [51.52238797921441, -0.08366235665359283], 71 | :zoom => 18 72 | }, 73 | :markers => [ 74 | { 75 | :latlng => [51.52238797921441, -0.08366235665359283], 76 | :popup => "Hello!" 77 | } 78 | ]) 79 | expect(result).to match(/marker = L\.marker\(\[51.52238797921441, -0.08366235665359283\]\).addTo\(map\)/) 80 | expect(result).to match(/marker\.bindPopup\('Hello!'\)/) 81 | end 82 | 83 | it 'should generate a marker with a popup and a custom icon' do 84 | icon_options = { :name => 'house', 85 | :icon_url => 'images/house.png', 86 | :shadow_url => 'images/house_shadow.png', 87 | :icon_size => [38, 95], 88 | :shadow_size => [50, 64], 89 | :icon_anchor => [22, 94], 90 | :shadow_anchor => [4, 62], 91 | :popup_anchor => [-3, -76]} 92 | result = @view.map(:center => { 93 | :latlng => [51.52238797921441, -0.08366235665359283], 94 | :zoom => 18 95 | }, 96 | :markers => [ 97 | { 98 | :latlng => [51.52238797921441, -0.08366235665359283], 99 | :popup => "Hello!", 100 | :icon => icon_options 101 | } 102 | ]) 103 | expected_icon_def = "var #{icon_options[:name]}0 = L.icon({iconUrl: '#{icon_options[:icon_url]}', shadowUrl: '#{icon_options[:shadow_url]}', iconSize: #{icon_options[:icon_size]}, shadowSize: #{icon_options[:shadow_size]}, iconAnchor: #{icon_options[:icon_anchor]}, shadowAnchor: #{icon_options[:shadow_anchor]}, popupAnchor: #{icon_options[:popup_anchor]}})" 104 | expect(result).to include(expected_icon_def) 105 | expect(result).to match(/marker = L\.marker\(\[51.52238797921441, -0.08366235665359283\], \{icon: house\d+\}\).addTo\(map\)/) 106 | expect(result).to match(/marker\.bindPopup\('Hello!'\)/) 107 | end 108 | 109 | it 'should generate many markers with popups and custom icons' do 110 | icon_options = { :name => 'house', 111 | :icon_url => 'images/house.png', 112 | :shadow_url => 'images/house_shadow.png', 113 | :icon_size => [38, 95], 114 | :shadow_size => [50, 64], 115 | :icon_anchor => [22, 94], 116 | :shadow_anchor => [4, 62], 117 | :popup_anchor => [-3, -76]} 118 | result = @view.map(:center => { 119 | :latlng => [51.52238797921441, -0.08366235665359283], 120 | :zoom => 18 121 | }, 122 | :markers => [ 123 | { 124 | :latlng => [51.52238797921441, -0.08366235665359283], 125 | :popup => "Hello!", 126 | :icon => icon_options 127 | }, 128 | { 129 | :latlng => [51.54238797921441, -0.08566235665359283], 130 | :popup => "Farewell!", 131 | :icon => icon_options 132 | } 133 | ]) 134 | expected_icon_def_1 = "var #{icon_options[:name]}0 = L.icon({iconUrl: '#{icon_options[:icon_url]}', shadowUrl: '#{icon_options[:shadow_url]}', iconSize: #{icon_options[:icon_size]}, shadowSize: #{icon_options[:shadow_size]}, iconAnchor: #{icon_options[:icon_anchor]}, shadowAnchor: #{icon_options[:shadow_anchor]}, popupAnchor: #{icon_options[:popup_anchor]}})" 135 | expected_icon_def_2 = "var #{icon_options[:name]}1 = L.icon({iconUrl: '#{icon_options[:icon_url]}', shadowUrl: '#{icon_options[:shadow_url]}', iconSize: #{icon_options[:icon_size]}, shadowSize: #{icon_options[:shadow_size]}, iconAnchor: #{icon_options[:icon_anchor]}, shadowAnchor: #{icon_options[:shadow_anchor]}, popupAnchor: #{icon_options[:popup_anchor]}})" 136 | expect(result).to include(expected_icon_def_1) 137 | expect(result).to include(expected_icon_def_2) 138 | expect(result).to match(/marker = L\.marker\(\[51.52238797921441, -0.08366235665359283\], \{icon: house\d+\}\).addTo\(map\)/) 139 | expect(result).to match(/marker = L\.marker\(\[51.54238797921441, -0.08566235665359283\], \{icon: house\d+\}\).addTo\(map\)/) 140 | expect(result).to match(/marker\.bindPopup\('Hello!'\)/) 141 | expect(result).to match(/marker\.bindPopup\('Farewell!'\)/) 142 | end 143 | 144 | it 'should have defaults for icon options' do 145 | icon_options = { :icon_url => 'images/house.png'} 146 | result = @view.map(:center => { 147 | :latlng => [51.52238797921441, -0.08366235665359283], 148 | :zoom => 18 149 | }, 150 | :markers => [ 151 | { 152 | :latlng => [51.52238797921441, -0.08366235665359283], 153 | :popup => "Hello!", 154 | :icon => icon_options 155 | } 156 | ]) 157 | expected_icon_def = "var icon0 = L.icon({iconUrl: '#{icon_options[:icon_url]}', shadowUrl: '', iconSize: [], shadowSize: [], iconAnchor: [0, 0], shadowAnchor: [0, 0], popupAnchor: [0, 0]})" 158 | expect(result).to include(expected_icon_def) 159 | expect(result).to match(/marker = L\.marker\(\[51.52238797921441, -0.08366235665359283\], \{icon: icon\d+\}\).addTo\(map\)/) 160 | expect(result).to match(/marker\.bindPopup\('Hello!'\)/) 161 | end 162 | 163 | it 'should override the method configuration options if set' do 164 | result = @view.map(:center => { 165 | :latlng => [51.52238797921441, -0.08366235665359283], 166 | :zoom => 18 167 | }, 168 | :tile_layer => "http://{s}.someotherdomain.com/blabla/{z}/{x}/{y}.png", 169 | :attribution => "Some other attribution text", 170 | :max_zoom => 4 171 | ) 172 | 173 | expect(result).to match(/L.tileLayer\('http:\/\/{s}.someotherdomain\.com\/blabla\/{z}\/{x}\/{y}\.png'/) 174 | expect(result).to match(/attribution: 'Some other attribution text'/) 175 | expect(result).to match(/maxZoom: 4/) 176 | end 177 | 178 | it 'should pass any configuration options to L.tileLayer if set' do 179 | result = @view.map(:center => { 180 | :latlng => [51.52238797921441, -0.08366235665359283], 181 | :zoom => 18 182 | }, 183 | :tile_layer => "http://{s}.someotherdomain.com/blabla/{z}/{x}/{y}.png", 184 | :attribution => "Some other attribution text", 185 | :max_zoom => 4, 186 | :some_key => 'some value', 187 | :key2 => 42 188 | ) 189 | 190 | expect(result).to match(/L.tileLayer\('http:\/\/{s}.someotherdomain\.com\/blabla\/{z}\/{x}\/{y}\.png'/) 191 | expect(result).to match(/attribution: 'Some other attribution text'/) 192 | expect(result).to match(/maxZoom: 4/) 193 | expect(result).to match(/someKey: 'some value'/) 194 | expect(result).to match(/key2: '42'/) 195 | 196 | end 197 | 198 | it 'should have multiple map on single page' do 199 | result = @view.map(:container_id => "first_map", :center => { 200 | :latlng => [51.52238797921441, -0.08366235665359283], 201 | }) 202 | 203 | result1 = @view.map(:container_id => "second_map", :center => { 204 | :latlng => [51.62238797921441, -0.08366235665359284], 205 | }) 206 | 207 | expect(result).to match(/id=\'first_map'/) 208 | expect(result).to match(/L.map\('first_map'/) 209 | 210 | expect(result1).to match(/id=\'second_map'/) 211 | expect(result1).to match(/L.map\('second_map'/) 212 | 213 | end 214 | 215 | it 'should generate a map and add a circle' do 216 | result = @view.map( 217 | :container_id => "first_map", 218 | :center => { 219 | :latlng => [51.52238797921441, -0.08366235665359283] 220 | }, 221 | :circles => [ 222 | { 223 | :latlng => [51.52238797921441, -0.08366235665359283], 224 | :radius => 12, 225 | :color => 'red', 226 | :fillColor => '#f03', 227 | :fillOpacity => 0.5 228 | } 229 | ]) 230 | expect(result).to match(/L.circle\(\[\'51.52238797921441\', \'-0.08366235665359283\'\], 12, \{ 231 | color: \'red\', 232 | fillColor: \'#f03\', 233 | fillOpacity: 0.5 234 | \}\).addTo\(map\)/) 235 | end 236 | 237 | it 'should not create the container tag if no_container is set' do 238 | result = @view.map(:center => { 239 | :latlng => [51.52238797921441, -0.08366235665359283], 240 | :zoom => 18 241 | }, 242 | :no_container => true 243 | ) 244 | 245 | expect(result).not_to match(/
/) 246 | end 247 | 248 | it 'should generate a map and add a polyline' do 249 | result = @view.map( 250 | :polylines => [{:latlngs => [[51.5, -0.08], [-51.5, 0.08]], :options => {:color => "green"}}] 251 | ) 252 | expect(result).to match(Regexp.quote('L.polyline([[51.5, -0.08], [-51.5, 0.08]],{"color":"green"}).addTo(map);')) 253 | end 254 | 255 | it 'should generate a map and add fitbounds' do 256 | result = @view.map( 257 | :fitbounds => [[51.5, -0.08],[-51.5, 0.08]] 258 | ) 259 | expect(result).to match(Regexp.quote("map.fitBounds(L.latLngBounds([[51.5, -0.08], [-51.5, 0.08]]));")) 260 | end 261 | 262 | it 'should not require a center option to generate a map' do 263 | result = @view.map({}) 264 | expect(result).not_to match(Regexp.quote("map.setView")) 265 | end 266 | end 267 | -------------------------------------------------------------------------------- /vendor/assets/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axyjo/leaflet-rails/9d9f69c59478847ae6ff0ae6df241477aa6a64c0/vendor/assets/images/layers-2x.png -------------------------------------------------------------------------------- /vendor/assets/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axyjo/leaflet-rails/9d9f69c59478847ae6ff0ae6df241477aa6a64c0/vendor/assets/images/layers.png -------------------------------------------------------------------------------- /vendor/assets/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axyjo/leaflet-rails/9d9f69c59478847ae6ff0ae6df241477aa6a64c0/vendor/assets/images/marker-icon-2x.png -------------------------------------------------------------------------------- /vendor/assets/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axyjo/leaflet-rails/9d9f69c59478847ae6ff0ae6df241477aa6a64c0/vendor/assets/images/marker-icon.png -------------------------------------------------------------------------------- /vendor/assets/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axyjo/leaflet-rails/9d9f69c59478847ae6ff0ae6df241477aa6a64c0/vendor/assets/images/marker-shadow.png -------------------------------------------------------------------------------- /vendor/assets/javascripts/leaflet.js.erb: -------------------------------------------------------------------------------- 1 | //= depend_on_asset "marker-icon-2x.png" 2 | //= depend_on_asset "marker-shadow.png" 3 | //= depend_on_asset "marker-icon.png" 4 | //= require leaflet-src 5 | <% icons = ['icon-2x.png', 'shadow.png', 'icon.png'] %> 6 | 7 | L.Icon.Default = L.Icon.Default.extend({ 8 | _getIconUrl: function (name) { 9 | var paths = <%= Hash[icons.map{|i| [i, asset_path('marker-' + i)]}].to_json %>; 10 | return paths[name + '.png']; 11 | }, 12 | 13 | _detectIconPath: function () { 14 | return ''; 15 | } 16 | }); 17 | L.Marker = L.Marker.extend({ 18 | options: { 19 | icon: new L.Icon.Default() 20 | } 21 | }); 22 | 23 | L.marker = function(latlng, options) { 24 | return new L.Marker(latlng, options); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/leaflet.css.erb: -------------------------------------------------------------------------------- 1 | //= depend_on_asset "layers.png" 2 | //= depend_on_asset "layers-2x.png" 3 | //= depend_on_asset "marker-icon.png" 4 | 5 | /* required styles */ 6 | 7 | .leaflet-pane, 8 | .leaflet-tile, 9 | .leaflet-marker-icon, 10 | .leaflet-marker-shadow, 11 | .leaflet-tile-container, 12 | .leaflet-pane > svg, 13 | .leaflet-pane > canvas, 14 | .leaflet-zoom-box, 15 | .leaflet-image-layer, 16 | .leaflet-layer { 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | } 21 | .leaflet-container { 22 | overflow: hidden; 23 | } 24 | .leaflet-tile, 25 | .leaflet-marker-icon, 26 | .leaflet-marker-shadow { 27 | -webkit-user-select: none; 28 | -moz-user-select: none; 29 | user-select: none; 30 | -webkit-user-drag: none; 31 | } 32 | /* Prevents IE11 from highlighting tiles in blue */ 33 | .leaflet-tile::selection { 34 | background: transparent; 35 | } 36 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ 37 | .leaflet-safari .leaflet-tile { 38 | image-rendering: -webkit-optimize-contrast; 39 | } 40 | /* hack that prevents hw layers "stretching" when loading new tiles */ 41 | .leaflet-safari .leaflet-tile-container { 42 | width: 1600px; 43 | height: 1600px; 44 | -webkit-transform-origin: 0 0; 45 | } 46 | .leaflet-marker-icon, 47 | .leaflet-marker-shadow { 48 | display: block; 49 | } 50 | /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ 51 | /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ 52 | .leaflet-container .leaflet-overlay-pane svg { 53 | max-width: none !important; 54 | max-height: none !important; 55 | } 56 | .leaflet-container .leaflet-marker-pane img, 57 | .leaflet-container .leaflet-shadow-pane img, 58 | .leaflet-container .leaflet-tile-pane img, 59 | .leaflet-container img.leaflet-image-layer, 60 | .leaflet-container .leaflet-tile { 61 | max-width: none !important; 62 | max-height: none !important; 63 | width: auto; 64 | padding: 0; 65 | } 66 | 67 | .leaflet-container img.leaflet-tile { 68 | /* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */ 69 | mix-blend-mode: plus-lighter; 70 | } 71 | 72 | .leaflet-container.leaflet-touch-zoom { 73 | -ms-touch-action: pan-x pan-y; 74 | touch-action: pan-x pan-y; 75 | } 76 | .leaflet-container.leaflet-touch-drag { 77 | -ms-touch-action: pinch-zoom; 78 | /* Fallback for FF which doesn't support pinch-zoom */ 79 | touch-action: none; 80 | touch-action: pinch-zoom; 81 | } 82 | .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { 83 | -ms-touch-action: none; 84 | touch-action: none; 85 | } 86 | .leaflet-container { 87 | -webkit-tap-highlight-color: transparent; 88 | } 89 | .leaflet-container a { 90 | -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); 91 | } 92 | .leaflet-tile { 93 | filter: inherit; 94 | visibility: hidden; 95 | } 96 | .leaflet-tile-loaded { 97 | visibility: inherit; 98 | } 99 | .leaflet-zoom-box { 100 | width: 0; 101 | height: 0; 102 | -moz-box-sizing: border-box; 103 | box-sizing: border-box; 104 | z-index: 800; 105 | } 106 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 107 | .leaflet-overlay-pane svg { 108 | -moz-user-select: none; 109 | } 110 | 111 | .leaflet-pane { z-index: 400; } 112 | 113 | .leaflet-tile-pane { z-index: 200; } 114 | .leaflet-overlay-pane { z-index: 400; } 115 | .leaflet-shadow-pane { z-index: 500; } 116 | .leaflet-marker-pane { z-index: 600; } 117 | .leaflet-tooltip-pane { z-index: 650; } 118 | .leaflet-popup-pane { z-index: 700; } 119 | 120 | .leaflet-map-pane canvas { z-index: 100; } 121 | .leaflet-map-pane svg { z-index: 200; } 122 | 123 | .leaflet-vml-shape { 124 | width: 1px; 125 | height: 1px; 126 | } 127 | .lvml { 128 | behavior: url(#default#VML); 129 | display: inline-block; 130 | position: absolute; 131 | } 132 | 133 | 134 | /* control positioning */ 135 | 136 | .leaflet-control { 137 | position: relative; 138 | z-index: 800; 139 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 140 | pointer-events: auto; 141 | } 142 | .leaflet-top, 143 | .leaflet-bottom { 144 | position: absolute; 145 | z-index: 1000; 146 | pointer-events: none; 147 | } 148 | .leaflet-top { 149 | top: 0; 150 | } 151 | .leaflet-right { 152 | right: 0; 153 | } 154 | .leaflet-bottom { 155 | bottom: 0; 156 | } 157 | .leaflet-left { 158 | left: 0; 159 | } 160 | .leaflet-control { 161 | float: left; 162 | clear: both; 163 | } 164 | .leaflet-right .leaflet-control { 165 | float: right; 166 | } 167 | .leaflet-top .leaflet-control { 168 | margin-top: 10px; 169 | } 170 | .leaflet-bottom .leaflet-control { 171 | margin-bottom: 10px; 172 | } 173 | .leaflet-left .leaflet-control { 174 | margin-left: 10px; 175 | } 176 | .leaflet-right .leaflet-control { 177 | margin-right: 10px; 178 | } 179 | 180 | 181 | /* zoom and fade animations */ 182 | 183 | .leaflet-fade-anim .leaflet-popup { 184 | opacity: 0; 185 | -webkit-transition: opacity 0.2s linear; 186 | -moz-transition: opacity 0.2s linear; 187 | transition: opacity 0.2s linear; 188 | } 189 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 190 | opacity: 1; 191 | } 192 | .leaflet-zoom-animated { 193 | -webkit-transform-origin: 0 0; 194 | -ms-transform-origin: 0 0; 195 | transform-origin: 0 0; 196 | } 197 | svg.leaflet-zoom-animated { 198 | will-change: transform; 199 | } 200 | 201 | .leaflet-zoom-anim .leaflet-zoom-animated { 202 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 203 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 204 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 205 | } 206 | .leaflet-zoom-anim .leaflet-tile, 207 | .leaflet-pan-anim .leaflet-tile { 208 | -webkit-transition: none; 209 | -moz-transition: none; 210 | transition: none; 211 | } 212 | 213 | .leaflet-zoom-anim .leaflet-zoom-hide { 214 | visibility: hidden; 215 | } 216 | 217 | 218 | /* cursors */ 219 | 220 | .leaflet-interactive { 221 | cursor: pointer; 222 | } 223 | .leaflet-grab { 224 | cursor: -webkit-grab; 225 | cursor: -moz-grab; 226 | cursor: grab; 227 | } 228 | .leaflet-crosshair, 229 | .leaflet-crosshair .leaflet-interactive { 230 | cursor: crosshair; 231 | } 232 | .leaflet-popup-pane, 233 | .leaflet-control { 234 | cursor: auto; 235 | } 236 | .leaflet-dragging .leaflet-grab, 237 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 238 | .leaflet-dragging .leaflet-marker-draggable { 239 | cursor: move; 240 | cursor: -webkit-grabbing; 241 | cursor: -moz-grabbing; 242 | cursor: grabbing; 243 | } 244 | 245 | /* marker & overlays interactivity */ 246 | .leaflet-marker-icon, 247 | .leaflet-marker-shadow, 248 | .leaflet-image-layer, 249 | .leaflet-pane > svg path, 250 | .leaflet-tile-container { 251 | pointer-events: none; 252 | } 253 | 254 | .leaflet-marker-icon.leaflet-interactive, 255 | .leaflet-image-layer.leaflet-interactive, 256 | .leaflet-pane > svg path.leaflet-interactive, 257 | svg.leaflet-image-layer.leaflet-interactive path { 258 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 259 | pointer-events: auto; 260 | } 261 | 262 | /* visual tweaks */ 263 | 264 | .leaflet-container { 265 | background: #ddd; 266 | outline-offset: 1px; 267 | } 268 | .leaflet-container a { 269 | color: #0078A8; 270 | } 271 | .leaflet-zoom-box { 272 | border: 2px dotted #38f; 273 | background: rgba(255,255,255,0.5); 274 | } 275 | 276 | 277 | /* general typography */ 278 | .leaflet-container { 279 | font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; 280 | font-size: 12px; 281 | font-size: 0.75rem; 282 | line-height: 1.5; 283 | } 284 | 285 | 286 | /* general toolbar styles */ 287 | 288 | .leaflet-bar { 289 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 290 | border-radius: 4px; 291 | } 292 | .leaflet-bar a { 293 | background-color: #fff; 294 | border-bottom: 1px solid #ccc; 295 | width: 26px; 296 | height: 26px; 297 | line-height: 26px; 298 | display: block; 299 | text-align: center; 300 | text-decoration: none; 301 | color: black; 302 | } 303 | .leaflet-bar a, 304 | .leaflet-control-layers-toggle { 305 | background-position: 50% 50%; 306 | background-repeat: no-repeat; 307 | display: block; 308 | } 309 | .leaflet-bar a:hover, 310 | .leaflet-bar a:focus { 311 | background-color: #f4f4f4; 312 | } 313 | .leaflet-bar a:first-child { 314 | border-top-left-radius: 4px; 315 | border-top-right-radius: 4px; 316 | } 317 | .leaflet-bar a:last-child { 318 | border-bottom-left-radius: 4px; 319 | border-bottom-right-radius: 4px; 320 | border-bottom: none; 321 | } 322 | .leaflet-bar a.leaflet-disabled { 323 | cursor: default; 324 | background-color: #f4f4f4; 325 | color: #bbb; 326 | } 327 | 328 | .leaflet-touch .leaflet-bar a { 329 | width: 30px; 330 | height: 30px; 331 | line-height: 30px; 332 | } 333 | .leaflet-touch .leaflet-bar a:first-child { 334 | border-top-left-radius: 2px; 335 | border-top-right-radius: 2px; 336 | } 337 | .leaflet-touch .leaflet-bar a:last-child { 338 | border-bottom-left-radius: 2px; 339 | border-bottom-right-radius: 2px; 340 | } 341 | 342 | /* zoom control */ 343 | 344 | .leaflet-control-zoom-in, 345 | .leaflet-control-zoom-out { 346 | font: bold 18px 'Lucida Console', Monaco, monospace; 347 | text-indent: 1px; 348 | } 349 | 350 | .leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { 351 | font-size: 22px; 352 | } 353 | 354 | 355 | /* layers control */ 356 | 357 | .leaflet-control-layers { 358 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 359 | background: #fff; 360 | border-radius: 5px; 361 | } 362 | .leaflet-control-layers-toggle { 363 | background-image: url(<%= asset_path 'layers.png' %>); 364 | width: 36px; 365 | height: 36px; 366 | } 367 | .leaflet-retina .leaflet-control-layers-toggle { 368 | background-image: url(<%= asset_path 'layers-2x.png' %>); 369 | background-size: 26px 26px; 370 | } 371 | .leaflet-touch .leaflet-control-layers-toggle { 372 | width: 44px; 373 | height: 44px; 374 | } 375 | .leaflet-control-layers .leaflet-control-layers-list, 376 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 377 | display: none; 378 | } 379 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 380 | display: block; 381 | position: relative; 382 | } 383 | .leaflet-control-layers-expanded { 384 | padding: 6px 10px 6px 6px; 385 | color: #333; 386 | background: #fff; 387 | } 388 | .leaflet-control-layers-scrollbar { 389 | overflow-y: scroll; 390 | overflow-x: hidden; 391 | padding-right: 5px; 392 | } 393 | .leaflet-control-layers-selector { 394 | margin-top: 2px; 395 | position: relative; 396 | top: 1px; 397 | } 398 | .leaflet-control-layers label { 399 | display: block; 400 | font-size: 13px; 401 | font-size: 1.08333em; 402 | } 403 | .leaflet-control-layers-separator { 404 | height: 0; 405 | border-top: 1px solid #ddd; 406 | margin: 5px -10px 5px -6px; 407 | } 408 | 409 | /* Default icon URLs */ 410 | .leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */ 411 | background-image: url(<%= asset_path 'marker-icon.png' %>); 412 | } 413 | 414 | 415 | /* attribution and scale controls */ 416 | 417 | .leaflet-container .leaflet-control-attribution { 418 | background: #fff; 419 | background: rgba(255, 255, 255, 0.8); 420 | margin: 0; 421 | } 422 | .leaflet-control-attribution, 423 | .leaflet-control-scale-line { 424 | padding: 0 5px; 425 | color: #333; 426 | line-height: 1.4; 427 | } 428 | .leaflet-control-attribution a { 429 | text-decoration: none; 430 | } 431 | .leaflet-control-attribution a:hover, 432 | .leaflet-control-attribution a:focus { 433 | text-decoration: underline; 434 | } 435 | .leaflet-attribution-flag { 436 | display: inline !important; 437 | vertical-align: baseline !important; 438 | width: 1em; 439 | height: 0.6669em; 440 | } 441 | .leaflet-left .leaflet-control-scale { 442 | margin-left: 5px; 443 | } 444 | .leaflet-bottom .leaflet-control-scale { 445 | margin-bottom: 5px; 446 | } 447 | .leaflet-control-scale-line { 448 | border: 2px solid #777; 449 | border-top: none; 450 | line-height: 1.1; 451 | padding: 2px 5px 1px; 452 | white-space: nowrap; 453 | -moz-box-sizing: border-box; 454 | box-sizing: border-box; 455 | background: rgba(255, 255, 255, 0.8); 456 | text-shadow: 1px 1px #fff; 457 | } 458 | .leaflet-control-scale-line:not(:first-child) { 459 | border-top: 2px solid #777; 460 | border-bottom: none; 461 | margin-top: -2px; 462 | } 463 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 464 | border-bottom: 2px solid #777; 465 | } 466 | 467 | .leaflet-touch .leaflet-control-attribution, 468 | .leaflet-touch .leaflet-control-layers, 469 | .leaflet-touch .leaflet-bar { 470 | box-shadow: none; 471 | } 472 | .leaflet-touch .leaflet-control-layers, 473 | .leaflet-touch .leaflet-bar { 474 | border: 2px solid rgba(0,0,0,0.2); 475 | background-clip: padding-box; 476 | } 477 | 478 | 479 | /* popup */ 480 | 481 | .leaflet-popup { 482 | position: absolute; 483 | text-align: center; 484 | margin-bottom: 20px; 485 | } 486 | .leaflet-popup-content-wrapper { 487 | padding: 1px; 488 | text-align: left; 489 | border-radius: 12px; 490 | } 491 | .leaflet-popup-content { 492 | margin: 13px 24px 13px 20px; 493 | line-height: 1.3; 494 | font-size: 13px; 495 | font-size: 1.08333em; 496 | min-height: 1px; 497 | } 498 | .leaflet-popup-content p { 499 | margin: 17px 0; 500 | margin: 1.3em 0; 501 | } 502 | .leaflet-popup-tip-container { 503 | width: 40px; 504 | height: 20px; 505 | position: absolute; 506 | left: 50%; 507 | margin-top: -1px; 508 | margin-left: -20px; 509 | overflow: hidden; 510 | pointer-events: none; 511 | } 512 | .leaflet-popup-tip { 513 | width: 17px; 514 | height: 17px; 515 | padding: 1px; 516 | 517 | margin: -10px auto 0; 518 | pointer-events: auto; 519 | 520 | -webkit-transform: rotate(45deg); 521 | -moz-transform: rotate(45deg); 522 | -ms-transform: rotate(45deg); 523 | transform: rotate(45deg); 524 | } 525 | .leaflet-popup-content-wrapper, 526 | .leaflet-popup-tip { 527 | background: white; 528 | color: #333; 529 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 530 | } 531 | .leaflet-container a.leaflet-popup-close-button { 532 | position: absolute; 533 | top: 0; 534 | right: 0; 535 | border: none; 536 | text-align: center; 537 | width: 24px; 538 | height: 24px; 539 | font: 16px/24px Tahoma, Verdana, sans-serif; 540 | color: #757575; 541 | text-decoration: none; 542 | background: transparent; 543 | } 544 | .leaflet-container a.leaflet-popup-close-button:hover, 545 | .leaflet-container a.leaflet-popup-close-button:focus { 546 | color: #585858; 547 | } 548 | .leaflet-popup-scrolled { 549 | overflow: auto; 550 | } 551 | 552 | .leaflet-oldie .leaflet-popup-content-wrapper { 553 | -ms-zoom: 1; 554 | } 555 | .leaflet-oldie .leaflet-popup-tip { 556 | width: 24px; 557 | margin: 0 auto; 558 | 559 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 560 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 561 | } 562 | 563 | .leaflet-oldie .leaflet-control-zoom, 564 | .leaflet-oldie .leaflet-control-layers, 565 | .leaflet-oldie .leaflet-popup-content-wrapper, 566 | .leaflet-oldie .leaflet-popup-tip { 567 | border: 1px solid #999; 568 | } 569 | 570 | 571 | /* div icon */ 572 | 573 | .leaflet-div-icon { 574 | background: #fff; 575 | border: 1px solid #666; 576 | } 577 | 578 | 579 | /* Tooltip */ 580 | /* Base styles for the element that has a tooltip */ 581 | .leaflet-tooltip { 582 | position: absolute; 583 | padding: 6px; 584 | background-color: #fff; 585 | border: 1px solid #fff; 586 | border-radius: 3px; 587 | color: #222; 588 | white-space: nowrap; 589 | -webkit-user-select: none; 590 | -moz-user-select: none; 591 | -ms-user-select: none; 592 | user-select: none; 593 | pointer-events: none; 594 | box-shadow: 0 1px 3px rgba(0,0,0,0.4); 595 | } 596 | .leaflet-tooltip.leaflet-interactive { 597 | cursor: pointer; 598 | pointer-events: auto; 599 | } 600 | .leaflet-tooltip-top:before, 601 | .leaflet-tooltip-bottom:before, 602 | .leaflet-tooltip-left:before, 603 | .leaflet-tooltip-right:before { 604 | position: absolute; 605 | pointer-events: none; 606 | border: 6px solid transparent; 607 | background: transparent; 608 | content: ""; 609 | } 610 | 611 | /* Directions */ 612 | 613 | .leaflet-tooltip-bottom { 614 | margin-top: 6px; 615 | } 616 | .leaflet-tooltip-top { 617 | margin-top: -6px; 618 | } 619 | .leaflet-tooltip-bottom:before, 620 | .leaflet-tooltip-top:before { 621 | left: 50%; 622 | margin-left: -6px; 623 | } 624 | .leaflet-tooltip-top:before { 625 | bottom: 0; 626 | margin-bottom: -12px; 627 | border-top-color: #fff; 628 | } 629 | .leaflet-tooltip-bottom:before { 630 | top: 0; 631 | margin-top: -12px; 632 | margin-left: -6px; 633 | border-bottom-color: #fff; 634 | } 635 | .leaflet-tooltip-left { 636 | margin-left: -6px; 637 | } 638 | .leaflet-tooltip-right { 639 | margin-left: 6px; 640 | } 641 | .leaflet-tooltip-left:before, 642 | .leaflet-tooltip-right:before { 643 | top: 50%; 644 | margin-top: -6px; 645 | } 646 | .leaflet-tooltip-left:before { 647 | right: 0; 648 | margin-right: -12px; 649 | border-left-color: #fff; 650 | } 651 | .leaflet-tooltip-right:before { 652 | left: 0; 653 | margin-left: -12px; 654 | border-right-color: #fff; 655 | } 656 | 657 | /* Printing */ 658 | 659 | @media print { 660 | /* Prevent printers from removing background-images of controls. */ 661 | .leaflet-control { 662 | -webkit-print-color-adjust: exact; 663 | print-color-adjust: exact; 664 | } 665 | } 666 | --------------------------------------------------------------------------------