mapkick-rb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 (2023-01-19)
2
+
3
+ - First release
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,12 @@
1
+ module Mapkick
2
+ class Engine < ::Rails::Engine
3
+ # for assets
4
+
5
+ # for importmap
6
+ initializer "mapkick.importmap" do |app|
7
+ if defined?(Importmap)
8
+ app.config.assets.precompile << "mapkick.bundle.js"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -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,5 @@
1
+ require "sinatra/base"
2
+
3
+ class Sinatra::Base
4
+ helpers Mapkick::Helper
5
+ 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
@@ -0,0 +1,3 @@
1
+ module Mapkick
2
+ VERSION = "0.1.0"
3
+ 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