├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── ci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── _config ├── config.yml └── legacy.yml ├── client ├── infowindow-description.html └── location-list-description.html ├── code-of-conduct.md ├── composer.json ├── css └── map.css ├── docs └── en │ ├── developerguide │ ├── customizing.md │ └── setup.md │ ├── index.md │ └── userguide │ ├── import.md │ └── index.md ├── images ├── Locator.png ├── LocatorCMS.png ├── LocatorPage.png ├── ajax-loader.gif ├── close-icon.png ├── overlay-bg.png └── submit.jpg ├── phpcs.xml.dist ├── phpunit.xml.dist ├── src ├── Model │ ├── Location.php │ └── LocationCategory.php ├── Page │ ├── LocationPage.php │ ├── LocationPageController.php │ ├── Locator.php │ └── LocatorController.php ├── Tasks │ ├── EmailAddressTask.php │ ├── LocationCategoriesTask.php │ ├── LocationCategoryExtension.php │ ├── LocationPublishTask.php │ └── LocationToPageTask.php ├── admin │ └── LocationsAdmin.php ├── bulkloader │ ├── LocationCsvBulkLoader.php │ └── LocationPageCsvBulkLoader.php └── form │ └── LocatorForm.php ├── templates └── Dynamic │ └── Locator │ ├── Data │ ├── JSON.ss │ └── XML.ss │ └── Page │ └── Layout │ └── Locator.ss └── thirdparty └── jquery-store-locator-plugin ├── assets ├── css │ ├── bootstrap-example.css │ ├── bootstrap-example.min.css │ ├── storelocator.css │ ├── storelocator.css.map │ └── storelocator.min.css ├── img │ ├── ajax-loader.gif │ ├── blue-marker.png │ ├── blue-marker.svg │ ├── close-icon-dark.png │ ├── close-icon.png │ ├── m1.png │ ├── m2.png │ ├── m3.png │ ├── m4.png │ ├── m5.png │ ├── overlay-bg.png │ ├── red-marker.png │ └── red-marker.svg └── js │ ├── geocode.min.js │ ├── libs │ ├── handlebars.min.js │ ├── infobubble.min.js │ └── markerclusterer.min.js │ └── plugins │ └── storeLocator │ ├── jquery.storelocator.js │ ├── jquery.storelocator.min.js │ └── templates │ ├── infowindow-description.html │ ├── kml-infowindow-description.html │ ├── kml-location-list-description.html │ └── location-list-description.html ├── autocomplete-example.html ├── autogeocode-example.html ├── bootstrap-example.html ├── categories-example.html ├── category-markers-example.html ├── cluster-example.html ├── data ├── locations.json ├── locations.kml └── locations.xml ├── default-location-example.html ├── fullmapstartblank-example.html ├── geocode.html ├── index.html ├── infobubble-example.html ├── inline-directions.html ├── json-example.html ├── kml-example.html ├── maxdistance-example.html ├── modal-example.html ├── namesearch-example.html ├── noform-example.html ├── pagination-example.html ├── query-string-example ├── index.html └── submit.html └── rawdata-example.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in 2 | # this file, please see the EditorConfig documentation: 3 | # http://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | # Docs say 80 ideally, 100 ok, no more than 120 16 | # http://doc.silverstripe.org/en/getting_started/coding_conventions/ 17 | max_line_length = 100 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | 22 | [*.yml] 23 | indent_size = 2 24 | indent_style = space 25 | 26 | #PSR 2 27 | [**.php] 28 | indent_style = space 29 | indent_size = 4 30 | 31 | [{.travis.yml,package.json}] 32 | # The indent size used in the `package.json` file cannot be changed 33 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 34 | indent_size = 2 35 | indent_style = space 36 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore 2 | /.gitignore export-ignore 3 | /.travis.yml export-ignore 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'BUG ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'FEATURE ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | ci: 10 | name: CI 11 | uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1 12 | with: 13 | phpcoverage: true 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | - Maintenance on this module is a shared effort of those who use it 3 | - To contribute improvements to the code, ensure you raise a pull request and discuss with the module maintainers 4 | - Please follow the SilverStripe [code contribution guidelines](https://docs.silverstripe.org/en/contributing/code/) and [Module Standard](https://docs.silverstripe.org/en/developer_guides/extending/modules/#module-standard) 5 | - Supply documentation that follows the [GitHub Flavored Markdown](https://help.github.com/articles/markdown-basics/) conventions 6 | - When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct/) 7 | 8 | 9 | ## Contributor license agreement 10 | By supplying code to this module in patches, tickets and pull requests, you agree to assign copyright 11 | of that code to Dynamic, Inc., on the condition that these code changes are released under the 12 | same BSD license as the original module. We ask for this so that the ownership in the license is clear 13 | and unambiguous. By releasing this code under a permissive license such as BSD, this copyright assignment 14 | won't prevent you from using the code in any way you see fit. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Dynamic, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SilverStripe Locator 2 | 3 | [![Build Status](https://travis-ci.org/dynamic/silverstripe-locator.svg?branch=master)](https://travis-ci.org/dynamic/silverstripe-locator) 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/dynamic/silverstripe-locator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dynamic/silverstripe-locator/?branch=master) 5 | [![Build Status](https://scrutinizer-ci.com/g/dynamic/silverstripe-locator/badges/build.png?b=master)](https://scrutinizer-ci.com/g/dynamic/silverstripe-locator/build-status/master) 6 | [![codecov.io](https://codecov.io/github/dynamic/silverstripe-locator/coverage.svg?branch=master)](https://codecov.io/github/dynamic/silverstripe-locator?branch=master) 7 | 8 | [![Latest Stable Version](https://poser.pugx.org/dynamic/silverstripe-locator/v/stable)](https://packagist.org/packages/dynamic/silverstripe-locator) 9 | [![Total Downloads](https://poser.pugx.org/dynamic/silverstripe-locator/downloads)](https://packagist.org/packages/dynamic/silverstripe-locator) 10 | [![Latest Unstable Version](https://poser.pugx.org/dynamic/silverstripe-locator/v/unstable)](https://packagist.org/packages/dynamic/silverstripe-locator) 11 | [![License](https://poser.pugx.org/dynamic/silverstripe-locator/license)](https://packagist.org/packages/dynamic/silverstripe-locator) 12 | 13 | SilverStripe Locator displays a filterable map of locations. You can choose whether to show all locations on load, or enable auto geocoding to filter the initial list based on the visitor's location. 14 | 15 | ## Requirements 16 | 17 | * SilverStripe ^4.0 18 | * muskie9/data-to-arraylist ^2.0 19 | * dynamic/silverstripe-geocoder ^1.0 20 | * symbiote/silverstripe-gridfieldextensions ^3.0 21 | 22 | ## License 23 | 24 | See [License](LICENSE.md) 25 | 26 | ## Installation 27 | 28 | `composer require dynamic/silverstripe-locator 3.0.x-dev` 29 | 30 | ## Example usage 31 | 32 | Displays a filterable list of locations on a map. Users can filter by address or category to find the location nearest them. 33 | 34 | With auto geocoding enabled in the CMS, the map will display the nearest 26 locations to the user. 35 | 36 | ![screen shot](images/Locator.png) 37 | 38 | ## Documentation 39 | 40 | See the [docs/en](docs/en/index.md) folder. 41 | 42 | ## Maintainers 43 | * [Dynamic](https://www.dynamicagency.com) () 44 | 45 | ## Bugtracker 46 | Bugs are tracked in the issues section of this repository. Before submitting an issue please read over 47 | existing issues to ensure yours is unique. 48 | 49 | If the issue does look like a new bug: 50 | 51 | - Create a new issue 52 | - Describe the steps required to reproduce your issue, and the expected outcome. Unit tests, screenshots 53 | and screencasts can help here. 54 | - Describe your environment as detailed as possible: SilverStripe version, Browser, PHP version, 55 | Operating System, any installed SilverStripe modules. 56 | 57 | Please report security issues to the module maintainers directly. Please don't file security issues in the bugtracker. 58 | 59 | ## Development and contribution 60 | If you would like to make contributions to the module please ensure you raise a pull request and discuss with the module maintainers. 61 | -------------------------------------------------------------------------------- /_config/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Name: locator 3 | --- 4 | Dynamic\Locator\Model\Location: 5 | extensions: 6 | - Dynamic\SilverStripeGeocoder\DistanceDataExtension 7 | - Dynamic\SilverStripeGeocoder\AddressDataExtension 8 | - SilverStripe\Versioned\Versioned('Stage','Live') 9 | 10 | Dynamic\Locator\Page\LocationPage: 11 | extensions: 12 | - LittleGiant\CatalogManager\Extensions\CatalogPageExtension 13 | - Dynamic\SilverStripeGeocoder\DistanceDataExtension 14 | - Dynamic\SilverStripeGeocoder\AddressDataExtension 15 | parent_classes: 16 | - Dynamic\Locator\Page\Locator 17 | sort_column: false 18 | automatic_live_sort: false 19 | show_in_sitetree: false 20 | 21 | Dynamic\Locator\Page\Locator: 22 | infoWindowTemplate: 'dynamic/silverstripe-locator: client/infowindow-description.html' 23 | listTemplate: 'dynamic/silverstripe-locator: client/location-list-description.html' 24 | limit: 50 25 | show_radius: true 26 | radius_var: 'Radius' 27 | category_var: 'CategoryID' 28 | extensions: 29 | - SilverStripe\Lumberjack\Model\Lumberjack 30 | 31 | --- 32 | Name: modules-locator 33 | After: modules-other 34 | Before: modules-framework 35 | --- 36 | SilverStripe\Core\Manifest\ModuleManifest: 37 | module_priority: 38 | - dynamic/silverstripe-locator 39 | -------------------------------------------------------------------------------- /_config/legacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: locatorlegacy 3 | --- 4 | SilverStripe\ORM\DatabaseAdmin: 5 | classname_value_remapping: 6 | Dynamic\Locator\Location: Dynamic\Locator\Model\Location 7 | Dynamic\Locator\Locator: Dynamic\Locator\Page\Locator 8 | Dynamic\Locator\LocationCategory: Dynamic\Locator\Model\LocationCategory 9 | -------------------------------------------------------------------------------- /client/infowindow-description.html: -------------------------------------------------------------------------------- 1 | {{#location}} 2 |
{{name}}
3 |
{{address}}
4 |
{{address2}}
5 |
{{city}}, {{state}} {{postal}}
6 | {{#if phone}}
{{phone}}
{{/if}} 7 | {{#if fax}}
{{fax}}
{{/if}} 8 | {{#if web}}
Website
{{/if}} 9 | {{/location}} 10 | -------------------------------------------------------------------------------- /client/location-list-description.html: -------------------------------------------------------------------------------- 1 | {{#location}} 2 |
  • 3 |
    {{marker}}
    4 |
    5 |
    6 |
    {{name}}
    7 |
    {{address}}
    8 | {{#if address2}}
    {{address2}}
    {{/if}} 9 |
    {{city}}, {{state}} {{postal}}
    10 | {{#if phone}}
    {{phone}}
    {{/if}} 11 | {{#if fax}}
    {{fax}}
    {{/if}} 12 | {{#if web}}{{/if}} 13 | {{#if email}}{{/if}} 14 |
    15 | {{#if distance}}{{distance}} {{length}} | {{/if}} Directions 16 |
    17 |
    18 |
    19 |
  • 20 | {{/location}} 21 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct). 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dynamic/silverstripe-locator", 3 | "description": "SilverStripe Locator Module. Show locations on a map. Search by geoposition.", 4 | "license": "BSD-3-Clause", 5 | "type": "silverstripe-vendormodule", 6 | "keywords": [ 7 | "silverstripe", 8 | "map", 9 | "locator", 10 | "dynamic", 11 | "geocode", 12 | "google maps", 13 | "location" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Dynamic", 18 | "email": "dev@dynamicagency.com", 19 | "homepage": "https://www.dynamicagency.com" 20 | } 21 | ], 22 | "require": { 23 | "colymba/gridfield-bulk-editing-tools": "^3.0", 24 | "dynamic/silverstripe-geocoder": "^1.3 || ^2.0", 25 | "littlegiant/silverstripe-catalogmanager": "^5.2", 26 | "muskie9/data-to-arraylist": "^2.0", 27 | "silverstripe/lumberjack": "^2.0", 28 | "silverstripe/recipe-cms": "^4.0", 29 | "symbiote/silverstripe-gridfieldextensions": "^3.0" 30 | }, 31 | "require-dev": { 32 | "silverstripe/recipe-testing": "^2" 33 | }, 34 | "suggest": { 35 | "axllent/silverstripe-bootstrap-forms": "Add CSS classes to Silverstripe forms to play nicer with Twitter Bootstrap" 36 | }, 37 | "minimum-stability": "dev", 38 | "prefer-stable": true, 39 | "autoload": { 40 | "psr-4": { 41 | "Dynamic\\Locator\\": "src/", 42 | "Dynamic\\Locator\\Tests\\": "tests/" 43 | } 44 | }, 45 | "config": { 46 | "allow-plugins": { 47 | "composer/installers": true, 48 | "silverstripe/vendor-plugin": true, 49 | "silverstripe/recipe-plugin": true 50 | }, 51 | "process-timeout": 600 52 | }, 53 | "extra": { 54 | "expose": [ 55 | "thirdparty", 56 | "css", 57 | "client" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /css/map.css: -------------------------------------------------------------------------------- 1 | /* === 2 | Search Form 3 | === */ 4 | #form-container form { 5 | width: 100%; 6 | max-width: 100%; 7 | margin-bottom: 20px; 8 | float: left; 9 | } 10 | 11 | #form-container fieldset, 12 | #form-container div.Actions { 13 | float: left; 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #form-container fieldset { width: 80%;} 19 | #form-container div.Actions { width: 20%;} 20 | 21 | #form-container div.field { 22 | width: 45%; 23 | margin-right: 5%; 24 | float: left; 25 | vertical-align: middle; 26 | } 27 | #form-container input, 28 | #form-container select { 29 | width: 100%; 30 | } 31 | 32 | 33 | /* === 34 | Map 35 | === */ 36 | #map-container { 37 | clear: left; 38 | margin-top: 27px; 39 | height: 530px; 40 | } 41 | 42 | #map-container a { 43 | color: #e76737; 44 | text-decoration: none; 45 | } 46 | 47 | #map-container a:hover, #map-container a:active { 48 | text-decoration: underline; 49 | } 50 | 51 | #map-container .custom-marker { 52 | width: 32px; 53 | height: 37px; 54 | color: #fff; 55 | background: url(../images/custom-marker.png) no-repeat; 56 | padding: 3px; 57 | cursor: pointer; 58 | } 59 | 60 | #map { 61 | float: right; 62 | width: 74%; 63 | height: 530px; 64 | } 65 | 66 | 67 | /* === 68 | Results List 69 | === */ 70 | .loc-list 71 | { 72 | float: left; 73 | height: 530px; 74 | width: 25%; 75 | overflow: auto; 76 | } 77 | 78 | .loc-list ul 79 | { 80 | display: block; 81 | clear: left; 82 | float: left; 83 | list-style: none; 84 | margin: 0; 85 | padding: 0; 86 | width: 100%; 87 | } 88 | 89 | .loc-list .list-label 90 | { 91 | float: left; 92 | margin: 10px 0 0 6px; 93 | padding: 2px 3px; 94 | width: 27px; 95 | text-align: center; 96 | background: #451400; 97 | color: #fff; 98 | font-weight: bold; 99 | } 100 | 101 | .loc-list .list-details 102 | { 103 | float: left; 104 | margin-left: 6px; 105 | width: 165px; 106 | } 107 | 108 | .loc-list .list-content 109 | { 110 | padding: 10px; 111 | } 112 | 113 | .loc-list .loc-dist 114 | { 115 | font-weight: bold; 116 | font-style: italic; 117 | color: #8e8e8e; 118 | } 119 | 120 | .loc-list li 121 | { 122 | display: block; 123 | clear: left; 124 | float: left; 125 | margin: 6px 0; 126 | cursor: pointer; 127 | width: 99%; 128 | border: 1px solid #fff; /* Adding this to prevent moving li elements when adding the list-focus class*/ 129 | } 130 | 131 | .loc-list li:first-child {margin-top: 0px;} 132 | 133 | 134 | .loc-list .list-focus 135 | { 136 | border: 1px solid rgba(82,168,236,0.9); 137 | -moz-box-shadow: 0 0 8px rgba(82,168,236,0.7); 138 | -webkit-box-shadow: 0 0 8px rgba(82,168,236,0.7); 139 | box-shadow: 0 0 8px rgba(82,168,236,0.7); 140 | transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s; 141 | } 142 | 143 | #map-container .loc-name 144 | { 145 | color: #AD2118; 146 | font-weight: bold; 147 | } 148 | 149 | 150 | /* === 151 | Loading 152 | === */ 153 | .typography img.loading { 154 | border: none; 155 | } 156 | 157 | 158 | /* === 159 | Modal window 160 | === */ 161 | 162 | #overlay 163 | { 164 | position: fixed; 165 | left: 0px; 166 | top: 0px; 167 | width:100%; 168 | height:100%; 169 | z-index: 10000; 170 | background: url(../images/overlay-bg.png) repeat; 171 | } 172 | 173 | #modal-window 174 | { 175 | position: absolute; 176 | left: 50%; 177 | margin-left: -460px; /* width divided by 2 */ 178 | margin-top: 60px; 179 | width: 920px; 180 | height: 590px; 181 | z-index: 10010; 182 | background: #fff; 183 | border-radius: 10px; 184 | box-shadow: 0 0 10px #656565; 185 | } 186 | 187 | #modal-content 188 | { 189 | padding: 0 22px; /* there's already a margin on the top of the map-container div */ 190 | } 191 | 192 | #close-icon 193 | { 194 | position: absolute; 195 | top: -6px; 196 | right: -6px; 197 | width: 18px; 198 | height: 18px; 199 | cursor: pointer; 200 | background: #2c2c2c url(../images/close-icon.png) 3px 3px no-repeat; 201 | border: 1px solid #000; 202 | border-radius: 3px; 203 | box-shadow: 0 0 3px #656565; 204 | } 205 | 206 | 207 | 208 | 209 | 210 | /* === 211 | responsive for Simple theme 212 | === */ 213 | 214 | /* BREAKPOINT 960px */ 215 | 216 | @media only screen and (max-width: 960px) { 217 | 218 | .loc-list { 219 | 220 | } 221 | 222 | #map { 223 | 224 | } 225 | 226 | } 227 | /* #Tablet (Portrait) 228 | ================================================== */ 229 | 230 | /* Note: Design for a width of 768px */ 231 | 232 | @media only screen and (min-width: 641px) and (max-width: 959px) { 233 | 234 | .loc-list .list-label{ 235 | margin-left: 0px; 236 | } 237 | 238 | .loc-list .list-content { 239 | padding: 10px 0px; 240 | } 241 | 242 | .loc-list .list-details { 243 | width: 140px; 244 | } 245 | 246 | } 247 | /* BREAKPOINT 640px */ 248 | 249 | @media only screen and (max-width: 640px) { 250 | 251 | .loc-list { 252 | width: 100%; 253 | height: auto; 254 | } 255 | 256 | #map { 257 | width: 100%; 258 | float: left; 259 | } 260 | 261 | #form-container fieldset { width: 100%;} 262 | #form-container div.field { 263 | width: 100%; 264 | margin-right: 0; 265 | float: none; 266 | } 267 | #form-container div.Actions { 268 | width: 100%; 269 | max-width: 400px; 270 | float: none; 271 | } 272 | 273 | } -------------------------------------------------------------------------------- /docs/en/developerguide/customizing.md: -------------------------------------------------------------------------------- 1 | ## Customizing 2 | 3 | ### YAML Configuration 4 | Some customization is done through a yml config. 5 | 6 | #### Limit 7 | Will limit the amount of locations that can be shown at one time. Set to `-1` to have no limit. Having no limit can result in slow load times, or even timeouts when loading the page or new data. 8 | ```yaml 9 | Dynamic\Locator\Locator: 10 | limit: 50 11 | ``` 12 | 13 | #### Radius 14 | - `show_radius` will determine if a radius dropdown should be shown. 15 | - `radii` is a list of radii to use in the radius dropdown. 16 | ```yaml 17 | Dynamic\Locator\Locator: 18 | show_radius: true 19 | radii: [30, 50, 100] 20 | ``` 21 | 22 | #### Templates 23 | Overriding the templates for the info window and list can be overridden by using `infoWindowTemplate` and `listTemplate`. 24 | - `infoWindowTemplate` is the popup when a location is clicked. 25 | - `listTemplate` is a single location in the list 26 | ```yaml 27 | Dynamic\Locator\Locator: 28 | infoWindowTemplate: 'dynamic/silverstripe-locator: client/infowindow-description.html' 29 | listTemplate: 'dynamic/silverstripe-locator: client/location-list-description.html' 30 | ``` 31 | The `vendor/module: file` pattern can be used to locate files. 32 | 33 | #### Custom URL Variables 34 | Sometimes it is useful to override the defaults for url variables. 35 | An example of this is when the module uses title case but you wrote everything for lowercase. It becomes seamless to switch out the variable names. 36 | - `radius_var` is the variable used to send and receive the currently selected radius. 37 | - `category_var` is the variable used to send and receive the currently selected category. 38 | - `address_var` is the variable used to send and receive the searched location. 39 | - `unit_var` is the variable used to send and receive the current unit of measure used for distance. 40 | ```yaml 41 | Dynamic\Locator\Locator: 42 | radius_var: 'Radius' 43 | category_var: 'CategoryID' 44 | 45 | Dynamic\SilverStripeGeocoder\DistanceDataExtension: 46 | address_var: 'Address' 47 | unit_var: 'Unit' 48 | ``` 49 | 50 | ### Extenison Points 51 | 52 | #### Locator Form 53 | ##### overrideRadiusArray 54 | Allows radius array to be changed outside of the yml config. 55 | `overrideRadiusArray` gets passed the radius array. 56 | 57 | ##### updateRequiredFields 58 | Alolows the required fields to be modified. 59 | `updateRequiredFields` gets passed a Validator object. 60 | 61 | ##### updateLocatorFormFields 62 | Allows form fields to be modified. 63 | `updateLocatorFormFields` gets passed a field list. 64 | 65 | ##### updateLocatorActions 66 | Allows form actions to be modified. 67 | `updateLocatorActions` gets passed a field list. 68 | 69 | #### Locator Admin 70 | ##### updateGetExportFields 71 | Allows exported fields to be modified. 72 | `updateGetExportFields` gets passed an associative array. 73 | 74 | #### Location 75 | ##### updateLocationFields 76 | Allows cms fields to be modified. 77 | `updateLocationFields` gets passed a field list. 78 | 79 | ##### updateWebsiteURL 80 | Allows modifying the wesite url of the location. 81 | `updateWebsiteURL` gets passed the website url. 82 | 83 | #### Locator Controller 84 | ##### modifyMapSettings 85 | Allows modifying the map settings used for the locator javascript. 86 | `modifyMapSettings` gets passed an associative array. 87 | 88 | ##### modifyMapSettingsEncoded 89 | Allows modifying the map settings after it is turned into a string. 90 | It is prefered to use the `modifyMapSettings` extension point. 91 | `modifyMapSettingsEncoded` gets passed a string. 92 | 93 | ##### updateLocatorFilter 94 | Allows updating the location list filter. 95 | `updateLocatorFilter` gets passed the current filter and the current request. 96 | 97 | ##### updateLocatorFilterAny 98 | Allows updating the location list filter any. 99 | `updateLocatorFilterAny` gets passed the current filter any and the current request. 100 | 101 | ##### updateLocatorExclude 102 | Allows updating the location list exlude. 103 | `updateLocatorFilterAny` gets passed the current exlude and the current request. 104 | 105 | ##### updateLocationList 106 | Allows updating the list of locations after distance calculations have been done. 107 | `updateLocationList` is passed an array list. 108 | 109 | ##### updateListType 110 | Allows updating the location list type. 111 | `updateListType` is passed an array list. 112 | -------------------------------------------------------------------------------- /docs/en/developerguide/setup.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | To use google maps two api keys are required. 3 | One for the client side map, and one for the back end geocoding. 4 | The back end key only needs access to 5 | In mysite/_config/settings.yml 6 | ```yaml 7 | Dynamic\SilverStripeGeocoder\GoogleGeocoder: 8 | geocoder_api_key: BACK_END_API_KEY 9 | map_api_key: FRONT_END_API_KEY 10 | ``` 11 | Replace `BACK_END_API_KEY` with your google maps api key that is restricted by ip. 12 | Replace `FRONT_END_API_KEY` with your google maps api key that is restricted by referrer. 13 | 14 | If you are unsure where to get an api key, [look here](https://developers.google.com/maps/documentation/javascript/get-api-key). 15 | -------------------------------------------------------------------------------- /docs/en/index.md: -------------------------------------------------------------------------------- 1 | # SilverStripe Locator 2 | 3 | ## Using Locator 4 | 5 | See [User Guide](userguide/index.md) for information on using the Locator module in the CMS. 6 | 7 | See [Importing Locations](userguide/import.md) for information on importing Location and Category records. 8 | 9 | ## Developer guides 10 | 11 | - [Setup](developerguide/setup.md) 12 | - [Customizing](developerguide/customizing.md) 13 | -------------------------------------------------------------------------------- /docs/en/userguide/import.md: -------------------------------------------------------------------------------- 1 | # Importing Locations 2 | 3 | For large installations, you can import Location and Category data via the Locator Admin. Simply export as CSV to get the spreadsheet template, fill out the sheet, and import via the Locator admin. 4 | 5 | For duplication checks, to update exsiting records rather than always replace the data, create a `Import_ID` column in your spreadsheet with an auto-increment integer. For large uploads, this will allow you to skip already existing records in those cases where your initial import times/errors out. 6 | -------------------------------------------------------------------------------- /docs/en/userguide/index.md: -------------------------------------------------------------------------------- 1 | # User Guide 2 | 3 | Create a Locator page in the CMS. Enabling Auto Geocode will return the 26 nearest locations to that user's current location. If not enabled, all locations will be shown on the map. 4 | 5 | You can also filter locations by one or more categories. This is useful if you'd like to have multiple Locators in a site with different locations on each map. 6 | 7 | ![screen shot](../../../images/LocatorPage.png) 8 | 9 | Locations are managed under the Locations tab in the CMS via Model Admin. Simply enter the name and address of each location, and the coordinates will be generated on save. 10 | 11 | ![screen shot](../../../images/LocatorCMS.png) 12 | 13 | Those locations marked as `ShowInLocator` will display on the map, per your Locator page settings. 14 | 15 | ![screen shot](../../../images/Locator.png) 16 | 17 | -------------------------------------------------------------------------------- /images/Locator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/Locator.png -------------------------------------------------------------------------------- /images/LocatorCMS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/LocatorCMS.png -------------------------------------------------------------------------------- /images/LocatorPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/LocatorPage.png -------------------------------------------------------------------------------- /images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/ajax-loader.gif -------------------------------------------------------------------------------- /images/close-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/close-icon.png -------------------------------------------------------------------------------- /images/overlay-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/overlay-bg.png -------------------------------------------------------------------------------- /images/submit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/images/submit.jpg -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeSniffer ruleset for SilverStripe coding conventions. 4 | 5 | src 6 | tests 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests/ 6 | 7 | 8 | 9 | 10 | src/ 11 | 12 | tests/ 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Model/Location.php: -------------------------------------------------------------------------------- 1 | 'Varchar(255)', 46 | 'Featured' => 'Boolean', 47 | 'Website' => 'Varchar(255)', 48 | 'Phone' => 'Varchar(40)', 49 | 'Email' => 'Varchar(255)', 50 | 'Fax' => 'Varchar(45)', 51 | 'Import_ID' => 'Int', 52 | ]; 53 | 54 | private static $many_many = [ 55 | 'Categories' => LocationCategory::class, 56 | ]; 57 | 58 | /** 59 | * @var string 60 | */ 61 | private static $table_name = 'Location'; 62 | 63 | /** 64 | * @var array 65 | */ 66 | private static $casting = [ 67 | 'distance' => 'Decimal(9,3)', 68 | ]; 69 | 70 | /** 71 | * @var string 72 | */ 73 | private static $default_sort = 'Title'; 74 | 75 | /** 76 | * api access via Restful Server module 77 | * 78 | * @var bool 79 | */ 80 | private static $api_access = true; 81 | 82 | /** 83 | * search fields for Model Admin 84 | * 85 | * @var array 86 | */ 87 | private static $searchable_fields = [ 88 | 'Title', 89 | 'Address', 90 | 'City', 91 | 'State', 92 | 'PostalCode', 93 | 'Country', 94 | 'Website', 95 | 'Phone', 96 | 'Email', 97 | 'Featured', 98 | ]; 99 | 100 | /** 101 | * columns for grid field 102 | * 103 | * @var array 104 | */ 105 | private static $summary_fields = [ 106 | 'Title', 107 | 'Address', 108 | 'Address2', 109 | 'City', 110 | 'State', 111 | 'PostalCode', 112 | 'CountryCode', 113 | 'Phone' => 'Phone', 114 | 'Fax' => 'Fax', 115 | 'Email' => 'Email', 116 | 'Website' => 'Website', 117 | 'Featured', 118 | 'CategoryList', 119 | 'Lat', 120 | 'Lng', 121 | 'Import_ID', 122 | ]; 123 | 124 | /** 125 | * Coords status for $summary_fields 126 | * 127 | * @return string 128 | */ 129 | public function getCoords() 130 | { 131 | return ($this->Lat != 0 && $this->Lng != 0) ? 'true' : 'false'; 132 | } 133 | 134 | /** 135 | * @return string 136 | */ 137 | public function getCategoryList() 138 | { 139 | if ($this->Categories()->count()) { 140 | return implode(', ', $this->Categories()->column('Name')); 141 | } 142 | 143 | return ''; 144 | } 145 | 146 | /** 147 | * @return bool|string 148 | */ 149 | public function getCountryCode() 150 | { 151 | if ($this->Country) { 152 | return strtoupper($this->Country); 153 | } 154 | 155 | return false; 156 | } 157 | 158 | /** 159 | * custom labels for fields 160 | * 161 | * @param bool $includerelations 162 | * @return array|string 163 | */ 164 | public function fieldLabels($includerelations = true) 165 | { 166 | $labels = parent::fieldLabels($includerelations); 167 | $labels['Title'] = 'Name'; 168 | $labels['Address2'] = 'Address 2'; 169 | $labels['PostalCode'] = 'Postal Code'; 170 | $labels['Categories.Name'] = 'Categories'; 171 | $labels['Featured.NiceAsBoolean'] = 'Featured'; 172 | 173 | return $labels; 174 | } 175 | 176 | /** 177 | * @return FieldList 178 | */ 179 | public function getCMSFields() 180 | { 181 | $this->beforeUpdateCMSFields(function ($fields) { 182 | $fields->removeByName([ 183 | 'Import_ID', 184 | 'LinkTracking', 185 | 'FileTracking', 186 | ]); 187 | 188 | $fields->dataFieldByName('Website') 189 | ->setAttribute('placeholder', 'http://'); 190 | 191 | $fields->replaceField('Email', EmailField::create('Email')); 192 | 193 | $featured = $fields->dataFieldByName('Featured') 194 | ->setDescription('Location will display near the top of the results list'); 195 | $fields->insertAfter( 196 | 'Fax', 197 | $featured 198 | ); 199 | 200 | if ($this->ID) { 201 | $categories = $fields->dataFieldByName('Categories'); 202 | $config = $categories->getConfig(); 203 | $config->removeComponentsByType([ 204 | GridFieldAddExistingAutocompleter::class, 205 | ]) 206 | ->addComponents([ 207 | new GridFieldAddExistingSearchButton(), 208 | ]); 209 | } 210 | }); 211 | 212 | $fields = parent::getCMSFields(); 213 | 214 | // allow to be extended via DataExtension 215 | $this->extend('updateLocationFields', $fields); 216 | 217 | return $fields; 218 | } 219 | 220 | /** 221 | * @param null $member 222 | * @param array $context 223 | * @return bool 224 | */ 225 | public function canView($member = null, $context = []) 226 | { 227 | return true; 228 | } 229 | 230 | /** 231 | * @param null $member 232 | * @param array $context 233 | * @return bool|int 234 | */ 235 | public function canEdit($member = null, $context = []) 236 | { 237 | return Permission::check('Location_EDIT', 'any', $member); 238 | } 239 | 240 | /** 241 | * @param null $member 242 | * @param array $context 243 | * @return bool|int 244 | */ 245 | public function canDelete($member = null, $context = []) 246 | { 247 | return Permission::check('Location_DELETE', 'any', $member); 248 | } 249 | 250 | /** 251 | * @param null $member 252 | * @param array $context 253 | * @return bool|int 254 | */ 255 | public function canCreate($member = null, $context = []) 256 | { 257 | return Permission::check('Location_CREATE', 'any', $member); 258 | } 259 | 260 | /** 261 | * @return array 262 | */ 263 | public function providePermissions() 264 | { 265 | return [ 266 | 'Location_EDIT' => 'Edit a Location', 267 | 'Location_DELETE' => 'Delete a Location', 268 | 'Location_CREATE' => 'Create a Location', 269 | ]; 270 | } 271 | 272 | /** 273 | * @return string 274 | */ 275 | public function getWebsiteURL() 276 | { 277 | $url = $this->Website; 278 | 279 | $this->extend('updateWebsiteURL', $url); 280 | 281 | return $url; 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/Model/LocationCategory.php: -------------------------------------------------------------------------------- 1 | 'Varchar(100)', 41 | ]; 42 | 43 | /** 44 | * @var array 45 | */ 46 | private static $belongs_many_many = [ 47 | 'Locators' => Locator::class, 48 | 'LocationSet' => Location::class, 49 | ]; 50 | 51 | /** 52 | * @var string 53 | */ 54 | private static $table_name = 'LocationCategory'; 55 | 56 | /** 57 | * @var string 58 | */ 59 | private static $default_sort = 'Name'; 60 | 61 | /** 62 | * @return \SilverStripe\Forms\FieldList 63 | */ 64 | public function getCMSFields() 65 | { 66 | $this->beforeUpdateCMSFields(function (FieldList $fields) { 67 | $fields->removeByName([ 68 | 'Locations', 69 | 'LocationSet', 70 | 'Locators', 71 | 'LinkTracking', 72 | 'FileTracking', 73 | ]); 74 | 75 | if ($this->ID) { 76 | // Locations 77 | $config = GridFieldConfig_RelationEditor::create(); 78 | $config->removeComponentsByType([ 79 | GridFieldAddExistingAutocompleter::class, 80 | GridFieldAddNewButton::class, 81 | ]) 82 | ->addComponents([ 83 | new GridFieldAddExistingSearchButton(), 84 | ]); 85 | $locations = $this->Locations(); 86 | $locationField = GridField::create('Locations', 'Locations', $locations, $config); 87 | 88 | $fields->addFieldsToTab('Root.Main', [ 89 | $locationField, 90 | ]); 91 | } 92 | }); 93 | 94 | $fields = parent::getCMSFields(); 95 | 96 | return $fields; 97 | } 98 | 99 | /** 100 | * For backwards compatability 101 | * @return ManyManyList 102 | */ 103 | public function Locations() 104 | { 105 | return $this->LocationSet(); 106 | } 107 | 108 | /** 109 | * @param null $member 110 | * @param array $context 111 | * @return bool 112 | */ 113 | public function canView($member = null, $context = []) 114 | { 115 | return true; 116 | } 117 | 118 | /** 119 | * @param null $member 120 | * @param array $context 121 | * @return bool|int 122 | */ 123 | public function canEdit($member = null, $context = []) 124 | { 125 | return Permission::check('Location_EDIT', 'any', $member); 126 | } 127 | 128 | /** 129 | * @param null $member 130 | * @param array $context 131 | * @return bool|int 132 | */ 133 | public function canDelete($member = null, $context = []) 134 | { 135 | return Permission::check('Location_DELETE', 'any', $member); 136 | } 137 | 138 | /** 139 | * @param null $member 140 | * @param array $context 141 | * @return bool|int 142 | */ 143 | public function canCreate($member = null, $context = []) 144 | { 145 | return Permission::check('Location_CREATE', 'any', $member); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Page/LocationPage.php: -------------------------------------------------------------------------------- 1 | 'Boolean', 49 | 'Website' => 'Varchar(255)', 50 | 'Phone' => 'Varchar(40)', 51 | 'Email' => 'Varchar(255)', 52 | 'Fax' => 'Varchar(45)', 53 | 'Import_ID' => 'Int', 54 | 'LegacyObjectID' => 'Int', 55 | ]; 56 | 57 | private static $many_many = [ 58 | 'Categories' => LocationCategory::class, 59 | ]; 60 | 61 | /** 62 | * @var string 63 | */ 64 | private static $table_name = 'LocationPage'; 65 | 66 | /** 67 | * @var array 68 | */ 69 | private static $casting = [ 70 | 'distance' => 'Decimal(9,3)', 71 | ]; 72 | 73 | /** 74 | * @var int[] 75 | */ 76 | private static $defaults = [ 77 | 'ShowInMenus' => 0, 78 | ]; 79 | 80 | /** 81 | * @var string 82 | */ 83 | private static $default_sort = 'Title'; 84 | 85 | /** 86 | * api access via Restful Server module 87 | * 88 | * @var bool 89 | */ 90 | private static $api_access = true; 91 | 92 | /** 93 | * search fields for Model Admin 94 | * 95 | * @var array 96 | */ 97 | private static $searchable_fields = [ 98 | 'Title', 99 | 'Address', 100 | 'City', 101 | 'State', 102 | 'PostalCode', 103 | 'Country', 104 | 'Website', 105 | 'Phone', 106 | 'Email', 107 | 'Featured', 108 | ]; 109 | 110 | /** 111 | * columns for grid field 112 | * 113 | * @var array 114 | */ 115 | private static $summary_fields = [ 116 | 'Title', 117 | 'Address', 118 | 'Address2', 119 | 'City', 120 | 'State', 121 | 'PostalCode', 122 | 'CountryCode', 123 | 'Phone' => 'Phone', 124 | 'Fax' => 'Fax', 125 | 'Email' => 'Email', 126 | 'Website' => 'Website', 127 | 'Featured', 128 | 'CategoryList', 129 | 'Lat', 130 | 'Lng', 131 | 'Import_ID', 132 | ]; 133 | 134 | /** 135 | * @var bool 136 | */ 137 | private static $show_in_sitetree = false; 138 | 139 | /** 140 | * @var array 141 | */ 142 | private static $allowed_children = []; 143 | 144 | /** 145 | * Coords status for $summary_fields 146 | * 147 | * @return string 148 | */ 149 | public function getCoords() 150 | { 151 | return ($this->Lat != 0 && $this->Lng != 0) ? 'true' : 'false'; 152 | } 153 | 154 | /** 155 | * @return string 156 | */ 157 | public function getCategoryList() 158 | { 159 | if ($this->Categories()->count()) { 160 | return implode(', ', $this->Categories()->column('Name')); 161 | } 162 | 163 | return ''; 164 | } 165 | 166 | /** 167 | * @return bool|string 168 | */ 169 | public function getCountryCode() 170 | { 171 | if ($this->Country) { 172 | return strtoupper($this->Country); 173 | } 174 | 175 | return false; 176 | } 177 | 178 | /** 179 | * custom labels for fields 180 | * 181 | * @param bool $includerelations 182 | * @return array|string 183 | */ 184 | public function fieldLabels($includerelations = true) 185 | { 186 | $labels = parent::fieldLabels($includerelations); 187 | 188 | $labels['Title'] = 'Name'; 189 | $labels['Address2'] = 'Address 2'; 190 | $labels['PostalCode'] = 'Postal Code'; 191 | $labels['Categories.Name'] = 'Categories'; 192 | $labels['Featured.NiceAsBoolean'] = 'Featured'; 193 | 194 | return $labels; 195 | } 196 | 197 | /** 198 | * @return FieldList 199 | */ 200 | public function getCMSFields() 201 | { 202 | $this->beforeUpdateCMSFields(function (FieldList $fields) { 203 | $fields->addFieldsToTab( 204 | 'Root.Main', 205 | [ 206 | CheckboxField::create('Featured') 207 | ->setTitle('Featured'), 208 | ], 209 | 'Content' 210 | ); 211 | 212 | if ($this->exists()) { 213 | $fields->addFieldToTab( 214 | 'Root.Categories', 215 | GridField::create( 216 | 'Categories', 217 | 'Categories', 218 | $this->Categories(), 219 | $catConfig = GridFieldConfig_RelationEditor::create() 220 | ) 221 | ); 222 | 223 | $catConfig->removeComponentsByType([ 224 | GridFieldAddExistingAutocompleter::class, 225 | ])->addComponents([ 226 | new GridFieldAddExistingSearchButton() 227 | ]); 228 | } 229 | 230 | $fields->addFieldsToTab('Root.Contact', [ 231 | TextField::create('Website') 232 | ->setTitle('Website') 233 | ->setDescription('Include the http/https (example: https://google.com)'), 234 | TextField::create('Phone') 235 | ->setTitle('Phone'), 236 | EmailField::create('Email') 237 | ->setTitle('Email'), 238 | TextField::create('Fax') 239 | ->setTitle('Fax'), 240 | ]); 241 | }); 242 | 243 | return parent::getCMSFields(); 244 | } 245 | 246 | /** 247 | * @param null $member 248 | * @param array $context 249 | * @return bool 250 | */ 251 | public function canView($member = null, $context = []) 252 | { 253 | return true; 254 | } 255 | 256 | /** 257 | * @param null $member 258 | * @param array $context 259 | * @return bool|int 260 | */ 261 | public function canEdit($member = null, $context = []) 262 | { 263 | return Permission::check('Location_EDIT', 'any', $member); 264 | } 265 | 266 | /** 267 | * @param null $member 268 | * @param array $context 269 | * @return bool|int 270 | */ 271 | public function canDelete($member = null, $context = []) 272 | { 273 | return Permission::check('Location_DELETE', 'any', $member); 274 | } 275 | 276 | /** 277 | * @param null $member 278 | * @param array $context 279 | * @return bool|int 280 | */ 281 | public function canCreate($member = null, $context = []) 282 | { 283 | return Permission::check('Location_CREATE', 'any', $member); 284 | } 285 | 286 | /** 287 | * @return array 288 | */ 289 | public function providePermissions() 290 | { 291 | return [ 292 | 'Location_EDIT' => 'Edit a Location', 293 | 'Location_DELETE' => 'Delete a Location', 294 | 'Location_CREATE' => 'Create a Location', 295 | ]; 296 | } 297 | 298 | /** 299 | * @return string 300 | */ 301 | public function getWebsiteURL() 302 | { 303 | $url = $this->Website; 304 | 305 | if ($url && !preg_match('/^(http|https):\/\//', $url)) { 306 | $url = 'http://' . $url; 307 | } 308 | 309 | $this->extend('updateWebsiteURL', $url); 310 | 311 | return $url; 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/Page/LocationPageController.php: -------------------------------------------------------------------------------- 1 | 'Enum("m,km","m")', 55 | 'ShowResultsDefault' => 'Boolean', 56 | 'ShowFormDefault' => 'Boolean', 57 | ]; 58 | 59 | /** 60 | * @var array 61 | */ 62 | private static $many_many = [ 63 | 'Categories' => LocationCategory::class, 64 | ]; 65 | 66 | /** 67 | * @var string 68 | */ 69 | private static $table_name = 'Locator'; 70 | 71 | /** 72 | * @var string 73 | */ 74 | private static $location_class = LocationPage::class; 75 | 76 | /** 77 | * @var string[] 78 | */ 79 | private static $extensions = [ 80 | Lumberjack::class, 81 | ]; 82 | 83 | /** 84 | * @var string[] 85 | */ 86 | private static $allowed_children = [ 87 | LocationPage::class, 88 | ]; 89 | 90 | /** 91 | * @var array 92 | */ 93 | private static $defaults = [ 94 | 'ShowResultsDefault' => false, 95 | 'ShowFormDefault' => true, 96 | 'ShowInMenus' => false, 97 | ]; 98 | 99 | /** 100 | * @return FieldList 101 | */ 102 | public function getCMSFields() 103 | { 104 | $this->beforeUpdateCMSFields(function ($fields) { 105 | // Settings 106 | $fields->addFieldsToTab('Root.Settings', [ 107 | HeaderField::create('DisplayOptions', 'Display Options', 3), 108 | OptionsetField::create('Unit', 'Unit of measure', ['m' => 'Miles', 'km' => 'Kilometers']), 109 | DropdownField::create('ShowResultsDefault') 110 | ->setTitle('Show results by default') 111 | ->setDescription('This will display results if no filters are applied to the locator') 112 | ->setSource([false => 'No', true => 'Yes']), 113 | DropdownField::create('ShowFormDefault') 114 | ->setTitle('Show filter form by default') 115 | ->setDescription('This will display the filter form for the locator') 116 | ->setSource([false => 'No', true => 'Yes']), 117 | ]); 118 | 119 | // Filter categories 120 | $config = GridFieldConfig_RelationEditor::create(); 121 | $config->removeComponentsByType(GridFieldAddExistingAutocompleter::class); 122 | $config->addComponent(new GridFieldAddExistingSearchButton()); 123 | $categories = $this->Categories(); 124 | $categoriesField = GridField::create('Categories', 'Categories', $categories, $config) 125 | ->setDescription('only show locations from the selected category'); 126 | 127 | // Filter 128 | $fields->addFieldsToTab('Root.Filter', [ 129 | HeaderField::create('CategoryOptionsHeader', 'Location Filtering', 3), 130 | $categoriesField, 131 | ]); 132 | }); 133 | 134 | return parent::getCMSFields(); 135 | } 136 | 137 | /** 138 | * @param array $filter 139 | * @param array $filterAny 140 | * @param array $exclude 141 | * @param null|callable $callback 142 | * 143 | * @return DataList|ArrayList 144 | */ 145 | public static function get_locations($filter = [], $filterAny = [], $exclude = [], $callback = null) 146 | { 147 | $locationClass = Config::inst()->get(static::class, 'location_class'); 148 | $locations = $locationClass::get()->filter($filter)->exclude($exclude); 149 | 150 | if (!empty($filterAny)) { 151 | $locations = $locations->filterAny($filterAny); 152 | } 153 | if (!empty($exclude)) { 154 | $locations = $locations->exclude($exclude); 155 | } 156 | 157 | if ($callback !== null && is_callable($callback)) { 158 | $locations->filterByCallback($callback); 159 | } 160 | 161 | return $locations; 162 | } 163 | 164 | /** 165 | * @return DataList 166 | */ 167 | public static function get_all_categories() 168 | { 169 | return LocationCategory::get(); 170 | } 171 | 172 | /** 173 | * @return ArrayList|ManyManyList 174 | */ 175 | public function getPageCategories(): ArrayList|ManyManyList 176 | { 177 | return self::locator_categories_by_locator($this->ID); 178 | } 179 | 180 | /** 181 | * @param int $id 182 | * @return ManyManyList|ArrayList 183 | */ 184 | public static function locator_categories_by_locator($id = 0): ArrayList|ManyManyList 185 | { 186 | if ($id == 0) { 187 | return ArrayList::create(); 188 | } 189 | 190 | /** @var Locator $locator */ 191 | if ($locator = static::get()->byID($id)) { 192 | if ($locator->Categories()->exists()) { 193 | return $locator->Categories(); 194 | } 195 | } 196 | 197 | return ArrayList::create(); 198 | } 199 | 200 | /** 201 | * Gets the list of radii 202 | * 203 | * @return ArrayList 204 | */ 205 | public function getRadii() 206 | { 207 | $radii = [ 208 | '0' => '25', 209 | '1' => '50', 210 | '2' => '75', 211 | '3' => '100', 212 | ]; 213 | $config_radii = $this->config()->get('radii'); 214 | if ($config_radii) { 215 | $radii = $config_radii; 216 | } 217 | 218 | return $radii; 219 | } 220 | 221 | public function getRadiiArrayList() 222 | { 223 | $list = []; 224 | 225 | foreach ($this->getRadii() as $radius) { 226 | $list[] = new ArrayData([ 227 | 'Radius' => $radius, 228 | ]); 229 | } 230 | 231 | return new ArrayList($list); 232 | } 233 | 234 | /** 235 | * Gets the limit of locations 236 | * @return mixed 237 | */ 238 | public function getLimit() 239 | { 240 | return $this->config()->get('limit'); 241 | } 242 | 243 | /** 244 | * Gets if the radius drop down should be shown 245 | * @return mixed 246 | */ 247 | public function getShowRadius() 248 | { 249 | return $this->config()->get('show_radius'); 250 | } 251 | 252 | /** 253 | * @return mixed 254 | * 255 | * @deprecated call the Categories() relation on the locator instead 256 | */ 257 | public function getUsedCategories() 258 | { 259 | return $this->Categories(); 260 | } 261 | 262 | /** 263 | * Gets the path of the info window template 264 | * 265 | * @return string 266 | */ 267 | public function getInfoWindowTemplate() 268 | { 269 | return ModuleResourceLoader::singleton()->resolveURL( 270 | Config::inst()->get( 271 | static::class, 272 | 'infoWindowTemplate' 273 | ) 274 | ); 275 | } 276 | 277 | /** 278 | * Gets the path of the list template 279 | * 280 | * @return string 281 | */ 282 | public function getListTemplate() 283 | { 284 | return ModuleResourceLoader::singleton()->resolveURL( 285 | Config::inst()->get( 286 | static::class, 287 | 'listTemplate' 288 | ) 289 | ); 290 | } 291 | 292 | /** 293 | * @return null|string 294 | */ 295 | public function getMapStyle() 296 | { 297 | return AddressDataExtension::getMapStyleJSON(); 298 | } 299 | 300 | public function getMapStyleJSONPath() 301 | { 302 | return AddressDataExtension::getMapStyleJSONPath(); 303 | } 304 | 305 | /** 306 | * @return null|string 307 | */ 308 | public function getMarkerIcon() 309 | { 310 | return AddressDataExtension::getIconImage(); 311 | } 312 | 313 | /** 314 | * @return string 315 | */ 316 | public function getLumberjackTitle() 317 | { 318 | return _t(__CLASS__ . '.LumberjackTitle', 'Locations'); 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/Page/LocatorController.php: -------------------------------------------------------------------------------- 1 | 0, 42 | 'Lng' => 0, 43 | ]; 44 | 45 | /** 46 | * @var array 47 | */ 48 | private static $base_filter_any = []; 49 | 50 | /** 51 | * @var bool 52 | */ 53 | private static $bootstrapify = true; 54 | 55 | /** 56 | * ID of map container 57 | * 58 | * @var string 59 | */ 60 | private static $map_container = 'map'; 61 | 62 | /** 63 | * class of location list container 64 | * 65 | * @var string 66 | */ 67 | private static $list_container = 'loc-list'; 68 | 69 | /** 70 | * GET variable which, if isset, will trigger storeLocator init and return XML 71 | * 72 | * @var string 73 | */ 74 | private static $query_trigger = 'action_doFilterLocations'; 75 | 76 | /** 77 | * @var DataList|ArrayList 78 | */ 79 | protected $locations; 80 | 81 | /** 82 | * Set Requirements based on input from CMS 83 | */ 84 | public function init() 85 | { 86 | parent::init(); 87 | 88 | // prevent init of map if no query 89 | $request = Controller::curr()->getRequest(); 90 | if (!$this->getTrigger($request) && !$this->ShowResultsDefault) { 91 | return; 92 | } 93 | 94 | $locations = $this->getLocations(); 95 | 96 | // prevent init of map if there are no locations 97 | if (!$locations) { 98 | return; 99 | } 100 | 101 | // google maps api key 102 | $key = Config::inst()->get(GoogleGeocoder::class, 'map_api_key'); 103 | Requirements::javascript('https://maps.google.com/maps/api/js?key=' . $key); 104 | 105 | $mapSettings = [ 106 | 'fullMapStart' => true, 107 | 'maxDistance' => true, 108 | 'storeLimit' => -1, 109 | 'originMarker' => true, 110 | 'slideMap' => false, 111 | 'distanceAlert' => -1, 112 | 'featuredLocations' => false, 113 | 'defaultLat' => 0, 114 | 'defaultLng' => 0, 115 | 'mapSettings' => [ 116 | 'zoom' => 12, 117 | 'mapTypeId' => 'google.maps.MapTypeId.ROADMAP', 118 | 'disableDoubleClickZoom' => true, 119 | 'scrollwheel' => false, 120 | 'navigationControl' => false, 121 | 'draggable' => false, 122 | ], 123 | ]; 124 | 125 | $mapSettings['listTemplatePath'] = $this->getListTemplate(); 126 | $mapSettings['infowindowTemplatePath'] = $this->getInfoWindowTemplate(); 127 | $mapSettings['lengthUnit'] = $this->data()->Unit == 'km' ? 'km' : 'm'; 128 | $mapSettings['mapID'] = Config::inst()->get(LocatorController::class, 'map_container'); 129 | $mapSettings['locationList'] = Config::inst()->get(LocatorController::class, 'list_container'); 130 | 131 | if ($locations->filter('Featured', true)->count() > 0) { 132 | $mapSettings['featuredLocations'] = true; 133 | } 134 | 135 | // map config based on user input in Settings tab 136 | $limit = Config::inst()->get(LocatorController::class, 'limit'); 137 | if (0 < $limit) { 138 | $mapSettings['storeLimit'] = $limit; 139 | } 140 | 141 | if ($coords = $this->getAddressSearchCoords()) { 142 | $mapSettings['defaultLat'] = $coords->getField("Lat"); 143 | $mapSettings['defaultLat'] = $coords->getField("Lng"); 144 | } 145 | 146 | // pass GET variables to xml action 147 | $vars = $this->request->getVars(); 148 | unset($vars['url']); 149 | $url = ''; 150 | if (count($vars)) { 151 | $url .= '?' . http_build_query($vars); 152 | } 153 | $mapSettings['dataLocation'] = Controller::join_links($this->Link(), 'xml.xml', $url); 154 | 155 | if ($stylePath = $this->getMapStyleJSONPath()) { 156 | if ($style = file_get_contents($stylePath)) { 157 | $mapSettings['mapSettings']['styles'] = json_decode($style); 158 | } 159 | } 160 | 161 | if ($imagePath = $this->getMarkerIcon()) { 162 | $mapSettings['markerImg'] = $imagePath; 163 | } 164 | 165 | $this->extend('modifyMapSettings', $mapSettings); 166 | 167 | $encoded = json_encode($mapSettings, JSON_UNESCAPED_SLASHES); 168 | // mapTypeId is a javascript object 169 | $encoded = preg_replace('/("mapTypeId"\s*:\s*)"(.+?)"/i', '$1$2', $encoded); 170 | 171 | $this->extend('modifyMapSettingsEncoded', $encoded); 172 | // init map 173 | Requirements::customScript(" 174 | $(function(){ 175 | $('#map-container').storeLocator({$encoded}); 176 | }); 177 | ", 'jquery-locator'); 178 | } 179 | 180 | /** 181 | * @param HTTPRequest $request 182 | * 183 | * @return bool 184 | */ 185 | public function getTrigger(HTTPRequest $request = null) 186 | { 187 | if ($request === null) { 188 | $request = $this->getRequest(); 189 | } 190 | $trigger = $request->getVar(Config::inst()->get(LocatorController::class, 'query_trigger')); 191 | 192 | return isset($trigger); 193 | } 194 | 195 | /** 196 | * @return ArrayList|DataList 197 | */ 198 | public function getLocations() 199 | { 200 | if (!$this->locations) { 201 | $this->setLocations($this->request); 202 | } 203 | 204 | return $this->locations; 205 | } 206 | 207 | /** 208 | * @param HTTPRequest|null $request 209 | * 210 | * @return $this 211 | */ 212 | public function setLocations(HTTPRequest $request = null) 213 | { 214 | 215 | if ($request === null) { 216 | $request = $this->request; 217 | } 218 | $filter = $this->config()->get('base_filter'); 219 | 220 | $categoryVar = $this->data()->config()->get('category_var'); 221 | if ($request->getVar($categoryVar)) { 222 | $filter['Categories.ID'] = $request->getVar($categoryVar); 223 | } else { 224 | if ($this->getPageCategories()->exists()) { 225 | foreach ($this->getPageCategories() as $category) { 226 | $filter['Categories.ID'][] = $category->ID; 227 | } 228 | } 229 | } 230 | 231 | $this->extend('updateLocatorFilter', $filter, $request); 232 | 233 | $filterAny = $this->config()->get('base_filter_any'); 234 | $this->extend('updateLocatorFilterAny', $filterAny, $request); 235 | 236 | $exclude = $this->config()->get('base_exclude'); 237 | $this->extend('updateLocatorExclude', $exclude, $request); 238 | 239 | $class = $this->data()->ClassName; 240 | $locations = $class::get_locations($filter, $filterAny, $exclude); 241 | $locations = DataToArrayListHelper::to_array_list($locations); 242 | 243 | //allow for adjusting list post possible distance calculation 244 | $this->extend('updateLocationList', $locations); 245 | 246 | if ($locations->canSortBy('Distance')) { 247 | $locations = $locations->sort('Distance'); 248 | } 249 | 250 | if ($this->getShowRadius()) { 251 | $radiusVar = $this->data()->config()->get('radius_var'); 252 | 253 | if ($radius = (int)$request->getVar($radiusVar)) { 254 | $locations = $locations->filterByCallback(function ($location) use (&$radius) { 255 | return $location->Distance <= $radius; 256 | }); 257 | } 258 | } 259 | 260 | //allow for returning list to be set as 261 | $this->extend('updateListType', $locations); 262 | 263 | $limit = $this->getLimit(); 264 | if ($limit > 0) { 265 | $locations = $locations->limit($limit); 266 | } 267 | 268 | $this->locations = $locations; 269 | 270 | return $this; 271 | } 272 | 273 | /** 274 | * @return ArrayData|boolean 275 | */ 276 | public function getAddressSearchCoords() 277 | { 278 | $addressVar = Config::inst()->get(DistanceDataExtension::class, 'address_var'); 279 | if (!$this->request->getVar($addressVar)) { 280 | return false; 281 | } 282 | if (class_exists(GoogleGeocoder::class)) { 283 | $geocoder = new GoogleGeocoder($this->request->getVar($addressVar)); 284 | $response = $geocoder->getResult(); 285 | $lat = $this->owner->Lat = $response->getCoordinates()->getLatitude(); 286 | $lng = $this->owner->Lng = $response->getCoordinates()->getLongitude(); 287 | 288 | return new ArrayData([ 289 | "Lat" => $lat, 290 | "Lng" => $lng, 291 | ]); 292 | } 293 | } 294 | 295 | /** 296 | * @param HTTPRequest $request 297 | * 298 | * @return \SilverStripe\View\ViewableData_Customised 299 | */ 300 | public function index(HTTPRequest $request) 301 | { 302 | if ($this->getTrigger($request) || $this->ShowResultsDefault) { 303 | $locations = $this->getLocations(); 304 | } else { 305 | $locations = ArrayList::create(); 306 | } 307 | 308 | return $this->customise([ 309 | 'Locations' => $locations, 310 | ]); 311 | } 312 | 313 | /** 314 | * Renders locations in xml format 315 | * 316 | * @return \SilverStripe\ORM\FieldType\DBHTMLText 317 | */ 318 | public function xml() 319 | { 320 | $this->getResponse()->addHeader("Content-Type", "application/xml"); 321 | $data = new ArrayData([ 322 | "Locations" => $this->getLocations(), 323 | "AddressCoords" => $this->getAddressSearchCoords(), 324 | ]); 325 | 326 | return $data->renderWith('Dynamic/Locator/Data/XML'); 327 | } 328 | 329 | /** 330 | * Renders locations in json format 331 | * 332 | * @return \SilverStripe\ORM\FieldType\DBHTMLText 333 | */ 334 | public function json() 335 | { 336 | $this->getResponse()->addHeader("Content-Type", "application/json"); 337 | $data = new ArrayData([ 338 | "Locations" => $this->getLocations(), 339 | "AddressCoords" => $this->getAddressSearchCoords(), 340 | ]); 341 | 342 | return $data->renderWith('Dynamic/Locator/Data/JSON'); 343 | } 344 | 345 | /** 346 | * LocationSearch form. 347 | * 348 | * Search form for locations, updates map and results list via AJAX 349 | * 350 | * @return LocatorForm|null 351 | */ 352 | public function LocationSearch(): ?LocatorForm 353 | { 354 | if (!$this->ShowFormDefault) { 355 | return null; 356 | } 357 | 358 | $form = LocatorForm::create($this, 'LocationSearch'); 359 | 360 | $this->extend('updateLocationSearch', $form); 361 | 362 | return $form 363 | ->setFormMethod('GET') 364 | ->setFormAction($this->Link()) 365 | ->disableSecurityToken() 366 | ->loadDataFrom($this->request->getVars()); 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/Tasks/EmailAddressTask.php: -------------------------------------------------------------------------------- 1 | set('DataObject', 'validation_enabled', false); 32 | 33 | $ct = 0; 34 | $updateEmail = function ($location) use (&$ct) { 35 | if (!$location->Email && $location->EmailAddress) { 36 | $location->Email = $location->EmailAddress; 37 | $location->write(); 38 | ++$ct; 39 | } 40 | }; 41 | 42 | Location::get()->each($updateEmail); 43 | Config::modify()->set('DataObject', 'validation_enabled', true); 44 | 45 | echo '

    ' . $ct . ' Locations updated

    '; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Tasks/LocationCategoriesTask.php: -------------------------------------------------------------------------------- 1 | getVar('locationclass')) ? $request->getVar('locationclass') : Location::class; 39 | $class::add_extension(LocationCategoryExtension::class); 40 | 41 | 42 | $categories = []; 43 | 44 | $convert = function (DataObject $location) use (&$categories) { 45 | /** @var Location $location */ 46 | // skip if no category 47 | if ($location->CategoryID > 0) { 48 | $categories[$location->CategoryID][] = $location->ID; 49 | } 50 | }; 51 | 52 | foreach ($this->iterateLocations($class) as $location) { 53 | $convert($location); 54 | } 55 | 56 | $catCt = 0; 57 | $locCT = 0; 58 | 59 | foreach ($categories as $categoryID => $locations) { 60 | /** @var LocationCategory $category */ 61 | $category = LocationCategory::get()->byID($categoryID); 62 | $category->Locations()->addMany($locations); 63 | 64 | $catCt++; 65 | $locCT += count($locations); 66 | } 67 | 68 | echo "{$catCt} categories converted
    "; 69 | echo "{$locCT} location relations converted
    "; 70 | 71 | $time = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]; 72 | echo "Process Time: {$time} seconds"; 73 | } 74 | 75 | /** 76 | * @param string $class 77 | * @return \Generator 78 | */ 79 | protected function iterateLocations($class) 80 | { 81 | foreach ($class::get() as $location) { 82 | yield $location; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Tasks/LocationCategoryExtension.php: -------------------------------------------------------------------------------- 1 | LocationCategory::class, 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /src/Tasks/LocationPublishTask.php: -------------------------------------------------------------------------------- 1 | getVar('locationclass')) ? $request->getVar('locationclass') : 'Location'; 33 | $this->publishLocations($class); 34 | } 35 | 36 | /** 37 | * @param string $class 38 | * @return \Generator 39 | */ 40 | protected function iterateLocations($class) 41 | { 42 | foreach ($class::get()->filter('ShowInLocator', true) as $location) { 43 | yield $location; 44 | } 45 | } 46 | 47 | /** 48 | * mark all ProductDetail records as ShowInMenus = 0. 49 | * 50 | * @param string $class 51 | */ 52 | public function publishLocations($class) 53 | { 54 | if (!isset($class) || !class_exists($class) || !$class instanceof Location) { 55 | $class = 'Location'; 56 | } 57 | $ct = 0; 58 | $publish = function ($location) use (&$ct) { 59 | if (!$location->isPublished()) { 60 | $title = $location->Title; 61 | $location->writeToStage('Stage'); 62 | $location->publish('Stage', 'Live'); 63 | static::write_message($title); 64 | ++$ct; 65 | } 66 | }; 67 | 68 | foreach ($this->iterateLocations($class) as $location) { 69 | $publish($location); 70 | } 71 | 72 | static::write_message("{$ct} locations updated."); 73 | } 74 | 75 | /** 76 | * @param $message 77 | */ 78 | protected static function write_message($message) 79 | { 80 | if (Director::is_cli()) { 81 | echo "{$message}\n"; 82 | } else { 83 | echo "{$message}

    "; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Tasks/LocationToPageTask.php: -------------------------------------------------------------------------------- 1 | migrateLocations(); 39 | } 40 | 41 | /** 42 | * 43 | */ 44 | protected function migrateLocations() 45 | { 46 | if (!$parent = Locator::get()->first()) { 47 | $parent = Locator::create(); 48 | $parent->Title = "Locator"; 49 | $parent->writeToStage(Versioned::DRAFT); 50 | } 51 | 52 | /** @var Location $location */ 53 | foreach ($this->getLocations() as $location) { 54 | if (!LocationPage::get()->filter('LegacyObjectID', $location->ID)->first()) { 55 | if ($location->hasMethod('isPublished')) { 56 | $published = $location->isPublished(); 57 | } 58 | 59 | /** @var LocationPage $newLocation */ 60 | $newLocation = Injector::inst()->create(LocationPage::class, $location->toMap(), false); 61 | $newLocation->setClassName(LocationPage::class); 62 | $newLocation->ID = null; 63 | $newLocation->ParentID = $parent->ID; 64 | $newLocation->LegacyObjectID = $location->ID; 65 | 66 | $this->extend('preLocationMigrationUpdate', $location, $newLocation); 67 | 68 | $newLocation->writeToStage(Versioned::DRAFT); 69 | 70 | if ((isset($published) && $published) || $this->config()->get('auto_publish_location')) { 71 | $newLocation->publishSingle(); 72 | } 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * @return \Generator 79 | */ 80 | protected function getLocations() 81 | { 82 | foreach (Location::get() as $location) { 83 | yield $location; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/admin/LocationsAdmin.php: -------------------------------------------------------------------------------- 1 | LocationPageCsvBulkLoader::class, 36 | LocationCategory::class => CsvBulkLoader::class, 37 | ]; 38 | 39 | /** 40 | * @var string 41 | */ 42 | private static $menu_title = 'Locations'; 43 | 44 | /** 45 | * @var string 46 | */ 47 | private static $url_segment = 'locations'; 48 | 49 | /** 50 | * @return array 51 | */ 52 | public function getExportFields() 53 | { 54 | if ($this->modelClass == LocationPage::class) { 55 | $fields = [ 56 | 'Title' => 'Name', 57 | 'Address' => 'Address', 58 | 'Address2' => 'Address2', 59 | 'City' => 'City', 60 | 'State' => 'State', 61 | 'PostalCode' => 'PostalCode', 62 | 'CountryCode' => 'Country', 63 | 'Phone' => 'Phone', 64 | 'Fax' => 'Fax', 65 | 'Email' => 'Email', 66 | 'Website' => 'Website', 67 | 'Featured' => 'Featured', 68 | 'CategoryList' => 'Categories', 69 | 'Lat' => 'Lat', 70 | 'Lng' => 'Lng', 71 | 'Import_ID' => 'Import_ID', 72 | ]; 73 | } 74 | 75 | if (!isset($fields)) { 76 | $fields = parent::getExportFields(); 77 | } 78 | 79 | $this->extend('updateGetExportFields', $fields); 80 | 81 | return $fields; 82 | } 83 | 84 | /** 85 | * @param null $id 86 | * @param null $fields 87 | * @return $this|Form 88 | */ 89 | public function getEditForm($id = null, $fields = null) 90 | { 91 | $form = parent::getEditForm($id, $fields); 92 | 93 | if ($this->modelClass == LocationPage::class) { 94 | /** @var GridField $gridField */ 95 | if ($gridField = $form->Fields()->fieldByName($this->sanitiseClassName($this->modelClass))) { 96 | $exportButton = new GridFieldExportButton('buttons-before-left'); 97 | $exportButton->setExportColumns($this->getExportFields()); 98 | $gridField->getConfig() 99 | ->addComponent($exportButton) 100 | ->removeComponentsByType('GridFieldDeleteAction'); 101 | } 102 | 103 | if ($this->showImportForm) { 104 | $fieldConfig = $gridField->getConfig(); 105 | $fieldConfig->addComponent( 106 | GridFieldImportButton::create('buttons-before-left') 107 | ->setImportForm($this->ImportForm()) 108 | ->setModalTitle(_t('SilverStripe\\Admin\\ModelAdmin.IMPORT', 'Import from CSV')) 109 | ); 110 | } 111 | } 112 | 113 | return $form; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/bulkloader/LocationCsvBulkLoader.php: -------------------------------------------------------------------------------- 1 | 'Title', 23 | 'EmailAddress' => 'Email', 24 | 'Categories' => '->getCategoryByName', 25 | 'Import_ID' => 'Import_ID', 26 | 'Country' => '->getCountryByName', 27 | ); 28 | 29 | /** 30 | * @var array 31 | */ 32 | public $duplicateChecks = array( 33 | 'Import_ID' => 'Import_ID' 34 | ); 35 | 36 | /** 37 | * @param $val 38 | * @return string|string[]|null 39 | */ 40 | public function getEscape($val) 41 | { 42 | return preg_replace("/\r|\n/", "", $val); 43 | } 44 | 45 | /** 46 | * @param $obj 47 | * @param $val 48 | * @param $record 49 | * @throws \SilverStripe\ORM\ValidationException 50 | */ 51 | public static function getCategoryByName(&$obj, $val, $record) 52 | { 53 | $val = Convert::raw2sql($val); 54 | $parts = explode(', ', $val); 55 | 56 | foreach ($parts as $part) { 57 | $category = LocationCategory::get()->filter(array('Name' => $part))->first(); 58 | if (!$category) { 59 | $category = LocationCategory::create(); 60 | $category->Name = $part; 61 | $category->write(); 62 | } 63 | $obj->Categories()->add($category); 64 | } 65 | } 66 | 67 | /** 68 | * @param $obj 69 | * @param $val 70 | * @param $record 71 | */ 72 | public function getCountryByName(&$obj, $val, $record) 73 | { 74 | $val = $this->getEscape($val); 75 | $countries = IntlLocales::singleton()->getCountries(); 76 | 77 | if (strlen((string)$val) == 2) { 78 | $val = strtolower($val); 79 | if (array_key_exists($val, $countries)) { 80 | $obj->Country = $val; 81 | } 82 | } 83 | 84 | if (in_array($val, $countries)) { 85 | $obj->Country = array_search($val, $countries); 86 | } 87 | } 88 | 89 | /** 90 | * @param array $record 91 | * @param array $columnMap 92 | * @param \SilverStripe\Dev\BulkLoader_Result $results 93 | * @param bool $preview 94 | * @return int|void 95 | */ 96 | protected function processRecord($record, $columnMap, &$results, $preview = false) 97 | { 98 | $objID = parent::processRecord($record, $columnMap, $results, $preview = false); 99 | 100 | $location = Location::get()->byID($objID); 101 | $location->publishSingle(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/bulkloader/LocationPageCsvBulkLoader.php: -------------------------------------------------------------------------------- 1 | 'Title', 24 | 'EmailAddress' => 'Email', 25 | 'Categories' => '->getCategoryByName', 26 | 'Import_ID' => 'Import_ID', 27 | 'Country' => '->getCountryByName', 28 | ); 29 | 30 | /** 31 | * @var array 32 | */ 33 | public $duplicateChecks = array( 34 | 'Import_ID' => 'Import_ID' 35 | ); 36 | 37 | /** 38 | * @param $val 39 | * @return string|string[]|null 40 | */ 41 | public function getEscape($val) 42 | { 43 | return preg_replace("/\r|\n/", "", $val); 44 | } 45 | 46 | /** 47 | * @param $obj 48 | * @param $val 49 | * @param $record 50 | * @throws \SilverStripe\ORM\ValidationException 51 | */ 52 | public static function getCategoryByName(&$obj, $val, $record) 53 | { 54 | if ($val = Convert::raw2sql($val)) { 55 | $parts = explode(', ', $val); 56 | 57 | foreach ($parts as $part) { 58 | $category = LocationCategory::get()->filter(array('Name' => $part))->first(); 59 | if (!$category) { 60 | $category = LocationCategory::create(); 61 | $category->Name = $part; 62 | $category->write(); 63 | } 64 | $obj->Categories()->add($category); 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * @param $obj 71 | * @param $val 72 | * @param $record 73 | */ 74 | public function getCountryByName(&$obj, $val, $record) 75 | { 76 | $val = $this->getEscape($val); 77 | $countries = IntlLocales::singleton()->getCountries(); 78 | 79 | if (strlen((string)$val) == 2) { 80 | $val = strtolower($val); 81 | if (array_key_exists($val, $countries)) { 82 | $obj->Country = $val; 83 | } 84 | } 85 | 86 | if (in_array($val, $countries)) { 87 | $obj->Country = array_search($val, $countries); 88 | } 89 | } 90 | 91 | /** 92 | * @param array $record 93 | * @param array $columnMap 94 | * @param \SilverStripe\Dev\BulkLoader_Result $results 95 | * @param bool $preview 96 | * @return int|void 97 | */ 98 | protected function processRecord($record, $columnMap, &$results, $preview = false) 99 | { 100 | $objID = parent::processRecord($record, $columnMap, $results, $preview = false); 101 | 102 | $location = LocationPage::get()->byID($objID); 103 | $location->ParentID = Locator::get()->first()->ID; 104 | $location->publishSingle(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/form/LocatorForm.php: -------------------------------------------------------------------------------- 1 | setTitle('Address or Postal Code') 32 | ); 33 | 34 | $pageCategories = Locator::locator_categories_by_locator($controller->data()->ID); 35 | if ($pageCategories && $pageCategories->count() > 0) { 36 | $categories = false; 37 | } else { 38 | $categories = Locator::get_all_categories(); 39 | if ($categories->count() < 1) { 40 | $categories = false; 41 | } 42 | } 43 | 44 | if ($categories) { 45 | $categoriesField = DropdownField::create('CategoryID') 46 | ->setTitle('Category') 47 | ->setEmptyString('All Categories') 48 | ->setSource($categories->map()); 49 | $fields->push($categoriesField); 50 | } 51 | 52 | if ($controller->getShowRadius()) { 53 | $radiusArray = array_values($controller->getRadii()); 54 | $this->extend('overrideRadiusArray', $radiusArray); 55 | $fields->push(DropdownField::create('Radius', 'Search Radius', ArrayLib::valuekey($radiusArray)) 56 | ->setEmptyString('Radius')); 57 | } 58 | 59 | $actions = FieldList::create( 60 | FormAction::create('doFilterLocations') 61 | ->setTitle('Search') 62 | ); 63 | 64 | $validator = $this->getValidator(); 65 | 66 | parent::__construct($controller, $name, $fields, $actions, $validator); 67 | } 68 | 69 | /** 70 | * @return null|RequiredFields|\SilverStripe\Forms\Validator 71 | */ 72 | public function getValidator() 73 | { 74 | $validator = parent::getValidator(); 75 | if (empty($validator)) { 76 | if (!$this->validator instanceof RequiredFields) { 77 | $this->setValidator(RequiredFields::create('Address')); 78 | } 79 | $validator = $this->validator; 80 | } 81 | $this->extend('updateRequiredFields', $validator); 82 | return $validator; 83 | } 84 | 85 | /** 86 | * @return FieldList 87 | */ 88 | public function Fields() 89 | { 90 | $fields = parent::Fields(); 91 | $this->extend('updateLocatorFormFields', $fields); 92 | return $fields; 93 | } 94 | 95 | /** 96 | * @return \SilverStripe\Forms\FieldList 97 | */ 98 | public function Actions() 99 | { 100 | $actions = parent::Actions(); 101 | $this->extend('updateLocatorActions', $actions); 102 | return $actions; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /templates/Dynamic/Locator/Data/JSON.ss: -------------------------------------------------------------------------------- 1 | { 2 | "locations": [ 3 | <% loop $Locations %>{ 4 | "ID": $ID, 5 | "Title": "$Title", 6 | "Featured": $Featured, 7 | "Website": "$WebsiteURL", 8 | "Phone": "$Phone", 9 | "Fax": "$Fax", 10 | "Email": "$Email", 11 | "Address": "$Address", 12 | "Address2": "$Address2", 13 | "City": "$City", 14 | "State": "$State", 15 | "PostalCode": "$PostalCode", 16 | "Country": "$Country", 17 | "Distance": <% if $Distance %>$Distance<% else %>-1<% end_if %>, 18 | "Lat": $Lat, 19 | "Lng": $Lng 20 | }<% if not $Last %>,<%end_if%><% end_loop %> 21 | ]<% if $AddressCoords %>, 22 | "center": { 23 | "Lat": $AddressCoords.Lat, 24 | "Lng": $AddressCoords.Lng 25 | }<% end_if %> 26 | } 27 | -------------------------------------------------------------------------------- /templates/Dynamic/Locator/Data/XML.ss: -------------------------------------------------------------------------------- 1 | 2 | <% loop $Locations %>distance="$Distance"<% end_if %>/> 3 | <% end_loop %> 4 | 5 | -------------------------------------------------------------------------------- /templates/Dynamic/Locator/Page/Layout/Locator.ss: -------------------------------------------------------------------------------- 1 | <% require css('dynamic/silverstripe-locator: css/map.css') %> 2 |
    3 |
    4 | $ElementalArea 5 |
    6 | 7 |
    8 |
    9 |
    10 | $LocationSearch 11 |
    12 |
    13 |
    14 | <% if $getTrigger %> 15 | <% if $Locations %> 16 |
    17 |
    18 |
    19 |
      20 |
    21 |
    22 |
    23 | <% else %> 24 |
    25 |

    No locations match your search criteria. Please refine your search and try again.

    26 |
    27 | <% end_if %> 28 | <% end_if %> 29 |
    30 | 31 | <% require javascript('silverstripe/admin: thirdparty/jquery/jquery.js') %> 32 | <% require javascript('dynamic/silverstripe-locator: thirdparty/jquery-store-locator-plugin/assets/js/libs/handlebars.min.js') %> 33 | <% require javascript('dynamic/silverstripe-locator: thirdparty/jquery-store-locator-plugin/assets/js/plugins/storeLocator/jquery.storelocator.js') %> 34 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/css/bootstrap-example.css: -------------------------------------------------------------------------------- 1 | /* Infowindow Roboto font override */ 2 | .gm-style div, 3 | .gm-style span, 4 | .gm-style label, 5 | .gm-style a { 6 | font-family: Arial, Helvetica, sans-serif; 7 | } 8 | .bh-sl-error { 9 | clear: both; 10 | float: left; 11 | width: 100%; 12 | padding: 10px 0; 13 | color: #ae2118; 14 | font-weight: bold; 15 | } 16 | .bh-sl-container { 17 | color: #333333; 18 | /* Avoid image issues with Google Maps and CSS resets */ 19 | /* Avoid issues with Google Maps and CSS frameworks */ 20 | } 21 | .bh-sl-container img { 22 | max-width: none !important; 23 | border-radius: 0 !important; 24 | box-shadow: none !important; 25 | } 26 | .bh-sl-container > * { 27 | box-sizing: content-box !important; 28 | } 29 | .bh-sl-container .jumbotron { 30 | padding-top: 30px; 31 | } 32 | .bh-sl-container .form-input input, 33 | .bh-sl-container .form-input select, 34 | .bh-sl-container .form-input label { 35 | margin-right: 10px; 36 | } 37 | .bh-sl-container .bh-sl-loading { 38 | float: left; 39 | margin: 4px 0 0 10px; 40 | width: 16px; 41 | height: 16px; 42 | background: url(../img/ajax-loader.gif) no-repeat; 43 | } 44 | .bh-sl-container .bh-sl-filters-container { 45 | clear: both; 46 | width: 100%; 47 | margin: 15px 0; 48 | } 49 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters { 50 | list-style: none; 51 | float: left; 52 | padding: 0; 53 | margin: 0 100px 0 0; 54 | } 55 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li { 56 | display: block; 57 | clear: left; 58 | float: left; 59 | width: 100%; 60 | margin: 5px 0; 61 | } 62 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li label { 63 | display: inline; 64 | } 65 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li input { 66 | display: block; 67 | float: left; 68 | margin: 2px 8px 2px 0; 69 | } 70 | .bh-sl-container .bh-sl-map-container { 71 | margin-top: 27px; 72 | } 73 | .bh-sl-container .bh-sl-map-container a { 74 | color: #005293; 75 | text-decoration: none; 76 | } 77 | .bh-sl-container .bh-sl-map-container a:hover, 78 | .bh-sl-container .bh-sl-map-container a:active { 79 | text-decoration: underline; 80 | } 81 | .bh-sl-container .bh-sl-loc-list { 82 | height: 530px; 83 | overflow-x: auto; 84 | font-size: 13px; 85 | } 86 | .bh-sl-container .bh-sl-loc-list ul { 87 | display: block; 88 | clear: left; 89 | float: left; 90 | width: 100%; 91 | list-style: none; 92 | margin: 0; 93 | padding: 0; 94 | } 95 | .bh-sl-container .bh-sl-loc-list ul li { 96 | display: block; 97 | clear: left; 98 | float: left; 99 | margin: 3% 4%; 100 | cursor: pointer; 101 | width: 92%; 102 | border: 1px solid #ffffff; 103 | /* Adding this to prevent moving li elements when adding the list-focus class*/ 104 | } 105 | .bh-sl-container .bh-sl-loc-list .list-label { 106 | float: left; 107 | margin: 10px 0 0 6px; 108 | padding: 4px; 109 | width: 27px; 110 | text-align: center; 111 | background: #00192d; 112 | color: #ffffff; 113 | font-weight: bold; 114 | border-radius: 15px; 115 | } 116 | .bh-sl-container .bh-sl-loc-list .list-details { 117 | float: left; 118 | margin-left: 6px; 119 | width: 80%; 120 | } 121 | .bh-sl-container .bh-sl-loc-list .list-details .list-content { 122 | padding: 10px; 123 | } 124 | .bh-sl-container .bh-sl-loc-list .list-details .loc-dist { 125 | font-weight: bold; 126 | font-style: italic; 127 | color: #8e8e8e; 128 | } 129 | .bh-sl-container .bh-sl-loc-list .list-focus { 130 | border: 1px solid rgba(0, 82, 147, 0.4); 131 | -moz-box-shadow: 0 0 8px rgba(0, 82, 147, 0.4); 132 | -webkit-box-shadow: 0 0 8px rgba(0, 82, 147, 0.4); 133 | box-shadow: 0 0 8px rgba(0, 100, 180, 0.4); 134 | transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s; 135 | } 136 | .bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container { 137 | width: 100%; 138 | height: 20px; 139 | position: relative; 140 | } 141 | .bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container .bh-sl-close-icon { 142 | top: 0; 143 | right: 6px; 144 | } 145 | .bh-sl-container .bh-sl-loc-list .bh-sl-noresults-title { 146 | font-weight: bold; 147 | } 148 | .bh-sl-container .loc-name { 149 | /* Picked up by both list and infowindows */ 150 | font-size: 15px; 151 | font-weight: bold; 152 | } 153 | .bh-sl-container .bh-sl-map { 154 | height: 530px; 155 | } 156 | .bh-sl-container .bh-sl-pagination-container { 157 | clear: both; 158 | } 159 | .bh-sl-container .bh-sl-pagination-container ol { 160 | list-style-type: none; 161 | text-align: center; 162 | margin: 0; 163 | padding: 10px 0; 164 | } 165 | .bh-sl-container .bh-sl-pagination-container ol li { 166 | display: inline-block; 167 | padding: 10px; 168 | cursor: pointer; 169 | font: bold 14px Arial, Helvetica, sans-serif; 170 | color: #005293; 171 | } 172 | .bh-sl-container .bh-sl-pagination-container ol .bh-sl-current { 173 | color: #333333; 174 | cursor: auto; 175 | text-decoration: none; 176 | } 177 | /* Modal window */ 178 | .bh-sl-overlay { 179 | position: fixed; 180 | left: 0; 181 | top: 0; 182 | width: 100%; 183 | height: 100%; 184 | z-index: 10000; 185 | background: url(../img/overlay-bg.png) repeat; 186 | } 187 | .bh-sl-overlay .bh-sl-modal-window { 188 | position: absolute; 189 | left: 50%; 190 | margin-left: -460px; 191 | /* width divided by 2 */ 192 | margin-top: 60px; 193 | width: 920px; 194 | height: 620px; 195 | z-index: 10010; 196 | background: #ffffff; 197 | border-radius: 10px; 198 | box-shadow: 0 0 10px #656565; 199 | } 200 | .bh-sl-overlay .bh-sl-modal-window .bh-sl-map-container { 201 | margin-top: 50px; 202 | /* increase map container margin */ 203 | } 204 | .bh-sl-overlay .bh-sl-modal-window .bh-sl-modal-content { 205 | float: left; 206 | padding: 0 22px; 207 | /* there's already a margin on the top of the map-container div */ 208 | } 209 | .bh-sl-overlay .bh-sl-modal-window .bh-sl-close-icon { 210 | top: 13px; 211 | right: 22px; 212 | } 213 | .bh-sl-close-icon { 214 | position: absolute; 215 | cursor: pointer; 216 | height: 24px; 217 | width: 24px; 218 | } 219 | .bh-sl-close-icon:after, 220 | .bh-sl-close-icon:before { 221 | position: absolute; 222 | top: 3px; 223 | right: 3px; 224 | bottom: 0; 225 | left: 50%; 226 | background: #cccccc; 227 | content: ''; 228 | display: block; 229 | height: 24px; 230 | margin: -3px 0 0 -1px; 231 | width: 3px; 232 | -webkit-transform: rotate(45deg); 233 | -moz-transform: rotate(45deg); 234 | -ms-transform: rotate(45deg); 235 | -o-transform: rotate(45deg); 236 | transform: rotate(45deg); 237 | } 238 | .bh-sl-close-icon:hover:after, 239 | .bh-sl-close-icon:hover:before { 240 | background: #b3b3b3; 241 | } 242 | .bh-sl-close-icon:before { 243 | -webkit-transform: rotate(-45deg); 244 | -moz-transform: rotate(-45deg); 245 | -ms-transform: rotate(-45deg); 246 | -o-transform: rotate(-45deg); 247 | transform: rotate(-45deg); 248 | } 249 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/css/bootstrap-example.min.css: -------------------------------------------------------------------------------- 1 | .gm-style a,.gm-style div,.gm-style label,.gm-style span{font-family:Arial,Helvetica,sans-serif}.bh-sl-error{clear:both;float:left;width:100%;padding:10px 0;color:#ae2118;font-weight:700}.bh-sl-container{color:#333}.bh-sl-container img{max-width:none!important;border-radius:0!important;box-shadow:none!important}.bh-sl-container>*{box-sizing:content-box!important}.bh-sl-container .jumbotron{padding-top:30px}.bh-sl-container .form-input input,.bh-sl-container .form-input label,.bh-sl-container .form-input select{margin-right:10px}.bh-sl-container .bh-sl-loading{float:left;margin:4px 0 0 10px;width:16px;height:16px;background:url(../img/ajax-loader.gif) no-repeat}.bh-sl-container .bh-sl-filters-container{clear:both;width:100%;margin:15px 0}.bh-sl-container .bh-sl-filters-container .bh-sl-filters{list-style:none;float:left;padding:0;margin:0 100px 0 0}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li{display:block;clear:left;float:left;width:100%;margin:5px 0}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li label{display:inline}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li input{display:block;float:left;margin:2px 8px 2px 0}.bh-sl-container .bh-sl-map-container{margin-top:27px}.bh-sl-container .bh-sl-map-container a{color:#005293;text-decoration:none}.bh-sl-container .bh-sl-map-container a:active,.bh-sl-container .bh-sl-map-container a:hover{text-decoration:underline}.bh-sl-container .bh-sl-loc-list{height:530px;overflow-x:auto;font-size:13px}.bh-sl-container .bh-sl-loc-list ul{display:block;clear:left;float:left;width:100%;list-style:none;margin:0;padding:0}.bh-sl-container .bh-sl-loc-list ul li{display:block;clear:left;float:left;margin:3% 4%;cursor:pointer;width:92%;border:1px solid #fff}.bh-sl-container .bh-sl-loc-list .list-label{float:left;margin:10px 0 0 6px;padding:4px;width:27px;text-align:center;background:#00192d;color:#fff;font-weight:700;border-radius:15px}.bh-sl-container .bh-sl-loc-list .list-details{float:left;margin-left:6px;width:80%}.bh-sl-container .bh-sl-loc-list .list-details .list-content{padding:10px}.bh-sl-container .bh-sl-loc-list .list-details .loc-dist{font-weight:700;font-style:italic;color:#8e8e8e}.bh-sl-container .bh-sl-loc-list .list-focus{border:1px solid rgba(0,82,147,.4);-moz-box-shadow:0 0 8px rgba(0,82,147,.4);-webkit-box-shadow:0 0 8px rgba(0,82,147,.4);box-shadow:0 0 8px rgba(0,100,180,.4);transition:border .2s linear 0s,box-shadow .2s linear 0s}.bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container{width:100%;height:20px;position:relative}.bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container .bh-sl-close-icon{top:0;right:6px}.bh-sl-container .bh-sl-loc-list .bh-sl-noresults-title{font-weight:700}.bh-sl-container .loc-name{font-size:15px;font-weight:700}.bh-sl-container .bh-sl-map{height:530px}.bh-sl-container .bh-sl-pagination-container{clear:both}.bh-sl-container .bh-sl-pagination-container ol{list-style-type:none;text-align:center;margin:0;padding:10px 0}.bh-sl-container .bh-sl-pagination-container ol li{display:inline-block;padding:10px;cursor:pointer;font:700 14px Arial,Helvetica,sans-serif;color:#005293}.bh-sl-container .bh-sl-pagination-container ol .bh-sl-current{color:#333;cursor:auto;text-decoration:none}.bh-sl-overlay{position:fixed;left:0;top:0;width:100%;height:100%;z-index:10000;background:url(../img/overlay-bg.png) repeat}.bh-sl-overlay .bh-sl-modal-window{position:absolute;left:50%;margin-left:-460px;margin-top:60px;width:920px;height:620px;z-index:10010;background:#fff;border-radius:10px;box-shadow:0 0 10px #656565}.bh-sl-overlay .bh-sl-modal-window .bh-sl-map-container{margin-top:50px}.bh-sl-overlay .bh-sl-modal-window .bh-sl-modal-content{float:left;padding:0 22px}.bh-sl-overlay .bh-sl-modal-window .bh-sl-close-icon{top:13px;right:22px}.bh-sl-close-icon{position:absolute;cursor:pointer;height:24px;width:24px}.bh-sl-close-icon:after,.bh-sl-close-icon:before{position:absolute;top:3px;right:3px;bottom:0;left:50%;background:#ccc;content:'';display:block;height:24px;margin:-3px 0 0 -1px;width:3px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bh-sl-close-icon:hover:after,.bh-sl-close-icon:hover:before{background:#b3b3b3}.bh-sl-close-icon:before{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)} -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/css/storelocator.css: -------------------------------------------------------------------------------- 1 | /* #page-header it just included for the examples */ 2 | #page-header { 3 | display: block; 4 | float: left; 5 | max-width: 800px; } 6 | #page-header .bh-sl-title { 7 | color: #797874; 8 | font: normal 20px/1.4 Arial, Helvetica, sans-serif; } 9 | @media (min-width: 1024px) { 10 | #page-header .bh-sl-title { 11 | font-size: 30px; } } 12 | 13 | /* Infowindow Roboto font override */ 14 | .gm-style div, .gm-style span, .gm-style label, .gm-style a { 15 | font-family: Arial, Helvetica, sans-serif; } 16 | 17 | /* InfoBubble font size */ 18 | .bh-sl-window { 19 | font-size: 13px; } 20 | 21 | .bh-sl-error { 22 | clear: both; 23 | color: #ae2118; 24 | float: left; 25 | font-weight: bold; 26 | padding: 10px 0; 27 | width: 100%; } 28 | 29 | /* Avoid image issues with Google Maps and CSS resets */ 30 | .bh-sl-map-container img { 31 | border-radius: 0 !important; 32 | box-shadow: none !important; 33 | max-height: none !important; 34 | max-width: none !important; } 35 | 36 | .bh-sl-container { 37 | box-sizing: border-box; 38 | color: #555; 39 | float: left; 40 | font: normal 14px/1.4 Arial, Helvetica, sans-serif; 41 | padding: 0 15px; 42 | width: 100%; 43 | /* Avoid issues with Google Maps and CSS frameworks */ } 44 | .bh-sl-container > * { 45 | box-sizing: content-box !important; } 46 | .bh-sl-container .bh-sl-form-container { 47 | clear: left; 48 | float: left; 49 | margin-top: 15px; 50 | width: 100%; } 51 | .bh-sl-container .form-input { 52 | float: left; 53 | margin-top: 3px; 54 | width: 100%; } 55 | @media (min-width: 768px) { 56 | .bh-sl-container .form-input { 57 | width: auto; } } 58 | .bh-sl-container .form-input label { 59 | display: block; 60 | font-weight: bold; 61 | width: 100%; } 62 | @media (min-width: 768px) { 63 | .bh-sl-container .form-input label { 64 | display: inline-block; 65 | width: auto; } } 66 | .bh-sl-container .form-input input, .bh-sl-container .form-input select { 67 | box-sizing: border-box; 68 | border: 1px solid #ccc; 69 | border-radius: 4px; 70 | font: normal 14px/1.4 Arial, Helvetica, sans-serif; 71 | margin: 15px 0; 72 | padding: 6px 12px; 73 | width: 100%; 74 | -webkit-border-radius: 4px; } 75 | @media (min-width: 768px) { 76 | .bh-sl-container .form-input input, .bh-sl-container .form-input select { 77 | width: auto; 78 | margin: 0 15px 0 10px; } } 79 | .bh-sl-container button { 80 | background: #00447a; 81 | border: none; 82 | border-radius: 4px; 83 | color: #fff; 84 | cursor: pointer; 85 | float: left; 86 | font: bold 14px/1.4 Arial, Helvetica, sans-serif; 87 | margin-top: 3px; 88 | padding: 6px 12px; 89 | white-space: nowrap; 90 | -webkit-border-radius: 4px; } 91 | .bh-sl-container .bh-sl-loading { 92 | background: url(../img/ajax-loader.gif) no-repeat; 93 | float: left; 94 | margin: 4px 0 0 10px; 95 | height: 16px; 96 | width: 16px; } 97 | .bh-sl-container .bh-sl-filters-container { 98 | clear: both; 99 | float: left; 100 | margin: 15px 0; 101 | width: 100%; } 102 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters { 103 | float: left; 104 | list-style: none; 105 | margin: 0 100px 0 0; 106 | padding: 0; } 107 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li { 108 | clear: left; 109 | display: block; 110 | float: left; 111 | margin: 5px 0; 112 | width: 100%; } 113 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li label { 114 | display: inline; 115 | vertical-align: text-bottom; } 116 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li input { 117 | display: block; 118 | float: left; 119 | margin-right: 8px; } 120 | .bh-sl-container .bh-sl-filters-container .bh-sl-filters li select { 121 | box-sizing: border-box; 122 | border: 1px solid #ccc; 123 | border-radius: 4px; 124 | font: normal 14px/1.4 Arial, Helvetica, sans-serif; 125 | padding: 6px 12px; 126 | -webkit-border-radius: 4px; } 127 | .bh-sl-container .bh-sl-map-container { 128 | clear: left; 129 | float: left; 130 | margin-top: 27px; 131 | width: 100%; } 132 | @media (min-width: 1024px) { 133 | .bh-sl-container .bh-sl-map-container { 134 | margin-bottom: 60px; } } 135 | .bh-sl-container .bh-sl-map-container a { 136 | color: #005293; 137 | text-decoration: none; } 138 | .bh-sl-container .bh-sl-map-container a:active, .bh-sl-container .bh-sl-map-container a:focus, .bh-sl-container .bh-sl-map-container a:hover { 139 | text-decoration: underline; } 140 | .bh-sl-container .bh-sl-loc-list { 141 | font-size: 13px; 142 | height: 530px; 143 | overflow-x: auto; 144 | width: 100%; } 145 | @media (min-width: 1024px) { 146 | .bh-sl-container .bh-sl-loc-list { 147 | width: 30%; } } 148 | .bh-sl-container .bh-sl-loc-list ul { 149 | display: block; 150 | clear: left; 151 | float: left; 152 | width: 100%; 153 | list-style: none; 154 | margin: 0; 155 | padding: 0; } 156 | .bh-sl-container .bh-sl-loc-list ul li { 157 | border: 1px solid #fff; 158 | /* Adding this to prevent moving li elements when adding the list-focus class*/ 159 | box-sizing: border-box; 160 | clear: left; 161 | cursor: pointer; 162 | display: block; 163 | float: left; 164 | width: 100%; } 165 | .bh-sl-container .bh-sl-loc-list .list-label { 166 | background: #00192d; 167 | border-radius: 15px; 168 | color: #fff; 169 | display: block; 170 | float: left; 171 | font-weight: bold; 172 | margin: 10px 0 0 15px; 173 | padding: 4px 7px; 174 | text-align: center; 175 | width: auto; 176 | min-width: 13px; } 177 | .bh-sl-container .bh-sl-loc-list .list-details { 178 | float: left; 179 | margin-left: 6px; 180 | width: 80%; } 181 | .bh-sl-container .bh-sl-loc-list .list-details .list-content { 182 | padding: 10px; } 183 | .bh-sl-container .bh-sl-loc-list .list-details .loc-dist { 184 | color: #8e8e8e; 185 | font-weight: bold; 186 | font-style: italic; } 187 | .bh-sl-container .bh-sl-loc-list .list-focus { 188 | border: 1px solid rgba(0, 82, 147, 0.4); 189 | transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s; } 190 | .bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container { 191 | height: 20px; 192 | position: relative; 193 | width: 100%; } 194 | .bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container .bh-sl-close-icon { 195 | right: 6px; 196 | top: 0; } 197 | .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel { 198 | margin: 0 2%; 199 | /* Avoid issues with table-layout */ } 200 | .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel table { 201 | table-layout: auto; 202 | width: 100%; } 203 | .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel table, .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel td { 204 | vertical-align: middle; 205 | border-collapse: separate; } 206 | .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel td { 207 | padding: 1px; } 208 | .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel .adp-placemark { 209 | margin: 10px 0; 210 | border: 1px solid #c0c0c0; } 211 | .bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel .adp-marker { 212 | padding: 3px; } 213 | .bh-sl-container .bh-sl-loc-list .bh-sl-noresults-title { 214 | font-weight: bold; 215 | margin: 15px; } 216 | .bh-sl-container .bh-sl-loc-list .bh-sl-noresults-desc { 217 | margin: 0 15px; } 218 | .bh-sl-container .loc-name { 219 | /* Picked up by both list and infowindows */ 220 | font-size: 15px; 221 | font-weight: bold; } 222 | .bh-sl-container .bh-sl-map { 223 | float: left; 224 | height: 530px; 225 | width: 100%; } 226 | @media (min-width: 1024px) { 227 | .bh-sl-container .bh-sl-map { 228 | width: 70%; } } 229 | .bh-sl-container .bh-sl-pagination-container { 230 | clear: both; } 231 | .bh-sl-container .bh-sl-pagination-container ol { 232 | list-style-type: none; 233 | margin: 0; 234 | padding: 10px 0; 235 | text-align: center; } 236 | .bh-sl-container .bh-sl-pagination-container ol li { 237 | color: #005293; 238 | cursor: pointer; 239 | display: inline-block; 240 | font: bold 14px Arial, Helvetica, sans-serif; 241 | padding: 10px; } 242 | .bh-sl-container .bh-sl-pagination-container ol .bh-sl-current { 243 | color: #555; 244 | cursor: auto; 245 | text-decoration: none; } 246 | 247 | /* Modal window */ 248 | .bh-sl-overlay { 249 | background: url(../img/overlay-bg.png) repeat; 250 | height: 100%; 251 | left: 0; 252 | position: fixed; 253 | top: 0; 254 | width: 100%; 255 | z-index: 10000; } 256 | .bh-sl-overlay .bh-sl-modal-window { 257 | background: #fff; 258 | border-radius: 10px; 259 | box-shadow: 0 0 10px #656565; 260 | position: absolute; 261 | left: 50%; 262 | margin-left: -460px; 263 | /* width divided by 2 */ 264 | margin-top: 60px; 265 | height: 620px; 266 | width: 920px; 267 | z-index: 10010; } 268 | .bh-sl-overlay .bh-sl-modal-window .bh-sl-map-container { 269 | margin-top: 50px; 270 | /* increase map container margin */ } 271 | .bh-sl-overlay .bh-sl-modal-window .bh-sl-modal-content { 272 | float: left; 273 | padding: 0 1%; 274 | /* there's already a margin on the top of the map-container div */ 275 | width: 98%; } 276 | .bh-sl-overlay .bh-sl-modal-window .bh-sl-close-icon { 277 | right: 22px; 278 | top: 13px; } 279 | 280 | .bh-sl-close-icon { 281 | cursor: pointer; 282 | height: 24px; 283 | position: absolute; 284 | width: 24px; } 285 | .bh-sl-close-icon:after, .bh-sl-close-icon:before { 286 | background: #ccc; 287 | content: ''; 288 | display: block; 289 | height: 24px; 290 | margin: -3px 0 0 -1px; 291 | position: absolute; 292 | bottom: 0; 293 | left: 50%; 294 | right: 3px; 295 | top: 3px; 296 | width: 3px; 297 | -webkit-transform: rotate(45deg); 298 | -moz-transform: rotate(45deg); 299 | -ms-transform: rotate(45deg); 300 | -o-transform: rotate(45deg); 301 | transform: rotate(45deg); } 302 | .bh-sl-close-icon:hover:after, .bh-sl-close-icon:hover:before { 303 | background: #b3b3b3; } 304 | .bh-sl-close-icon:before { 305 | -webkit-transform: rotate(-45deg); 306 | -moz-transform: rotate(-45deg); 307 | -ms-transform: rotate(-45deg); 308 | -o-transform: rotate(-45deg); 309 | transform: rotate(-45deg); } 310 | 311 | /*# sourceMappingURL=storelocator.css.map */ 312 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/css/storelocator.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAcA,oDAAoD;AACpD,YAAa;EACZ,OAAO,EAAE,KAAK;EACd,KAAK,EAAE,IAAI;EACX,SAAS,EAAE,KAAK;EAEhB,yBAAa;IACZ,KAAK,EAlBK,OAAO;IAmBjB,IAAI,EAAE,4CAA0B;IAEhC,0BAAuB;MAJxB,yBAAa;QAKX,SAAS,EAAE,IAAI;;AAKlB,qCAAqC;AAEpC,2DAAoB;EACnB,WAAW,EAvBD,4BAA4B;;AA2BxC,0BAA0B;AAC1B,aAAc;EACb,SAAS,EAAE,IAAI;;AAGhB,YAAa;EACZ,KAAK,EAAE,IAAI;EACX,KAAK,EAnCA,OAAO;EAoCZ,KAAK,EAAE,IAAI;EACX,WAAW,EAAE,IAAI;EACjB,OAAO,EAAE,MAAM;EACf,KAAK,EAAE,IAAI;;AAGZ,wDAAwD;AACxD,wBAAyB;EACxB,aAAa,EAAE,YAAY;EAC3B,UAAU,EAAE,eAAe;EAC3B,UAAU,EAAE,eAAe;EAC3B,SAAS,EAAE,eAAe;;AAG3B,gBAAiB;EAChB,UAAU,EAAE,UAAU;EACtB,KAAK,EAxDK,IAAI;EAyDd,KAAK,EAAE,IAAI;EACX,IAAI,EAAE,4CAA0B;EAChC,OAAO,EAAE,MAAM;EACf,KAAK,EAAE,IAAI;EAEX,sDAAsD;EACtD,oBAAI;IACH,UAAU,EAAE,sBAAsB;EAGnC,sCAAsB;IACrB,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;EAGZ,4BAAY;IACX,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,GAAG;IACf,KAAK,EAAE,IAAI;IAGX,yBAAkB;MANnB,4BAAY;QAOV,KAAK,EAAE,IAAI;IAGZ,kCAAM;MACL,OAAO,EAAE,KAAK;MACd,WAAW,EAAE,IAAI;MACjB,KAAK,EAAE,IAAI;MAGX,yBAAkB;QANnB,kCAAM;UAOJ,OAAO,EAAE,YAAY;UACrB,KAAK,EAAE,IAAI;IAIb,uEAAc;MACb,UAAU,EAAE,UAAU;MACtB,MAAM,EAAE,cAAe;MACvB,aAAa,EAAE,GAAG;MAClB,IAAI,EAAE,4CAA0B;MAChC,MAAM,EAAE,MAAM;MACd,OAAO,EAAE,QAAQ;MACjB,KAAK,EAAE,IAAI;MACX,qBAAqB,EAAE,GAAG;MAG1B,yBAAkB;QAXnB,uEAAc;UAYZ,KAAK,EAAE,IAAI;UACX,MAAM,EAAE,aAAa;EAKxB,uBAAO;IACN,UAAU,EAAE,OAAmB;IAC/B,MAAM,EAAE,IAAI;IACZ,aAAa,EAAE,GAAG;IAClB,KAAK,EA3HC,IAAI;IA4HV,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,IAAI;IACX,IAAI,EAAE,0CAAwB;IAC9B,UAAU,EAAE,GAAG;IACf,OAAO,EAAE,QAAQ;IACjB,WAAW,EAAE,MAAM;IACnB,qBAAqB,EAAE,GAAG;EAG3B,+BAAe;IACd,UAAU,EAAE,qCAAqC;IACjD,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;EAGZ,yCAAyB;IACxB,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,IAAI;IAEX,wDAAe;MACd,KAAK,EAAE,IAAI;MACX,UAAU,EAAE,IAAI;MAChB,MAAM,EAAE,WAAW;MACnB,OAAO,EAAE,CAAC;MAEV,2DAAG;QACF,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,IAAI;QAEX,iEAAM;UACL,OAAO,EAAE,MAAM;UACf,cAAc,EAAE,WAAW;QAG5B,iEAAM;UACL,OAAO,EAAE,KAAK;UACd,KAAK,EAAE,IAAI;UACX,YAAY,EAAE,GAAG;QAGlB,kEAAO;UACN,UAAU,EAAE,UAAU;UACtB,MAAM,EAAE,cAAe;UACvB,aAAa,EAAE,GAAG;UAClB,IAAI,EAAE,4CAA0B;UAChC,OAAO,EAAE,QAAQ;UACjB,qBAAqB,EAAE,GAAG;EAM9B,qCAAqB;IACpB,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;IAGX,0BAAuB;MAPxB,qCAAqB;QAQnB,aAAa,EAAE,IAAI;IAGpB,uCAAE;MACD,KAAK,EA3LD,OAAO;MA4LX,eAAe,EAAE,IAAI;MAErB,4IAEQ;QACP,eAAe,EAAE,SAAS;EAK7B,gCAAgB;IACf,SAAS,EAAE,IAAI;IACf,MAAM,EAAE,KAAK;IACb,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;IAGX,0BAAuB;MAPxB,gCAAgB;QAQd,KAAK,EAAE,GAAG;IAGX,mCAAG;MACF,OAAO,EAAE,KAAK;MACd,KAAK,EAAE,IAAI;MACX,KAAK,EAAE,IAAI;MACX,KAAK,EAAE,IAAI;MACX,UAAU,EAAE,IAAI;MAChB,MAAM,EAAE,CAAC;MACT,OAAO,EAAE,CAAC;MAEV,sCAAG;QACF,MAAM,EAAE,cAAgB;QAAE,+EAA+E;QACzG,UAAU,EAAE,UAAU;QACtB,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,IAAI;IAIb,4CAAY;MACX,UAAU,EAAE,OAAoB;MAChC,aAAa,EAAE,IAAI;MACnB,KAAK,EAhPA,IAAI;MAiPT,OAAO,EAAE,KAAK;MACd,KAAK,EAAE,IAAI;MACX,WAAW,EAAE,IAAI;MACjB,MAAM,EAAE,aAAa;MACrB,OAAO,EAAE,OAAO;MAChB,UAAU,EAAE,MAAM;MAClB,KAAK,EAAE,IAAI;MACX,SAAS,EAAE,IAAI;IAGhB,8CAAc;MACb,KAAK,EAAE,IAAI;MACX,WAAW,EAAE,GAAG;MAChB,KAAK,EAAE,GAAG;MAEV,4DAAc;QACb,OAAO,EAAE,IAAI;MAGd,wDAAU;QACT,KAAK,EAnQE,OAAO;QAoQd,WAAW,EAAE,IAAI;QACjB,UAAU,EAAE,MAAM;IAIpB,4CAAY;MACX,MAAM,EAAE,+BAA+B;MACvC,UAAU,EAAE,gDAAgD;IAG7D,kEAAkC;MACjC,MAAM,EAAE,IAAI;MACZ,QAAQ,EAAE,QAAQ;MAClB,KAAK,EAAE,IAAI;MAEX,oFAAkB;QACjB,KAAK,EAAE,GAAG;QACV,GAAG,EAAE,CAAC;IAIR,wDAAwB;MACvB,MAAM,EAAE,IAAI;MAEZ,oCAAoC;MACpC,8DAAM;QACL,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,IAAI;MAGZ,2HAAU;QACT,cAAc,EAAE,MAAM;QACtB,eAAe,EAAE,QAAQ;MAG1B,2DAAG;QACF,OAAO,EAAE,GAAG;MAGb,uEAAe;QACd,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,iBAAiB;MAG1B,oEAAY;QACX,OAAO,EAAE,GAAG;IAId,uDAAuB;MACtB,WAAW,EAAE,IAAI;MACjB,MAAM,EAAE,IAAI;IAGb,sDAAsB;MACrB,MAAM,EAAE,MAAM;EAIhB,0BAAU;IACT,4CAA4C;IAC5C,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;EAGlB,2BAAW;IACV,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,KAAK;IACb,KAAK,EAAE,IAAI;IAGX,0BAAuB;MANxB,2BAAW;QAOT,KAAK,EAAE,GAAG;EAIZ,4CAA4B;IAC3B,KAAK,EAAE,IAAI;IAEX,+CAAG;MACF,eAAe,EAAE,IAAI;MACrB,MAAM,EAAE,CAAC;MACT,OAAO,EAAE,MAAM;MACf,UAAU,EAAE,MAAM;MAElB,kDAAG;QACF,KAAK,EApVF,OAAO;QAqVV,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,sCAAoB;QAC1B,OAAO,EAAE,IAAI;MAGd,8DAAe;QACd,KAAK,EA/VE,IAAI;QAgWX,MAAM,EAAE,IAAI;QACZ,eAAe,EAAE,IAAI;;AAMzB,kBAAkB;AAClB,cAAe;EACd,UAAU,EAAE,iCAAiC;EAC7C,MAAM,EAAE,IAAI;EACZ,IAAI,EAAE,CAAC;EACP,QAAQ,EAAE,KAAK;EACf,GAAG,EAAE,CAAC;EACN,KAAK,EAAE,IAAI;EACX,OAAO,EAAE,KAAK;EAEd,kCAAoB;IACnB,UAAU,EAvXJ,IAAI;IAwXV,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,gBAAgB;IAC5B,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,GAAG;IACT,WAAW,EAAE,MAAM;IAAE,wBAAwB;IAC7C,UAAU,EAAE,IAAI;IAChB,MAAM,EAAE,KAAK;IACb,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,KAAK;IAEd,uDAAqB;MACpB,UAAU,EAAE,IAAI;MAAE,mCAAmC;IAGtD,uDAAqB;MACpB,KAAK,EAAE,IAAI;MACX,OAAO,EAAE,IAAI;MAAE,kEAAkE;MACjF,KAAK,EAAE,GAAG;IAGX,oDAAkB;MACjB,KAAK,EAAE,IAAI;MACX,GAAG,EAAE,IAAI;;AAKZ,iBAAkB;EACjB,MAAM,EAAE,OAAO;EACf,MAAM,EAAE,IAAI;EACZ,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,IAAI;EAEX,iDACS;IACR,UAAU,EA1ZL,IAAI;IA2ZT,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,aAAa;IACrB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;IACV,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;IACV,iBAAiB,EAAE,aAAa;IAChC,cAAc,EAAE,aAAa;IAC7B,aAAa,EAAE,aAAa;IAC5B,YAAY,EAAE,aAAa;IAC3B,SAAS,EAAE,aAAa;EAGzB,6DACe;IACd,UAAU,EAAE,OAAkB;EAG/B,wBAAS;IACR,iBAAiB,EAAE,cAAc;IACjC,cAAc,EAAE,cAAc;IAC9B,aAAa,EAAE,cAAc;IAC7B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,cAAc", 4 | "sources": ["../../../src/css/storelocator.scss"], 5 | "names": [], 6 | "file": "storelocator.css" 7 | } 8 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/css/storelocator.min.css: -------------------------------------------------------------------------------- 1 | #page-header{display:block;float:left;max-width:800px}#page-header .bh-sl-title{color:#797874;font:400 20px/1.4 Arial,Helvetica,sans-serif}@media (min-width:1024px){#page-header .bh-sl-title{font-size:30px}}.gm-style a,.gm-style div,.gm-style label,.gm-style span{font-family:Arial,Helvetica,sans-serif}.bh-sl-window{font-size:13px}.bh-sl-error{clear:both;color:#ae2118;float:left;font-weight:700;padding:10px 0;width:100%}.bh-sl-map-container img{border-radius:0!important;box-shadow:none!important;max-height:none!important;max-width:none!important}.bh-sl-container{box-sizing:border-box;color:#555;float:left;font:400 14px/1.4 Arial,Helvetica,sans-serif;padding:0 15px;width:100%}.bh-sl-container>*{box-sizing:content-box!important}.bh-sl-container .bh-sl-form-container{clear:left;float:left;margin-top:15px;width:100%}.bh-sl-container .form-input{float:left;margin-top:3px;width:100%}@media (min-width:768px){.bh-sl-container .form-input{width:auto}}.bh-sl-container .form-input label{display:block;font-weight:700;width:100%}@media (min-width:768px){.bh-sl-container .form-input label{display:inline-block;width:auto}}.bh-sl-container .form-input input,.bh-sl-container .form-input select{box-sizing:border-box;border:1px solid #ccc;border-radius:4px;font:400 14px/1.4 Arial,Helvetica,sans-serif;margin:15px 0;padding:6px 12px;width:100%;-webkit-border-radius:4px}@media (min-width:768px){.bh-sl-container .form-input input,.bh-sl-container .form-input select{width:auto;margin:0 15px 0 10px}}.bh-sl-container button{background:#00447a;border:none;border-radius:4px;color:#fff;cursor:pointer;float:left;font:700 14px/1.4 Arial,Helvetica,sans-serif;margin-top:3px;padding:6px 12px;white-space:nowrap;-webkit-border-radius:4px}.bh-sl-container .bh-sl-loading{background:url(../img/ajax-loader.gif) no-repeat;float:left;margin:4px 0 0 10px;height:16px;width:16px}.bh-sl-container .bh-sl-filters-container{clear:both;float:left;margin:15px 0;width:100%}.bh-sl-container .bh-sl-filters-container .bh-sl-filters{float:left;list-style:none;margin:0 100px 0 0;padding:0}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li{clear:left;display:block;float:left;margin:5px 0;width:100%}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li label{display:inline;vertical-align:text-bottom}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li input{display:block;float:left;margin-right:8px}.bh-sl-container .bh-sl-filters-container .bh-sl-filters li select{box-sizing:border-box;border:1px solid #ccc;border-radius:4px;font:400 14px/1.4 Arial,Helvetica,sans-serif;padding:6px 12px;-webkit-border-radius:4px}.bh-sl-container .bh-sl-map-container{clear:left;float:left;margin-top:27px;width:100%}@media (min-width:1024px){.bh-sl-container .bh-sl-map-container{margin-bottom:60px}}.bh-sl-container .bh-sl-map-container a{color:#005293;text-decoration:none}.bh-sl-container .bh-sl-map-container a:active,.bh-sl-container .bh-sl-map-container a:focus,.bh-sl-container .bh-sl-map-container a:hover{text-decoration:underline}.bh-sl-container .bh-sl-loc-list{font-size:13px;height:530px;overflow-x:auto;width:100%}@media (min-width:1024px){.bh-sl-container .bh-sl-loc-list{width:30%}}.bh-sl-container .bh-sl-loc-list ul{display:block;clear:left;float:left;width:100%;list-style:none;margin:0;padding:0}.bh-sl-container .bh-sl-loc-list ul li{border:1px solid #fff;box-sizing:border-box;clear:left;cursor:pointer;display:block;float:left;width:100%}.bh-sl-container .bh-sl-loc-list .list-label{background:#00192d;border-radius:15px;color:#fff;display:block;float:left;font-weight:700;margin:10px 0 0 15px;padding:4px 7px;text-align:center;width:auto;min-width:13px}.bh-sl-container .bh-sl-loc-list .list-details{float:left;margin-left:6px;width:80%}.bh-sl-container .bh-sl-loc-list .list-details .list-content{padding:10px}.bh-sl-container .bh-sl-loc-list .list-details .loc-dist{color:#8e8e8e;font-weight:700;font-style:italic}.bh-sl-container .bh-sl-loc-list .list-focus{border:1px solid rgba(0,82,147,.4);transition:border .2s linear 0s,box-shadow .2s linear 0s}.bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container{height:20px;position:relative;width:100%}.bh-sl-container .bh-sl-loc-list .bh-sl-close-directions-container .bh-sl-close-icon{right:6px;top:0}.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel{margin:0 2%}.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel table{table-layout:auto;width:100%}.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel table,.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel td{vertical-align:middle;border-collapse:separate}.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel td{padding:1px}.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel .adp-placemark{margin:10px 0;border:1px solid silver}.bh-sl-container .bh-sl-loc-list .bh-sl-directions-panel .adp-marker{padding:3px}.bh-sl-container .bh-sl-loc-list .bh-sl-noresults-title{font-weight:700;margin:15px}.bh-sl-container .bh-sl-loc-list .bh-sl-noresults-desc{margin:0 15px}.bh-sl-container .loc-name{font-size:15px;font-weight:700}.bh-sl-container .bh-sl-map{float:left;height:530px;width:100%}@media (min-width:1024px){.bh-sl-container .bh-sl-map{width:70%}}.bh-sl-container .bh-sl-pagination-container{clear:both}.bh-sl-container .bh-sl-pagination-container ol{list-style-type:none;margin:0;padding:10px 0;text-align:center}.bh-sl-container .bh-sl-pagination-container ol li{color:#005293;cursor:pointer;display:inline-block;font:700 14px Arial,Helvetica,sans-serif;padding:10px}.bh-sl-container .bh-sl-pagination-container ol .bh-sl-current{color:#555;cursor:auto;text-decoration:none}.bh-sl-overlay{background:url(../img/overlay-bg.png) repeat;height:100%;left:0;position:fixed;top:0;width:100%;z-index:10000}.bh-sl-overlay .bh-sl-modal-window{background:#fff;border-radius:10px;box-shadow:0 0 10px #656565;position:absolute;left:50%;margin-left:-460px;margin-top:60px;height:620px;width:920px;z-index:10010}.bh-sl-overlay .bh-sl-modal-window .bh-sl-map-container{margin-top:50px}.bh-sl-overlay .bh-sl-modal-window .bh-sl-modal-content{float:left;padding:0 1%;width:98%}.bh-sl-overlay .bh-sl-modal-window .bh-sl-close-icon{right:22px;top:13px}.bh-sl-close-icon{cursor:pointer;height:24px;position:absolute;width:24px}.bh-sl-close-icon:after,.bh-sl-close-icon:before{background:#ccc;content:'';display:block;height:24px;margin:-3px 0 0 -1px;position:absolute;bottom:0;left:50%;right:3px;top:3px;width:3px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bh-sl-close-icon:hover:after,.bh-sl-close-icon:hover:before{background:#b3b3b3}.bh-sl-close-icon:before{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)} -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/ajax-loader.gif -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/blue-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/blue-marker.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/blue-marker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/close-icon-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/close-icon-dark.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/close-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/close-icon.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/m1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/m1.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/m2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/m2.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/m3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/m3.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/m4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/m4.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/m5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/m5.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/overlay-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/overlay-bg.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/red-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynamic/silverstripe-locator/59311ec5899601110f9c0b4d96930aa9527d84e3/thirdparty/jquery-store-locator-plugin/assets/img/red-marker.png -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/img/red-marker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/js/geocode.min.js: -------------------------------------------------------------------------------- 1 | function GoogleGeocode(){var a=new google.maps.Geocoder;this.geocode=function(b,c){a.geocode({address:b},function(a,b){if(b===google.maps.GeocoderStatus.OK){var d={};d.latitude=a[0].geometry.location.lat(),d.longitude=a[0].geometry.location.lng(),c(d)}else alert("Geocode was not successful for the following reason: "+b),c(null)})}}$(function(){$("#bh-sl-user-location").on("submit",function(a){a.preventDefault();var b=$("form #bh-sl-address").val();""===b&&alert("The input box was blank.");var c=new GoogleGeocode,d=b;c.geocode(d,function(a){if(null!==a){var b=a.latitude,c=a.longitude;$("#geocode-result").append("Latitude: "+b+"
    Longitude: "+c+"

    ")}else alert("ERROR! Unable to geocode address")})})}); -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/js/libs/markerclusterer.min.js: -------------------------------------------------------------------------------- 1 | function MarkerClusterer(a,b,c){this.extend(MarkerClusterer,google.maps.OverlayView),this.map_=a,this.markers_=[],this.clusters_=[],this.sizes=[53,56,66,78,90],this.styles_=[],this.ready_=!1;var d=c||{};this.gridSize_=d.gridSize||60,this.minClusterSize_=d.minimumClusterSize||2,this.maxZoom_=d.maxZoom||null,this.styles_=d.styles||[],this.imagePath_=d.imagePath||this.MARKER_CLUSTER_IMAGE_PATH_,this.imageExtension_=d.imageExtension||this.MARKER_CLUSTER_IMAGE_EXTENSION_,this.zoomOnClick_=!0,void 0!=d.zoomOnClick&&(this.zoomOnClick_=d.zoomOnClick),this.averageCenter_=!1,void 0!=d.averageCenter&&(this.averageCenter_=d.averageCenter),this.setupStyles_(),this.setMap(a),this.prevZoom_=this.map_.getZoom();var e=this;google.maps.event.addListener(this.map_,"zoom_changed",function(){var a=e.map_.getZoom();e.prevZoom_!=a&&(e.prevZoom_=a,e.resetViewport())}),google.maps.event.addListener(this.map_,"idle",function(){e.redraw()}),b&&b.length&&this.addMarkers(b,!1)}function Cluster(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinClusterSize(),this.averageCenter_=a.isAverageCenter(),this.center_=null,this.markers_=[],this.bounds_=null,this.clusterIcon_=new ClusterIcon(this,a.getStyles(),a.getGridSize())}function ClusterIcon(a,b,c){a.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView),this.styles_=b,this.padding_=c||0,this.cluster_=a,this.center_=null,this.map_=a.getMap(),this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(this.map_)}MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_="../images/m",MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_="png",MarkerClusterer.prototype.extend=function(a,b){return function(a){for(var b in a.prototype)this.prototype[b]=a.prototype[b];return this}.apply(a,[b])},MarkerClusterer.prototype.onAdd=function(){this.setReady_(!0)},MarkerClusterer.prototype.draw=function(){},MarkerClusterer.prototype.setupStyles_=function(){if(!this.styles_.length)for(var a,b=0;a=this.sizes[b];b++)this.styles_.push({url:this.imagePath_+(b+1)+"."+this.imageExtension_,height:a,width:a})},MarkerClusterer.prototype.fitMapToMarkers=function(){for(var a,b=this.getMarkers(),c=new google.maps.LatLngBounds,d=0;a=b[d];d++)c.extend(a.getPosition());this.map_.fitBounds(c)},MarkerClusterer.prototype.setStyles=function(a){this.styles_=a},MarkerClusterer.prototype.getStyles=function(){return this.styles_},MarkerClusterer.prototype.isZoomOnClick=function(){return this.zoomOnClick_},MarkerClusterer.prototype.isAverageCenter=function(){return this.averageCenter_},MarkerClusterer.prototype.getMarkers=function(){return this.markers_},MarkerClusterer.prototype.getTotalMarkers=function(){return this.markers_.length},MarkerClusterer.prototype.setMaxZoom=function(a){this.maxZoom_=a},MarkerClusterer.prototype.getMaxZoom=function(){return this.maxZoom_},MarkerClusterer.prototype.calculator_=function(a,b){for(var c=0,d=a.length,e=d;0!==e;)e=parseInt(e/10,10),c++;return c=Math.min(c,b),{text:d,index:c}},MarkerClusterer.prototype.setCalculator=function(a){this.calculator_=a},MarkerClusterer.prototype.getCalculator=function(){return this.calculator_},MarkerClusterer.prototype.addMarkers=function(a,b){for(var c,d=0;c=a[d];d++)this.pushMarkerTo_(c);b||this.redraw()},MarkerClusterer.prototype.pushMarkerTo_=function(a){if(a.isAdded=!1,a.draggable){var b=this;google.maps.event.addListener(a,"dragend",function(){a.isAdded=!1,b.repaint()})}this.markers_.push(a)},MarkerClusterer.prototype.addMarker=function(a,b){this.pushMarkerTo_(a),b||this.redraw()},MarkerClusterer.prototype.removeMarker_=function(a){var b=-1;if(this.markers_.indexOf)b=this.markers_.indexOf(a);else for(var c,d=0;c=this.markers_[d];d++)if(c==a){b=d;break}return-1==b?!1:(a.setMap(null),this.markers_.splice(b,1),!0)},MarkerClusterer.prototype.removeMarker=function(a,b){var c=this.removeMarker_(a);return!b&&c?(this.resetViewport(),this.redraw(),!0):!1},MarkerClusterer.prototype.removeMarkers=function(a,b){for(var c,d=!1,e=0;c=a[e];e++){var f=this.removeMarker_(c);d=d||f}return!b&&d?(this.resetViewport(),this.redraw(),!0):void 0},MarkerClusterer.prototype.setReady_=function(a){this.ready_||(this.ready_=a,this.createClusters_())},MarkerClusterer.prototype.getTotalClusters=function(){return this.clusters_.length},MarkerClusterer.prototype.getMap=function(){return this.map_},MarkerClusterer.prototype.setMap=function(a){this.map_=a},MarkerClusterer.prototype.getGridSize=function(){return this.gridSize_},MarkerClusterer.prototype.setGridSize=function(a){this.gridSize_=a},MarkerClusterer.prototype.getMinClusterSize=function(){return this.minClusterSize_},MarkerClusterer.prototype.setMinClusterSize=function(a){this.minClusterSize_=a},MarkerClusterer.prototype.getExtendedBounds=function(a){var b=this.getProjection(),c=new google.maps.LatLng(a.getNorthEast().lat(),a.getNorthEast().lng()),d=new google.maps.LatLng(a.getSouthWest().lat(),a.getSouthWest().lng()),e=b.fromLatLngToDivPixel(c);e.x+=this.gridSize_,e.y-=this.gridSize_;var f=b.fromLatLngToDivPixel(d);f.x-=this.gridSize_,f.y+=this.gridSize_;var g=b.fromDivPixelToLatLng(e),h=b.fromDivPixelToLatLng(f);return a.extend(g),a.extend(h),a},MarkerClusterer.prototype.isMarkerInBounds_=function(a,b){return b.contains(a.getPosition())},MarkerClusterer.prototype.clearMarkers=function(){this.resetViewport(!0),this.markers_=[]},MarkerClusterer.prototype.resetViewport=function(a){for(var b,c=0;b=this.clusters_[c];c++)b.remove();for(var d,c=0;d=this.markers_[c];c++)d.isAdded=!1,a&&d.setMap(null);this.clusters_=[]},MarkerClusterer.prototype.repaint=function(){var a=this.clusters_.slice();this.clusters_.length=0,this.resetViewport(),this.redraw(),window.setTimeout(function(){for(var b,c=0;b=a[c];c++)b.remove()},0)},MarkerClusterer.prototype.redraw=function(){this.createClusters_()},MarkerClusterer.prototype.distanceBetweenPoints_=function(a,b){if(!a||!b)return 0;var c=6371,d=(b.lat()-a.lat())*Math.PI/180,e=(b.lng()-a.lng())*Math.PI/180,f=Math.sin(d/2)*Math.sin(d/2)+Math.cos(a.lat()*Math.PI/180)*Math.cos(b.lat()*Math.PI/180)*Math.sin(e/2)*Math.sin(e/2),g=2*Math.atan2(Math.sqrt(f),Math.sqrt(1-f)),h=c*g;return h},MarkerClusterer.prototype.addToClosestCluster_=function(a){for(var b,c=4e4,d=null,e=(a.getPosition(),0);b=this.clusters_[e];e++){var f=b.getCenter();if(f){var g=this.distanceBetweenPoints_(f,a.getPosition());c>g&&(c=g,d=b)}}if(d&&d.isMarkerInClusterBounds(a))d.addMarker(a);else{var b=new Cluster(this);b.addMarker(a),this.clusters_.push(b)}},MarkerClusterer.prototype.createClusters_=function(){if(this.ready_)for(var a,b=new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),this.map_.getBounds().getNorthEast()),c=this.getExtendedBounds(b),d=0;a=this.markers_[d];d++)!a.isAdded&&this.isMarkerInBounds_(a,c)&&this.addToClosestCluster_(a)},Cluster.prototype.isMarkerAlreadyAdded=function(a){if(this.markers_.indexOf)return-1!=this.markers_.indexOf(a);for(var b,c=0;b=this.markers_[c];c++)if(b==a)return!0;return!1},Cluster.prototype.addMarker=function(a){if(this.isMarkerAlreadyAdded(a))return!1;if(this.center_){if(this.averageCenter_){var b=this.markers_.length+1,c=(this.center_.lat()*(b-1)+a.getPosition().lat())/b,d=(this.center_.lng()*(b-1)+a.getPosition().lng())/b;this.center_=new google.maps.LatLng(c,d),this.calculateBounds_()}}else this.center_=a.getPosition(),this.calculateBounds_();a.isAdded=!0,this.markers_.push(a);var e=this.markers_.length;if(ef;f++)this.markers_[f].setMap(null);return e>=this.minClusterSize_&&a.setMap(null),this.updateIcon(),!0},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){for(var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers(),d=0;a=c[d];d++)b.extend(a.getPosition());return b},Cluster.prototype.remove=function(){this.clusterIcon_.remove(),this.markers_.length=0,delete this.markers_},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.updateIcon=function(){var a=this.map_.getZoom(),b=this.markerClusterer_.getMaxZoom();if(b&&a>b)for(var c,d=0;c=this.markers_[d];d++)c.setMap(this.map_);else{if(this.markers_.length0&&this.anchor_[0]0&&this.anchor_[1]{{name}} 3 |
    {{address}}
    4 |
    {{address2}}
    5 |
    {{city}}{{#if city}},{{/if}} {{state}} {{postal}}
    6 |
    {{hours1}}
    7 |
    {{hours2}}
    8 |
    {{hours3}}
    9 |
    {{phone}}
    10 | 11 | {{/location}} -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/js/plugins/storeLocator/templates/kml-infowindow-description.html: -------------------------------------------------------------------------------- 1 | {{#location}} 2 |
    {{name}}
    3 |
    {{{description}}}
    4 | {{/location}} -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/js/plugins/storeLocator/templates/kml-location-list-description.html: -------------------------------------------------------------------------------- 1 | {{#location}} 2 |
  • 3 |
    {{marker}}
    4 |
    5 |
    6 |
    {{name}}
    7 |
    {{{description}}}
    8 |
    9 |
    10 |
  • 11 | {{/location}} -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/assets/js/plugins/storeLocator/templates/location-list-description.html: -------------------------------------------------------------------------------- 1 | {{#location}} 2 |
  • 3 |
    {{marker}}
    4 |
    5 |
    6 |
    {{name}}
    7 |
    {{address}}
    8 |
    {{address2}}
    9 |
    {{city}}{{#if city}},{{/if}} {{state}} {{postal}}
    10 |
    {{phone}}
    11 | 12 | {{#if distance}} 13 |
    {{distance}} {{length}}
    14 | 15 | {{/if}} 16 |
    17 |
    18 |
  • 19 | {{/location}} -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/autocomplete-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Autocomplete Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
    12 | 17 | 18 |
    19 |
    20 |
    21 | 22 | 23 |
    24 | 25 | 26 |
    27 |
    28 | 29 |
    30 |
    31 |
    32 |
      33 |
      34 |
      35 |
      36 | 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/autogeocode-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Auto geocoding 5 | 6 | 7 | 8 | 9 | 10 | 11 |
      12 | 17 | 18 |
      19 |
      20 |
      21 | 22 | 23 |
      24 | 25 | 26 |
      27 |
      28 | 29 |
      30 |
      31 |
      32 |
        33 |
        34 |
        35 |
        36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/bootstrap-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
        15 |
        16 |
        17 |

        Using Chipotle as an Example

        18 |

        I used locations around Minneapolis and the southwest suburbs. So, for example, Edina, Plymouth, Eden 19 | Prarie, etc. would be good for testing the functionality. You can use just the city as the address - ex: 20 | Edina, MN.

        21 | 22 |
        23 |
        24 |
        25 | 26 | 27 |
        28 | 29 | 30 |
        31 |
        32 |
        33 |
        34 | 35 |
        36 |
        37 |
        38 |
        39 |
        40 |
          41 |
          42 |
          43 |
          44 |
          45 |
          46 | 47 | 48 | 49 | 50 | 51 | 52 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/categories-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Categories 5 | 6 | 7 | 8 | 9 | 10 | 11 |
          12 | 17 | 18 |
          19 |
          20 |
          21 | 22 | 23 | 29 |
          30 | 31 | 32 | 33 |
          34 |
            35 |
          • Categories

          • 36 |
          • 37 | 40 |
          • 41 |
          • 42 | 45 |
          • 46 |
          • 47 | 50 |
          • 51 |
          • 52 | 55 |
          • 56 |
          • 57 | 60 |
          • 61 |
          62 | 63 |
            64 |
          • Features

          • 65 |
          • 66 | 69 |
          • 70 |
          • 71 | 74 |
          • 75 |
          • 76 | 79 |
          • 80 |
          81 | 82 |
            83 |
          • City

          • 84 |
          • 85 | 92 |
          • 93 |
          94 | 95 |
            96 |
          • Zip

          • 97 |
          • 98 | 55416 99 |
          • 100 |
          • 101 | 55343 102 |
          • 103 |
          • 104 | 55402 105 |
          • 106 |
          • 107 | 55317 108 |
          • 109 |
          110 |
          111 |
          112 | 113 |
          114 | 115 |
          116 |
          117 |
          118 |
            119 |
            120 |
            121 |
            122 | 123 | 124 | 125 | 126 | 127 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/category-markers-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Category Markers 5 | 6 | 7 | 8 | 9 | 10 | 11 |
            12 | 18 | 19 |
            20 |
            21 |
            22 | 23 | 24 |
            25 | 26 | 27 |
            28 |
            29 | 30 |
            31 |
            32 |
            33 |
              34 |
              35 |
              36 |
              37 | 38 | 39 | 40 | 41 | 42 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/cluster-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cluster Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
              12 | 17 | 18 |
              19 |
              20 |
              21 | 22 | 23 |
              24 | 25 | 26 |
              27 |
              28 | 29 |
              30 |
              31 |
              32 |
                33 |
                34 |
                35 |
                36 | 37 | 38 | 39 | 40 | 41 | 42 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/data/locations.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","name":"Chipotle Minneapolis","lat":"44.947464","lng":"-93.320826","address":"3040 Excelsior Blvd","address2":"","city":"Minneapolis","state":"MN","postal":"55416","phone":"612-922-6662","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"2","name":"Chipotle St. Louis Park","lat":"44.930810","lng":"-93.347877","address":"5480 Excelsior Blvd.","address2":"","city":"St. Louis Park","state":"MN","postal":"55416","phone":"952-922-1970","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"3","name":"Chipotle Minneapolis","lat":"44.9553438","lng":"-93.29719699999998","address":"2600 Hennepin Ave.","address2":"","city":"Minneapolis","state":"MN","postal":"55404","phone":"612-377-6035","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"4","name":"Chipotle Golden Valley","lat":"44.983935","lng":"-93.380542","address":"515 Winnetka Ave. N","address2":"","city":"Golden Valley","state":"MN","postal":"55427","phone":"763-544-2530","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"5","name":"Chipotle Hopkins","lat":"44.924363","lng":"-93.410158","address":"786 Mainstreet","address2":"","city":"Hopkins","state":"MN","postal":"55343","phone":"952-935-0044","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"6","name":"Chipotle Minneapolis","lat":"44.973557","lng":"-93.275111","address":"1040 Nicollet Ave","address2":"","city":"Minneapolis","state":"MN","postal":"55403","phone":"612-659-7955","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"7","name":"Chipotle Minneapolis","lat":"44.97774","lng":"-93.270909","address":"50 South 6th","address2":"","city":"Minneapolis","state":"MN","postal":"55402","phone":"612-333-0434","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"8","name":"Chipotle Edina","lat":"44.879826","lng":"-93.321280","address":"6801 York Avenue South","address2":"","city":"Edina","state":"MN","postal":"55435","phone":"952-926-6651","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"9","name":"Chipotle Minnetonka","lat":"44.970495","lng":"-93.437430","address":"12509 Wayzata Blvd","address2":"","city":"Minnetonka","state":"MN","postal":"55305","phone":"952-252-4900","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"10","name":"Chipotle Minneapolis","lat":"44.972808","lng":"-93.247153","address":"229 Cedar Ave S","address2":"","city":"Minneapolis","state":"MN","postal":"55454","phone":"612-659-7830","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"11","name":"Chipotle Minneapolis","lat":"44.987687","lng":"-93.257581","address":"225 Hennepin Ave E","address2":"","city":"Minneapolis","state":"MN","postal":"55414","phone":"612-331-6330","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"12","name":"Chipotle Minneapolis","lat":"44.973665","lng":"-93.227023","address":"800 Washington Ave SE","address2":"","city":"Minneapolis","state":"MN","postal":"55414","phone":"612-378-7078","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"13","name":"Chipotle Bloomington","lat":"44.8458631","lng":"-93.2860161","address":"322 South Ave","address2":"","city":"Bloomington","state":"MN","postal":"55425","phone":"952-252-3800","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"14","name":"Chipotle Wayzata","lat":"44.9716626","lng":"-93.4967757","address":"1313 Wayzata Blvd","address2":"","city":"Wayzata","state":"MN","postal":"55391","phone":"952-473-7100","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"15","name":"Chipotle Eden Prairie","lat":"44.859761","lng":"-93.436379","address":"13250 Technology Dr","address2":"","city":"Eden Prairie","state":"MN","postal":"55344","phone":"952-934-5955","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"16","name":"Chipotle Plymouth","lat":"45.019846","lng":"-93.481832","address":"3425 Vicksburg Lane N","address2":"","city":"Plymouth","state":"MN","postal":"55447","phone":"763-519-0063","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"17","name":"Chipotle Roseville","lat":"44.998965","lng":"-93.194622","address":"860 Rosedale Center Plaza","address2":"","city":"Roseville","state":"MN","postal":"55113","phone":"651-633-2300","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"18","name":"Chipotle St. Paul","lat":"44.939865","lng":"-93.136768","address":"867 Grand Ave","address2":"","city":"St. Paul","state":"MN","postal":"55105","phone":"651-602-0560","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"19","name":"Chipotle Chanhassen","lat":"44.858736","lng":"-93.533661","address":"560 W 79th","address2":"","city":"Chanhassen","state":"MN","postal":"55317","phone":"952-294-0301","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"20","name":"Chipotle St. Paul","lat":"44.945127","lng":"-93.095368","address":"29 5th St West","address2":"","city":"St. Paul","state":"MN","postal":"55102","phone":"651-291-5411","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""}] -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/data/locations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/default-location-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Default Location 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                12 | 17 | 18 |
                19 |
                20 |
                21 | 22 | 23 |
                24 | 25 | 26 |
                27 |
                28 | 29 |
                30 |
                31 |
                32 |
                  33 |
                  34 |
                  35 |
                  36 | 37 | 38 | 39 | 40 | 41 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/fullmapstartblank-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - fullMapStartBlank 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                  12 | 17 | 18 |
                  19 |
                  20 |
                  21 | 22 | 23 |
                  24 | 25 | 26 |
                  27 |
                  28 | 29 |
                  30 |
                  31 |
                  32 |
                    33 |
                    34 |
                    35 |
                    36 | 37 | 38 | 39 | 40 | 41 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/geocode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Geocode 5 | 6 | 7 | 16 | 17 | 18 | 19 | 20 |
                    21 | 25 |
                    26 |
                    27 |
                    28 | 29 | 30 |
                    31 | 32 | 33 |
                    34 |
                    35 | 36 |
                    37 |
                    38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                    12 | 17 | 18 |
                    19 |
                    20 |
                    21 | 22 | 23 |
                    24 | 25 | 26 |
                    27 |
                    28 | 29 |
                    30 |
                    31 |
                    32 |
                      33 |
                      34 |
                      35 |
                      36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/infobubble-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Infobubble Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                      12 | 17 | 18 |
                      19 |
                      20 |
                      21 | 22 | 23 |
                      24 | 25 | 26 |
                      27 |
                      28 | 29 |
                      30 |
                      31 |
                      32 |
                        33 |
                        34 |
                        35 |
                        36 | 37 | 38 | 39 | 40 | 41 | 42 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/inline-directions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Inline Directions 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                        12 | 17 | 18 |
                        19 |
                        20 |
                        21 | 22 | 23 |
                        24 | 25 | 26 |
                        27 |
                        28 | 29 |
                        30 |
                        31 |
                        32 |
                          33 |
                          34 |
                          35 |
                          36 | 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/json-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - JSON Data 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                          12 | 17 | 18 |
                          19 |
                          20 |
                          21 | 22 | 23 |
                          24 | 25 | 26 |
                          27 |
                          28 | 29 |
                          30 |
                          31 |
                          32 |
                            33 |
                            34 |
                            35 |
                            36 | 37 | 38 | 39 | 40 | 41 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/kml-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - KML Data 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                            12 | 17 | 18 |
                            19 |
                            20 |
                            21 | 22 | 23 |
                            24 | 25 | 26 |
                            27 |
                            28 | 29 |
                            30 |
                            31 |
                            32 |
                              33 |
                              34 |
                              35 |
                              36 | 37 | 38 | 39 | 40 | 41 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/maxdistance-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Maximum Distance 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                              12 | 17 | 18 |
                              19 |
                              20 |
                              21 | 22 | 23 | 29 |
                              30 | 31 | 32 |
                              33 |
                              34 | 35 |
                              36 |
                              37 |
                              38 |
                                39 |
                                40 |
                                41 |
                                42 | 43 | 44 | 45 | 46 | 47 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/modal-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Modal Window 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                                12 | 17 | 18 |
                                19 |
                                20 |
                                21 | 22 | 23 |
                                24 | 25 | 26 |
                                27 |
                                28 | 29 |
                                30 |
                                31 |
                                32 |
                                  33 |
                                  34 |
                                  35 |
                                  36 | 37 | 38 | 39 | 40 | 41 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/namesearch-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NameSearch Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                                  12 | 18 | 19 |
                                  20 |
                                  21 |
                                  22 | 23 | 24 | 25 | 26 | 27 |
                                  28 | 29 | 30 |
                                  31 |
                                  32 | 33 |
                                  34 |
                                  35 |
                                  36 |
                                    37 |
                                    38 |
                                    39 |
                                    40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/noform-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - No Form for ASP.net 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                                    12 | 17 | 18 |
                                    19 |
                                    20 | 21 | 22 |
                                    23 | 24 |
                                    25 | 26 |
                                    27 |
                                    28 |
                                    29 |
                                      30 |
                                      31 |
                                      32 |
                                      33 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/pagination-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example - Pagination 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                                      12 | 17 | 18 |
                                      19 |
                                      20 |
                                      21 | 22 | 23 |
                                      24 | 25 | 26 |
                                      27 |
                                      28 | 29 |
                                      30 |
                                      31 |
                                      32 |
                                        33 |
                                        34 |
                                        35 |
                                          36 |
                                          37 |
                                          38 |
                                          39 | 40 | 41 | 42 | 43 | 44 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/query-string-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                                          12 | 17 | 18 |
                                          19 |
                                          20 |
                                          21 | 22 | 23 |
                                          24 | 25 | 26 |
                                          27 |
                                          28 |
                                          29 | 30 | 31 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/query-string-example/submit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
                                          12 | 17 | 18 |
                                          19 |
                                          20 |
                                          21 | 22 | 23 |
                                          24 | 25 | 26 |
                                          27 |
                                          28 | 29 |
                                          30 |
                                          31 |
                                          32 |
                                            33 |
                                            34 |
                                            35 |
                                            36 | 37 | 38 | 39 | 40 | 41 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /thirdparty/jquery-store-locator-plugin/rawdata-example.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | '; 27 | 28 | ?> 29 | 30 | 31 | 32 | 33 | Map Example - Raw Data 34 | 35 | 36 | 37 | 38 | 39 | 40 |
                                            41 | 46 | 47 |
                                            48 |
                                            49 |
                                            50 | 51 | 52 |
                                            53 | 54 | 55 |
                                            56 |
                                            57 | 58 |
                                            59 |
                                            60 |
                                            61 |
                                              62 |
                                              63 |
                                              64 |
                                              65 | 66 | 67 | 68 | 69 | 70 | 77 | 78 | 79 | --------------------------------------------------------------------------------