├── assets ├── css │ └── map.css └── js │ └── map.js ├── LICENSE ├── simplemap.php └── README.md /assets/css/map.css: -------------------------------------------------------------------------------- 1 | .simplemap-canvas { 2 | padding-top: 56.25%; 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Hristiyan Dodov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /simplemap.php: -------------------------------------------------------------------------------- 1 | array('map.css'), 8 | 'js' => array('map.js') 9 | ); 10 | 11 | public function get_field_data() { 12 | if (!empty($this->map)) { 13 | $mapSettings = $this->map; 14 | } else { 15 | $mapSettings = array( 16 | 'zoom' => 2, 17 | 'center' => array( 18 | 'lat' => 0, 19 | 'lng' => 0 20 | ) 21 | ); 22 | } 23 | 24 | if (!empty($this->value)) { 25 | $parsed = (array)yaml::decode($this->value); // get saved values 26 | $mapSettings['zoom'] = $parsed['zoom']; 27 | $mapSettings['center'] = array( 28 | 'lat' => $parsed['lat'], 29 | 'lng' => $parsed['lng'] 30 | ); 31 | } 32 | 33 | $data = array( 34 | 'key' => c::get('google.maps.key'), 35 | 'lang' => panel()->translation()->code(), 36 | 'map' => $mapSettings 37 | ); 38 | 39 | if (!empty($this->marker)) { 40 | $data['marker'] = $this->marker; 41 | } 42 | 43 | if (!empty($this->style)) { 44 | $styleJson = c::get('google.maps.styles.' . $this->style); 45 | 46 | if ($styleJson) { 47 | $data['style'] = $styleJson; 48 | } 49 | } 50 | 51 | return $data; 52 | } 53 | 54 | public function content() { 55 | $fieldData = $this->get_field_data(); 56 | 57 | $field = new Brick('div'); 58 | $field->addClass('field-content'); 59 | 60 | if (empty($fieldData['key'])) { 61 | $field->append('Missing Google Maps API key config setting.'); 62 | return $field; 63 | } 64 | 65 | $field->data('simplemap', json_encode($fieldData, JSON_NUMERIC_CHECK)); 66 | $field->data('field', 'kirbySimplemap'); // jQuery plugin, defined in assets/js/map.js 67 | 68 | $map = new Brick('div'); 69 | $map->addClass('simplemap-canvas input'); 70 | 71 | if ($this->readonly() || $this->disabled()) { 72 | $map->addClass('is-disabled'); 73 | } 74 | 75 | if (!empty($this->ratio)) { 76 | $ratioParts = explode(':', $this->ratio); 77 | 78 | if (count($ratioParts) == 2 && $ratioParts[0] > 0) { 79 | $paddingValue = ($ratioParts[1] / $ratioParts[0]) * 100; 80 | $map->attr('style', "padding-top: $paddingValue%;"); 81 | } 82 | } 83 | 84 | $field->append($map); 85 | $field->append($this->create_input('lat', $fieldData['map']['center']['lat'])); 86 | $field->append($this->create_input('lng', $fieldData['map']['center']['lng'])); 87 | $field->append($this->create_input('zoom', $fieldData['map']['zoom'])); 88 | 89 | return $field; 90 | } 91 | 92 | public function create_input($key, $value) { 93 | $input = new Brick('input'); 94 | $input->attr('type', 'hidden'); 95 | $input->attr('name', $this->name() . "[$key]"); 96 | $input->addClass("simplemap-$key"); 97 | $input->val($value); 98 | 99 | return $input; 100 | } 101 | 102 | public function result() { 103 | $input = parent::result(); 104 | return yaml::encode($input); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /assets/js/map.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | var mapsPromise; 3 | 4 | function whenGoogleMapsLoaded(params) { 5 | if (mapsPromise) { 6 | return mapsPromise; 7 | } 8 | 9 | if ( 10 | typeof google != 'undefined' && 11 | typeof google.maps != 'undefined' 12 | ) { 13 | return mapsPromise = $.when(); // already resolved promise 14 | } 15 | 16 | var url = 'https://maps.googleapis.com/maps/api/js'; 17 | if (params.key) { 18 | url += '?key=' + params.key; 19 | 20 | if (params.lang) { 21 | url += '&language=' + params.lang; 22 | } 23 | 24 | return mapsPromise = $.getScript(url); 25 | } else { 26 | throw new Error('Simplemap: No Google Maps API key found'); 27 | } 28 | } 29 | 30 | function SimpleMap($field, data) { 31 | this.$field = $field; 32 | this.$inputLat = this.$field.find('.simplemap-lat'); 33 | this.$inputLng = this.$field.find('.simplemap-lng'); 34 | this.$inputZoom = this.$field.find('.simplemap-zoom'); 35 | this.$mapCanvas = this.$field.find('.simplemap-canvas'); 36 | 37 | this.isDisabled = this.$mapCanvas.hasClass('is-disabled'); 38 | this.data = data; 39 | 40 | if (this.data.style) { 41 | var self = this; 42 | 43 | $.get(this.data.style).always(function (data, status) { 44 | if (status == 'success') { 45 | self.data.map.styles = data; 46 | } else { 47 | console.warn('Simplemap: Failed to load map styles from:', self.data.style); 48 | } 49 | 50 | self.init(); 51 | }); 52 | } else { 53 | this.init(); 54 | } 55 | } SimpleMap.prototype = { 56 | init: function () { 57 | this.map = new google.maps.Map(this.$mapCanvas[0], this.data.map); 58 | this.pin = new google.maps.Marker( 59 | $.extend(this.data.marker || {}, { 60 | map: this.map, 61 | position: this.data.map.center, 62 | draggable: !this.isDisabled 63 | }) 64 | ); 65 | 66 | if (!this.isDisabled) { 67 | this.listen(); 68 | } 69 | }, 70 | 71 | listen: function () { 72 | var self = this; 73 | 74 | this.pin.addListener('dragend', function (event) { 75 | self.updatePosition(event.latLng); 76 | }); 77 | 78 | this.map.addListener('click', function (event) { 79 | self.updatePosition(event.latLng); 80 | }); 81 | 82 | this.map.addListener('zoom_changed', function () { 83 | self.$inputZoom.val(self.map.getZoom()); 84 | }); 85 | }, 86 | 87 | updatePosition: function (position) { 88 | this.$inputLat.val(position.lat()); 89 | this.$inputLng.val(position.lng()); 90 | this.pin.setPosition(position); 91 | } 92 | }; 93 | 94 | $.fn.kirbySimplemap = function () { 95 | return this.each(function (i, element) { 96 | var $field = $(element); 97 | 98 | try { 99 | var fieldData = JSON.parse($field.attr('data-simplemap')); 100 | } catch (e) { 101 | throw new Error('Simplemap: Failed to parse data'); 102 | } 103 | 104 | whenGoogleMapsLoaded({ 105 | key: fieldData.key, 106 | lang: fieldData.lang 107 | }).then(function () { 108 | new SimpleMap($field, fieldData); 109 | }); 110 | }); 111 | }; 112 | })(jQuery); 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simplemap Field 2 | Very quick, simple, easy, flexible and 100% working integration for Google Maps in the Kirby CMS Panel. 3 | 4 | ## ⚠ Deprecated! 5 | 6 | This plugin only works in Kirby 2 and is discontinued. For Kirby 3, you can use [sylvainjule/kirby-locator](https://github.com/sylvainjule/kirby-locator). 7 | 8 | ## Features 9 | - Localization: The map language is set to the language of the panel, providing a consistent experience. 10 | - Readonly/disabled support: The map will be disabled when the field has `translate: false` and a non-primary language is selected, for example. 11 | - UI/UX settings: You can change the map UI (`zoomControl`, `fullscreenControl`...) and UX (`gestureHandling`) directly from your blueprint. 12 | - Marker settings: Like the map settings, you can also change the various marker functionality via blueprints. 13 | - Map styling: You can style the map however you wish with styles from [Snazzy Maps](https://snazzymaps.com/). 14 | - Variable width: Works perfectly with the default Kirby `width` blueprint property, as seen on the screenshots. 15 | - Height setting: You can set the height of the map as a ratio relative to its width, like `16:9`, `4:3`, `1:1`, any pair of numbers will do. 16 | - Smart script loading: In case some other plugin loaded Google Maps in the panel already, nothing will be loaded. 17 | 18 | ## Screenshots 19 | 20 | On the left, there’s a map with 2/3 width, 16:9 size ratio and the default Google Maps settings. On the right, there’s a map with 1/3 width, 1:1 ratio, customized map settings (no UI), custom marker (pin) styles and the field has `translate: false`. Both maps are in English because the panel is in that language. 21 | 22 | ![Simplemap field in primary language](https://i.imgur.com/UQrPrKi.jpg) 23 | 24 | On this screenshot, the panel language was switched to Bulgarian and the site (translation) language was also set to Bulgarian. As you can see, both maps are displayed in that language and the map on the right is disabled because it has `translate: false`. Disabled maps can be interacted with, but the user won’t be able to change marker position. 25 | 26 | ![Simplemap field in secondary language](https://i.imgur.com/thNwWoj.jpg) 27 | 28 | ## Installation 29 | 30 | Open a terminal and navigate to your root Kirby folder: 31 | 32 | ```sh 33 | cd /var/www/html/my-kirby-project 34 | ``` 35 | 36 | Then use git submodules: 37 | 38 | ```sh 39 | git submodule add https://github.com/hdodov/kirby-simplemap-field.git site/fields/simplemap 40 | ``` 41 | 42 | or simply clone the repo: 43 | 44 | ```sh 45 | git clone https://github.com/hdodov/kirby-simplemap-field.git site/fields/simplemap 46 | ``` 47 | 48 | Make sure that the repository is under `site/fields/simplemap` and that `simplemap.php` is inside that folder. 49 | 50 | ## Usage 51 | 52 | After you’ve installed the field, open your `config/config.php` and add your Google Maps API key: 53 | 54 | ```php 55 | c::set('google.maps.key', '__________GOOGLE_MAPS_API_KEY__________'); 56 | ``` 57 | 58 | Then open a blueprint and use the field: 59 | 60 | ```yaml 61 | fields: 62 | location: 63 | label: Location 64 | type: simplemap 65 | ``` 66 | 67 | Now you should have a working location picker field! 68 | 69 | ## Options 70 | 71 | ### Default Kirby stuff 72 | 73 | You’ve got no problem using `width`, `disabled`, `help`... 74 | 75 | ```yaml 76 | location: 77 | label: Location 78 | type: simplemap 79 | width: 2/3 80 | disabled: true 81 | help: Drag the marker or click on the map to change the location. 82 | ``` 83 | 84 | ### Map height 85 | 86 | You can set the map height relative to its width as a ratio. Any pair of numbers will work: 87 | 88 | ```yaml 89 | location: 90 | label: Location 91 | type: simplemap 92 | ratio: 16:9 # or 4:3, 1:1, 4:20, 999:666 93 | ``` 94 | 95 | ### Map settings 96 | 97 | The map settings you specify in your blueprint are directly passed as JSON to the JavaScript that initializes the map. Technically you can change everything specified in the [Google Maps documentation](https://developers.google.com/maps/documentation/javascript/controls): 98 | 99 | ```yaml 100 | location: 101 | label: Location 102 | type: simplemap 103 | map: 104 | zoom: 7 # default zoom 105 | center: 106 | lat: 42.693752936972366 # default latitude 107 | lng: 23.328649668569142 # default longitude 108 | disableDefaultUI: true 109 | zoomControl: true 110 | fullscreenControl: true 111 | gestureHandling: cooperative # zoom the map with ctrl + mousewheel 112 | ``` 113 | 114 | ### Marker settings 115 | 116 | Marker settings work the same way map settings work, meaning you can change everything specified in the [Google Maps Marker documentation](https://developers.google.com/maps/documentation/javascript/markers): 117 | 118 | ```yaml 119 | location: 120 | label: Location 121 | type: simplemap 122 | marker: 123 | icon: https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png 124 | title: Drag me! 125 | ``` 126 | 127 | --- 128 | 129 | **Warning:** Map and marker settings are passed to the Google Maps API as primitive values! Settings that expect a non-primitve value **will not work**. For example, if you try to specify a marker animation: 130 | 131 | ```yaml 132 | location: 133 | label: Location 134 | type: simplemap 135 | marker: 136 | animation: google.maps.Animation.DROP 137 | ``` 138 | 139 | This will not work! The API will receive `"google.maps.Animation.DROP"` as a string. It won’t access the `google` object. 140 | 141 | **Conclusion:** Use the map and marker settings to change values that expect a string, number or boolean, like the ones up in the examples. 142 | 143 | ### Map styles 144 | 145 | To add a custom style to your map, you need to have a JSON file with the style data inside. Let’s say we want to add [this style](https://snazzymaps.com/style/72543/assassins-creed-iv) to a map: 146 | 147 | 1. Copy the JavaScript style array from Snazzy Maps 148 | 149 | 2. Save the array in `assets/maps/style.json` 150 | 151 | 3. Open `config/config.php` and register a new style by pointing to the file: 152 | 153 | ```php 154 | c::set('google.maps.styles.assassins', kirby()->urls()->assets() . '/maps/style.json'); 155 | ``` 156 | 157 | 4. In your blueprint, add the `style` property: 158 | 159 | ```yaml 160 | location: 161 | label: Location 162 | type: simplemap 163 | style: assassins 164 | ``` 165 | 166 | 5. The map is styled! 167 | 168 | ![Simplemap with Snazzy Maps styles](https://i.imgur.com/4WnO3j3.png) 169 | 170 | --- 171 | 172 | You can register any style by setting a configuration variable like that: 173 | 174 | ``` 175 | c::set('google.maps.styles.STYLE_NAME', 'STYLE_JSON_FILE_URL'); 176 | ``` 177 | 178 | And then use it by specifying the name in the blueprint: 179 | 180 | ```yaml 181 | style: STYLE_NAME 182 | ``` 183 | 184 | ## Comparing with other plugins 185 | 186 | ### [kirby-map-field](https://github.com/AugustMiller/kirby-map-field) by [August Miller](https://github.com/AugustMiller) 187 | 188 | Simplemap is actually an overhaul of kirby-map-field. Despite it being a great plugin, it had some functionalities that didn’t align with my needs: 189 | 190 | - It uses the route `example.com/maps/key` to serve the Google Maps API key. This is a bit tricky to handle if you use URL rewriting, which I do. For that reason, Simplemap passes the API key as an attribute to the actual field in the panel. From there, JavaScript knows where to look and gets the key. No routes involved. 191 | 192 | - Has poor user experience (in my opinion). Whenever you zoom in/out, it pans to the current marker position. If I want to move the pin from New York to Moscow, I need to drag it all the way from one place to the other while the map is constantly being panned to the marker. Simplemap doesn’t pan the map at all. You can simply zoom out, navigate to the place you want and just click to place the marker. You can drag it, but you’re not _forced_ to do so. 193 | 194 | - Has locked map settings. For example, zooming in/out with your mousewheel is disabled. With Simplemap, you have all the control _and_ you can control the marker settings. 195 | 196 | - Doesn’t use the panel’s language to display the map. 197 | 198 | - Doesn’t work with readonly/disabled fields. On a multi-language site, you can change the location in any translation, which is unneeded. 199 | 200 | - It uses the deprecated Google JSAPI loader https://www.google.com/jsapi to load Google Maps. 201 | 202 | - Its address field can’t be hidden and if you don’t enable the Geolocation API, that field is obsolete. 203 | 204 | - Its latitude and longitude fields are clutter. If you’re the content manager, you probably don’t care about coordinates. 205 | --------------------------------------------------------------------------------