mapkick-rb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +193 -0
- data/lib/mapkick/engine.rb +12 -0
- data/lib/mapkick/helper.rb +101 -0
- data/lib/mapkick/sinatra.rb +5 -0
- data/lib/mapkick/utils.rb +24 -0
- data/lib/mapkick/version.rb +3 -0
- data/lib/mapkick-rb.rb +1 -0
- data/lib/mapkick.rb +27 -0
- data/licenses/LICENSE-mapkick-bundle.txt +1029 -0
- data/vendor/assets/javascripts/mapkick.bundle.js +828 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7678cfa0645e5386090c2ce73f06ffca32e0cbe3d3643b23d241ac80036d8c9d
|
4
|
+
data.tar.gz: 348c96a8b02cc983dda7fcc4a655bd2bb276d20b210290d6cce424b669449eac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 466776e05996b2a6cd0144c7f68e627696b6bbf85742f700ceab060a347a444a8152aa2b8fe6f30d4f0ca677985e5cbc93b7ea80c9c736500ab6e609ae620f04
|
7
|
+
data.tar.gz: 68f2cf1f40bdd1242a2b91ade1bfce042bf2a234c23a64ec87b9989bf6708adcbe822d11e2c477714f989ec151b15358ac082773593f0ad400730c169d12b7a2
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2023 Andrew Kane
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# Mapkick
|
2
|
+
|
3
|
+
Create beautiful JavaScript maps with one line of Ruby. No more fighting with mapping libraries!
|
4
|
+
|
5
|
+
[See it in action](https://chartkick.com/mapkick)
|
6
|
+
|
7
|
+
:fire: For charts, check out [Chartkick](https://chartkick.com)
|
8
|
+
|
9
|
+
[![Build Status](https://github.com/ankane/mapkick/workflows/build/badge.svg?branch=master)](https://github.com/ankane/mapkick/actions)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application’s Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem "mapkick-rb"
|
17
|
+
```
|
18
|
+
|
19
|
+
Mapkick uses [Mapbox GL JS v1](https://github.com/mapbox/mapbox-gl-js/tree/v1.13.3). To use tiles from Mapbox, [create a Mapbox account](https://account.mapbox.com/auth/signup/) to get an access token and set `ENV["MAPBOX_ACCESS_TOKEN"]` in your environment.
|
20
|
+
|
21
|
+
Then follow the instructions for your JavaScript setup:
|
22
|
+
|
23
|
+
- [Importmap](#importmap) (Rails 7 default)
|
24
|
+
- [esbuild, rollup.js, or Webpack](#esbuild-rollupjs-or-webpack)
|
25
|
+
- [Webpacker](#webpacker) (Rails 6 default)
|
26
|
+
- [Sprockets](#sprockets)
|
27
|
+
|
28
|
+
### Importmap
|
29
|
+
|
30
|
+
In `config/importmap.rb`, add:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
pin "mapkick/bundle", to: "mapkick.bundle.js"
|
34
|
+
```
|
35
|
+
|
36
|
+
And in `app/javascript/application.js`, add:
|
37
|
+
|
38
|
+
```js
|
39
|
+
import "mapkick/bundle"
|
40
|
+
```
|
41
|
+
|
42
|
+
### esbuild, rollup.js, or Webpack
|
43
|
+
|
44
|
+
Run:
|
45
|
+
|
46
|
+
```sh
|
47
|
+
yarn add mapkick
|
48
|
+
```
|
49
|
+
|
50
|
+
And in `app/javascript/application.js`, add:
|
51
|
+
|
52
|
+
```js
|
53
|
+
import "mapkick/bundle"
|
54
|
+
```
|
55
|
+
|
56
|
+
Note: For rollup.js, this requires `format: "iife"` in `rollup.config.js`.
|
57
|
+
|
58
|
+
### Webpacker
|
59
|
+
|
60
|
+
Run:
|
61
|
+
|
62
|
+
```sh
|
63
|
+
yarn add mapkick
|
64
|
+
```
|
65
|
+
|
66
|
+
And in `app/javascript/packs/application.js`, add:
|
67
|
+
|
68
|
+
```js
|
69
|
+
import "mapkick/bundle"
|
70
|
+
```
|
71
|
+
|
72
|
+
### Sprockets
|
73
|
+
|
74
|
+
In `app/assets/javascripts/application.js`, add:
|
75
|
+
|
76
|
+
```js
|
77
|
+
//= require mapkick.bundle
|
78
|
+
```
|
79
|
+
|
80
|
+
## Maps
|
81
|
+
|
82
|
+
Create a map
|
83
|
+
|
84
|
+
```erb
|
85
|
+
<%= js_map [{latitude: 1.23, longitude: 4.56}] %>
|
86
|
+
```
|
87
|
+
|
88
|
+
## Data
|
89
|
+
|
90
|
+
Data can be an array
|
91
|
+
|
92
|
+
```erb
|
93
|
+
<%= js_map [{latitude: 1.23, longitude: 4.56}] %>
|
94
|
+
```
|
95
|
+
|
96
|
+
Or a URL that returns JSON (same format as above)
|
97
|
+
|
98
|
+
```erb
|
99
|
+
<%= js_map cities_path %>
|
100
|
+
```
|
101
|
+
|
102
|
+
You can use `latitude`, `lat`, `longitude`, `lon`, and `lng`
|
103
|
+
|
104
|
+
You can specify a label and tooltip for each data point
|
105
|
+
|
106
|
+
```javascript
|
107
|
+
{
|
108
|
+
latitude: ...,
|
109
|
+
longitude: ...,
|
110
|
+
label: "Hot Chicken Takeover",
|
111
|
+
tooltip: "5 stars"
|
112
|
+
}
|
113
|
+
```
|
114
|
+
|
115
|
+
## Options
|
116
|
+
|
117
|
+
Id, width, and height
|
118
|
+
|
119
|
+
```erb
|
120
|
+
<%= js_map data, id: "cities-map", width: "800px", height: "500px" %>
|
121
|
+
```
|
122
|
+
|
123
|
+
Markers
|
124
|
+
|
125
|
+
```erb
|
126
|
+
<%= js_map data, markers: {color: "#f84d4d"} %>
|
127
|
+
```
|
128
|
+
|
129
|
+
Tooltips
|
130
|
+
|
131
|
+
```erb
|
132
|
+
<%= js_map data, tooltips: {hover: false, html: true} %>
|
133
|
+
```
|
134
|
+
|
135
|
+
Map style
|
136
|
+
|
137
|
+
```erb
|
138
|
+
<%= js_map data, style: "mapbox://styles/mapbox/outdoors-v12" %>
|
139
|
+
```
|
140
|
+
|
141
|
+
Zoom and controls
|
142
|
+
|
143
|
+
```erb
|
144
|
+
<%= js_map data, zoom: 15, controls: true %>
|
145
|
+
```
|
146
|
+
|
147
|
+
Refresh data from a remote source every `n` seconds
|
148
|
+
|
149
|
+
```erb
|
150
|
+
<%= js_map url, refresh: 60 %>
|
151
|
+
```
|
152
|
+
|
153
|
+
### Global Options
|
154
|
+
|
155
|
+
To set options for all of your maps, create an initializer `config/initializers/mapkick.rb` with:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
Mapkick.options[:height] = "400px"
|
159
|
+
```
|
160
|
+
|
161
|
+
## Sinatra and Padrino
|
162
|
+
|
163
|
+
Download [mapkick.bundle.js](https://raw.githubusercontent.com/ankane/mapkick/master/vendor/assets/javascripts/mapkick.js) and include it manually.
|
164
|
+
|
165
|
+
```html
|
166
|
+
<script src="mapkick.bundle.js"></script>
|
167
|
+
```
|
168
|
+
|
169
|
+
## No Ruby? No Problem
|
170
|
+
|
171
|
+
Check out [mapkick.js](https://github.com/ankane/mapkick.js)
|
172
|
+
|
173
|
+
## History
|
174
|
+
|
175
|
+
View the [changelog](CHANGELOG.md)
|
176
|
+
|
177
|
+
## Contributing
|
178
|
+
|
179
|
+
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
180
|
+
|
181
|
+
- [Report bugs](https://github.com/ankane/mapkick/issues)
|
182
|
+
- Fix bugs and [submit pull requests](https://github.com/ankane/mapkick/pulls)
|
183
|
+
- Write, clarify, or fix documentation
|
184
|
+
- Suggest or add new features
|
185
|
+
|
186
|
+
To get started with development:
|
187
|
+
|
188
|
+
```sh
|
189
|
+
git clone https://github.com/ankane/mapkick.git
|
190
|
+
cd mapkick
|
191
|
+
bundle install
|
192
|
+
bundle exec rake test
|
193
|
+
```
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Mapkick
|
2
|
+
module Helper
|
3
|
+
# don't break out options since need to merge with default options
|
4
|
+
def js_map(data_source, **options)
|
5
|
+
options = Mapkick::Utils.deep_merge(Mapkick.options, options)
|
6
|
+
|
7
|
+
@mapkick_map_id ||= 0
|
8
|
+
element_id = options.delete(:id) || "map-#{@mapkick_map_id += 1}"
|
9
|
+
|
10
|
+
height = (options.delete(:height) || "500px").to_s
|
11
|
+
width = (options.delete(:width) || "100%").to_s
|
12
|
+
|
13
|
+
nonce = options.fetch(:nonce, true)
|
14
|
+
options.delete(:nonce)
|
15
|
+
if nonce == true
|
16
|
+
# Secure Headers also defines content_security_policy_nonce but it takes an argument
|
17
|
+
# Rails 5.2 overrides this method, but earlier versions do not
|
18
|
+
if respond_to?(:content_security_policy_nonce) && (content_security_policy_nonce rescue nil)
|
19
|
+
# Rails 5.2+
|
20
|
+
nonce = content_security_policy_nonce
|
21
|
+
elsif respond_to?(:content_security_policy_script_nonce)
|
22
|
+
# Secure Headers
|
23
|
+
nonce = content_security_policy_script_nonce
|
24
|
+
else
|
25
|
+
nonce = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
nonce_html = nonce ? " nonce=\"#{ERB::Util.html_escape(nonce)}\"" : nil
|
29
|
+
|
30
|
+
# html vars
|
31
|
+
html_vars = {
|
32
|
+
id: element_id,
|
33
|
+
height: height,
|
34
|
+
width: width,
|
35
|
+
# don't delete loading option since it needs to be passed to JS
|
36
|
+
loading: options[:loading] || "Loading..."
|
37
|
+
}
|
38
|
+
|
39
|
+
[:height, :width].each do |k|
|
40
|
+
# limit to alphanumeric and % for simplicity
|
41
|
+
# this prevents things like calc() but safety is the priority
|
42
|
+
# dot does not need escaped in square brackets
|
43
|
+
raise ArgumentError, "Invalid #{k}" unless html_vars[k] =~ /\A[a-zA-Z0-9%.]*\z/
|
44
|
+
end
|
45
|
+
|
46
|
+
html_vars.each_key do |k|
|
47
|
+
# escape all variables
|
48
|
+
# we already limit height and width above, but escape for safety as fail-safe
|
49
|
+
# to prevent XSS injection in worse-case scenario
|
50
|
+
html_vars[k] = ERB::Util.html_escape(html_vars[k])
|
51
|
+
end
|
52
|
+
|
53
|
+
html = %(<div id="%{id}" style="height: %{height}; width: %{width};"><div style="height: %{height}; text-align: center; color: #999; line-height: %{height}; font-size: 14px; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif;">%{loading}</div></div>) % html_vars
|
54
|
+
|
55
|
+
# access token
|
56
|
+
access_token = options.delete(:access_token) || options.delete(:accessToken) || ENV["MAPBOX_ACCESS_TOKEN"]
|
57
|
+
if access_token
|
58
|
+
# can bypass with string keys
|
59
|
+
# but should help prevent common errors
|
60
|
+
if access_token.start_with?("sk.")
|
61
|
+
raise Mapkick::Error, "Expected public access token"
|
62
|
+
elsif !access_token.start_with?("pk.")
|
63
|
+
raise Mapkick::Error, "Invalid access token"
|
64
|
+
end
|
65
|
+
options[:accessToken] = access_token
|
66
|
+
end
|
67
|
+
|
68
|
+
# js vars
|
69
|
+
js_vars = {
|
70
|
+
id: element_id,
|
71
|
+
data: data_source,
|
72
|
+
options: options
|
73
|
+
}
|
74
|
+
js_vars.each_key do |k|
|
75
|
+
js_vars[k] = Mapkick::Utils.json_escape(js_vars[k].to_json)
|
76
|
+
end
|
77
|
+
createjs = "new Mapkick.Map(%{id}, %{data}, %{options});" % js_vars
|
78
|
+
|
79
|
+
# don't rerun JS on preview
|
80
|
+
js = <<~JS
|
81
|
+
<script#{nonce_html}>
|
82
|
+
(function() {
|
83
|
+
if (document.documentElement.hasAttribute("data-turbolinks-preview")) return;
|
84
|
+
if (document.documentElement.hasAttribute("data-turbo-preview")) return;
|
85
|
+
|
86
|
+
var createMap = function() { #{createjs} };
|
87
|
+
if ("Mapkick" in window) {
|
88
|
+
createMap();
|
89
|
+
} else {
|
90
|
+
window.addEventListener("mapkick:load", createMap, true);
|
91
|
+
}
|
92
|
+
})();
|
93
|
+
</script>
|
94
|
+
JS
|
95
|
+
|
96
|
+
html += "\n#{js}"
|
97
|
+
|
98
|
+
html.respond_to?(:html_safe) ? html.html_safe : html
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mapkick
|
2
|
+
module Utils
|
3
|
+
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
|
4
|
+
def self.deep_merge(hash_a, hash_b)
|
5
|
+
hash_a = hash_a.dup
|
6
|
+
hash_b.each_pair do |k, v|
|
7
|
+
tv = hash_a[k]
|
8
|
+
hash_a[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? deep_merge(tv, v) : v
|
9
|
+
end
|
10
|
+
hash_a
|
11
|
+
end
|
12
|
+
|
13
|
+
# from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/output_safety.rb
|
14
|
+
JSON_ESCAPE = { "&" => '\u0026', ">" => '\u003e', "<" => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
|
15
|
+
JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
|
16
|
+
def self.json_escape(s)
|
17
|
+
if ERB::Util.respond_to?(:json_escape)
|
18
|
+
ERB::Util.json_escape(s)
|
19
|
+
else
|
20
|
+
s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/mapkick-rb.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "mapkick"
|
data/lib/mapkick.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# stdlib
|
2
|
+
require "json"
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
# modules
|
6
|
+
require_relative "mapkick/helper"
|
7
|
+
require_relative "mapkick/utils"
|
8
|
+
require_relative "mapkick/version"
|
9
|
+
|
10
|
+
# integrations
|
11
|
+
require_relative "mapkick/engine" if defined?(Rails)
|
12
|
+
require_relative "mapkick/sinatra" if defined?(Sinatra)
|
13
|
+
|
14
|
+
if defined?(ActiveSupport.on_load)
|
15
|
+
ActiveSupport.on_load(:action_view) do
|
16
|
+
include Mapkick::Helper
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Mapkick
|
21
|
+
class Error < StandardError; end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_accessor :options
|
25
|
+
end
|
26
|
+
self.options = {}
|
27
|
+
end
|