WP Store Locator - Version 1.0

Version Description

  • Initial release
Download this release

Release Info

Developer tijmensmit
Plugin Icon 128x128 WP Store Locator
Version 1.0
Comparing to
See all releases

Version 1.0

Files changed (51) hide show
  1. admin/class-admin.php +1156 -0
  2. admin/class-store-overview.php +411 -0
  3. admin/css/style-3.8.css +29 -0
  4. admin/css/style.css +229 -0
  5. admin/font/fontello.eot +0 -0
  6. admin/font/fontello.svg +12 -0
  7. admin/font/fontello.ttf +0 -0
  8. admin/font/fontello.woff +0 -0
  9. admin/js/ajax-queue.js +54 -0
  10. admin/js/retina-1.1.0.js +152 -0
  11. admin/js/wpsl-admin.js +375 -0
  12. admin/templates/add-store.php +109 -0
  13. admin/templates/edit-store.php +135 -0
  14. admin/templates/faq.php +10 -0
  15. admin/templates/map-settings.php +234 -0
  16. admin/templates/stores-overview.php +24 -0
  17. css/styles.css +598 -0
  18. frontend/class-frontend.php +179 -0
  19. frontend/templates/default.php +45 -0
  20. frontend/wpsl-ajax-functions.php +114 -0
  21. img/ajax-loader.gif +0 -0
  22. img/markers/blue.png +0 -0
  23. img/markers/blue@2x.png +0 -0
  24. img/markers/dark-blue.png +0 -0
  25. img/markers/dark-blue@2x.png +0 -0
  26. img/markers/dark-green.png +0 -0
  27. img/markers/dark-green@2x.png +0 -0
  28. img/markers/dark-orange.png +0 -0
  29. img/markers/dark-orange@2x.png +0 -0
  30. img/markers/green.png +0 -0
  31. img/markers/green@2x.png +0 -0
  32. img/markers/orange.png +0 -0
  33. img/markers/orange@2x.png +0 -0
  34. img/markers/pink.png +0 -0
  35. img/markers/pink@2x.png +0 -0
  36. img/markers/purple.png +0 -0
  37. img/markers/purple@2x.png +0 -0
  38. img/markers/red.png +0 -0
  39. img/markers/red@2x.png +0 -0
  40. img/store-locator-icon.png +0 -0
  41. js/jquery.easydropdown.js +469 -0
  42. js/jquery.easydropdown.min.js +24 -0
  43. js/wpsl-gmap.js +555 -0
  44. readme.txt +59 -0
  45. screenshot-1.jpg +0 -0
  46. screenshot-2.jpg +0 -0
  47. screenshot-3.jpg +0 -0
  48. screenshot-4.jpg +0 -0
  49. screenshot-5.jpg +0 -0
  50. uninstall.php +19 -0
  51. wp-store-locator.php +234 -0
admin/class-admin.php ADDED
@@ -0,0 +1,1156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin class
4
+ *
5
+ * @package WP_Store_locator
6
+ * @subpackage Classes/Admin
7
+ * @copyright Copyright (c) 2013, Tijmen Smit
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) exit;
13
+
14
+ if ( !class_exists( 'WPSL_Admin' ) ) {
15
+ /**
16
+ * Handle the backend of the store locator
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class WPSL_Admin extends WP_Store_locator {
21
+
22
+ /**
23
+ * Holds the store data
24
+ *
25
+ * @var array
26
+ * @since 1.0
27
+ */
28
+ public $store_data;
29
+
30
+ /**
31
+ * Class constructor
32
+ */
33
+ function __construct() {
34
+ add_action( 'init', array( $this, 'output_buffer' ) );
35
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
36
+ add_action( 'wp_loaded', array( $this, 'init' ) );
37
+ add_action( 'wp_ajax_delete_store', array( $this, 'delete_store_ajax' ) );
38
+
39
+ $this->settings = $this->get_settings();
40
+ }
41
+
42
+ public function output_buffer() {
43
+ ob_start();
44
+ }
45
+
46
+ /**
47
+ * Register a callback function for the settings page
48
+ *
49
+ * @since 1.0
50
+ * @return void
51
+ */
52
+ public function admin_init() {
53
+ register_setting( 'wpsl_settings', 'wpsl_settings', array( $this, 'sanitize_settings' ) );
54
+ }
55
+
56
+ /**
57
+ * Add the admin menu and enqueue the admin scripts
58
+ *
59
+ * @since 1.0
60
+ * @return void
61
+ */
62
+ public function init() {
63
+ add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
64
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
65
+ }
66
+
67
+ /**
68
+ * Add the menu pages
69
+ *
70
+ * @since 1.0
71
+ * @return void
72
+ */
73
+ public function create_admin_menu() {
74
+ add_menu_page( 'Store Locator', __( 'Store Locator', 'wpsl' ), __( 'manage_options', 'wpsl' ), 'wpsl_store_editor', array( $this, 'manage_stores' ), plugins_url( 'img/store-locator-icon.png', dirname( __FILE__ ) ) );
75
+ add_submenu_page( 'wpsl_store_editor', __( 'Manage Stores', 'wpsl' ), __( 'Manage Stores', 'wpsl' ), 'manage_options', 'wpsl_store_editor', array( $this, 'manage_stores' ) );
76
+ add_submenu_page( 'wpsl_store_editor', __( 'Add Store', 'wpsl' ), __( 'Add Store', 'wpsl' ), 'manage_options', 'wpsl_add_store', array( $this, 'add_store' ) );
77
+ add_submenu_page( 'wpsl_store_editor', __( 'Settings', 'wpsl' ), __( 'Settings', 'wpsl' ), 'manage_options', 'wpsl_settings', array( $this, 'show_settings' ) );
78
+ add_submenu_page( 'wpsl_store_editor', __( 'FAQ', 'wpsl' ), __( 'FAQ', 'wpsl' ), 'manage_options', 'wpsl_faq', array( $this, 'show_faq' ) );
79
+ }
80
+
81
+ /**
82
+ * Load the add store template
83
+ *
84
+ * @since 1.0
85
+ * @return void
86
+ */
87
+ public function add_store() {
88
+ $this->store_actions();
89
+ require_once( WPSL_PLUGIN_DIR . 'admin/templates/add-store.php' );
90
+ }
91
+
92
+ /**
93
+ * If a store form is submitted, process the store data
94
+ *
95
+ * @since 1.0
96
+ * @return void
97
+ */
98
+ public function store_actions() {
99
+ if ( isset( $_REQUEST['wpsl_actions'] ) ) {
100
+ $this->handle_store_data();
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Handle the different actions for the store.
106
+ *
107
+ * Based on the action value, either show the store overview list or the edit store template
108
+ *
109
+ * @since 1.0
110
+ * @return void
111
+ */
112
+ public function manage_stores() {
113
+
114
+ $this->store_actions();
115
+
116
+ /* Check which store template to show */
117
+ switch ( $_GET['action'] ) {
118
+ case 'edit_store':
119
+ require_once( WPSL_PLUGIN_DIR . 'admin/templates/edit-store.php' );
120
+ break;
121
+ default:
122
+ require_once( WPSL_PLUGIN_DIR . 'admin/templates/stores-overview.php' );
123
+ break;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Process new store data
129
+ *
130
+ * @since 1.0
131
+ * @return void
132
+ */
133
+ public function handle_store_data() {
134
+
135
+ global $wpdb;
136
+
137
+ if ( !current_user_can( 'manage_options' ) )
138
+ die( '-1' );
139
+
140
+ check_admin_referer( 'wpsl_' . $_POST['wpsl_actions'] );
141
+
142
+ $this->store_data = $this->validate_store_data();
143
+
144
+ if ( $this->store_data ) {
145
+ $latlng = $this->validate_latlng( $this->store_data['lat'], $this->store_data['lng'] );
146
+
147
+ /* If we don't have a valid latlng value, we geocode the supplied address to get one */
148
+ if ( !$latlng ) {
149
+ $reponse = $this->geocode_location();
150
+ $this->store_data['country'] = $reponse['country']['long_name'];
151
+ $this->store_data['country-iso'] = $reponse['country']['short_name'];
152
+ $this->store_data['latlng'] = $reponse['latlng'];
153
+ } else {
154
+ $this->store_data['country-iso'] = $_POST['wpsl']['country-iso'];
155
+ $this->store_data['latlng'] = $latlng;
156
+ }
157
+
158
+ if ( $_POST['wpsl_actions'] == 'add_new_store' )
159
+ $this->add_new_store();
160
+
161
+ if ( $_POST['wpsl_actions'] == 'update_store' )
162
+ $this->update_store();
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Delete a single store
168
+ *
169
+ * This is called from the store overview page when a user clicks the delete button
170
+ *
171
+ * @since 1.0
172
+ * @return json Either fail or success
173
+ */
174
+ public function delete_store_ajax() {
175
+
176
+ global $wpdb;
177
+
178
+ $store_id = absint( $_POST['store_id'] );
179
+
180
+ if ( !current_user_can( 'manage_options' ) )
181
+ die( '-1' );
182
+
183
+ check_ajax_referer( 'wpsl_delete_nonce_'.$store_id );
184
+
185
+ $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->wpsl_stores WHERE wpsl_id = %d", $store_id ) );
186
+
187
+ if ( $result === false ) {
188
+ wp_send_json_error();
189
+ } else {
190
+ wp_send_json_success();
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Update store details
196
+ *
197
+ * @since 1.0
198
+ * @param array $store_data The updated store data
199
+ * @return void
200
+ */
201
+ public function update_store() {
202
+
203
+ global $wpdb;
204
+
205
+ $result = $wpdb->query(
206
+ $wpdb->prepare(
207
+ "
208
+ UPDATE $wpdb->wpsl_stores
209
+ SET store = %s, street = %s, city = %s, state = %s, zip = %s, country = %s, country_iso = %s, lat = %s, lng = %s, description = %s, phone = %s, fax = %s, url = %s, email = %s, hours = %s, thumb_id = %d, active = %d
210
+ WHERE wpsl_id = %d",
211
+ $this->store_data['store'],
212
+ $this->store_data['street'],
213
+ $this->store_data['city'],
214
+ $this->store_data['state'],
215
+ strtoupper ( $this->store_data['zip'] ),
216
+ $this->store_data['country'],
217
+ $this->store_data['country-iso'],
218
+ $this->store_data['latlng']['lat'],
219
+ $this->store_data['latlng']['lng'],
220
+ $this->store_data['desc'],
221
+ $this->store_data['phone'],
222
+ $this->store_data['fax'],
223
+ $this->store_data['url'],
224
+ $this->store_data['email'],
225
+ $this->store_data['hours'],
226
+ $this->store_data['thumb-id'],
227
+ $this->store_data['active'],
228
+ $_GET['store_id']
229
+ )
230
+ );
231
+
232
+ if ( $result === false ) {
233
+ $state = 'error';
234
+ $msg = __( 'There was a problem updating the store details, please try again.', 'wpsl' );
235
+ } else {
236
+ $_POST = array();
237
+ $state = 'updated';
238
+ $msg = __( 'Store details updated.', 'wpsl' );
239
+ }
240
+
241
+ add_settings_error ( 'update-store', esc_attr( 'update-store' ), $msg, $state );
242
+ }
243
+
244
+ /**
245
+ * Add a new store to the db
246
+ *
247
+ * @since 1.0
248
+ * @param array $store_data The submitted store data
249
+ * @return void
250
+ */
251
+ public function add_new_store() {
252
+
253
+ global $wpdb;
254
+
255
+ $result = $wpdb->query(
256
+ $wpdb->prepare(
257
+ "
258
+ INSERT INTO $wpdb->wpsl_stores
259
+ (store, street, city, state, zip, country, country_iso, lat, lng, description, phone, fax, url, email, hours, thumb_id)
260
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d)
261
+ ",
262
+ $this->store_data['store'],
263
+ $this->store_data['street'],
264
+ $this->store_data['city'],
265
+ $this->store_data['state'],
266
+ strtoupper ( $this->store_data['zip'] ),
267
+ $this->store_data['country'],
268
+ $this->store_data['country-iso'],
269
+ $this->store_data['latlng']['lat'],
270
+ $this->store_data['latlng']['lng'],
271
+ $this->store_data['desc'],
272
+ $this->store_data['phone'],
273
+ $this->store_data['fax'],
274
+ $this->store_data['url'],
275
+ $this->store_data['email'],
276
+ $this->store_data['hours'],
277
+ $this->store_data['thumb-id']
278
+ )
279
+ );
280
+
281
+ if ( $result === false ) {
282
+ $state = 'error';
283
+ $msg = __( 'There was a problem saving the new store details, please try again.', 'wpsl' );
284
+ } else {
285
+ $_POST = array();
286
+ $state = 'updated';
287
+ $msg = __( 'Store succesfully added.', 'wpsl' );
288
+ }
289
+
290
+ add_settings_error ( 'add-store', esc_attr( 'add-store' ), $msg, $state );
291
+ }
292
+
293
+ /**
294
+ * Get a single value from the default settings
295
+ *
296
+ * @since 1.0
297
+ * @param string $setting The value that should be restored
298
+ * @return string the default setting value
299
+ */
300
+ public function get_default_setting( $setting ) {
301
+ return $this->default_settings[$setting];
302
+ }
303
+
304
+ /**
305
+ * Validate the submitted store data
306
+ *
307
+ * @since 1.0
308
+ * @return mixed array|void $store_data the submitted store data if not empty, otherwise nothing
309
+ */
310
+ public function validate_store_data() {
311
+
312
+ $store_data = $_POST['wpsl'];
313
+
314
+ if ( empty( $store_data['store'] ) || ( empty( $store_data['street'] ) ) || ( empty( $store_data['city'] ) ) || ( empty( $store_data['zip'] ) ) || ( empty( $store_data['country'] ) ) ) {
315
+ add_settings_error ( 'validate-store', esc_attr( 'validate-store' ), __( 'Please fill in all the required fields.', 'wpsl' ), 'error' );
316
+ } else {
317
+ return $store_data;
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Get the data for a single store
323
+ *
324
+ * @since 1.0
325
+ * @param string $store_id The id for a single store
326
+ * @return array $result The store details
327
+ */
328
+ public function get_store_data( $store_id ) {
329
+
330
+ global $wpdb;
331
+
332
+ $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wpsl_stores WHERE wpsl_id = %d", $store_id ), ARRAY_A );
333
+
334
+ return $result;
335
+ }
336
+
337
+ /**
338
+ * Show the settings template
339
+ *
340
+ * @since 1.0
341
+ * @return void
342
+ */
343
+ public function show_settings() {
344
+ require 'templates/map-settings.php';
345
+ }
346
+
347
+ /**
348
+ * Show the faq template
349
+ *
350
+ * @since 1.0
351
+ * @return void
352
+ */
353
+ public function show_faq() {
354
+ require 'templates/faq.php';
355
+ }
356
+
357
+ /**
358
+ * Handle the different validation errors for the plugin settings
359
+ *
360
+ * @since 1.0
361
+ * @param string $error_type Contains the type of validation error that occured
362
+ * @return void
363
+ */
364
+ private function settings_error( $error_type ) {
365
+
366
+ switch ( $error_type ) {
367
+ case 'max_results':
368
+ $error_msg = __( 'The max results field cannot be empty, the default value has been restored.', 'wpsl' );
369
+ break;
370
+ case 'search_radius':
371
+ $error_msg = __( 'The search radius field cannot be empty, the default value has been restored.', 'wpsl' );
372
+ break;
373
+ case 'label_missing':
374
+ $error_msg = __( 'One of the label fields was left empty, the default value for that field has been restored.', 'wpsl' );
375
+ break;
376
+ }
377
+
378
+ add_settings_error ( 'setting-errors', esc_attr( 'settings_fail' ), $error_msg, 'error' );
379
+ }
380
+
381
+ /**
382
+ * Sanitize the submitted plugin settings
383
+ *
384
+ * @since 1.0
385
+ * @return array $output The setting values
386
+ */
387
+ public function sanitize_settings() {
388
+
389
+ $map_types = array(
390
+ 'roadmap',
391
+ 'satellite',
392
+ 'hybrid',
393
+ 'terrain'
394
+ );
395
+ $unit_values = array(
396
+ 'px',
397
+ '%'
398
+ );
399
+ $distance_units = array(
400
+ 'km',
401
+ 'mi'
402
+ );
403
+
404
+ $output['api_key'] = sanitize_text_field( $_POST['wpsl_api']['key'] );
405
+ $output['api_language'] = wp_filter_nohtml_kses( $_POST['wpsl_api']['language'] );
406
+ $output['api_region'] = wp_filter_nohtml_kses( $_POST['wpsl_api']['region'] );
407
+
408
+ if ( in_array( $_POST['wpsl_search']['distance_unit'], $distance_units ) ) {
409
+ $output['distance_unit'] = $_POST['wpsl_search']['distance_unit'];
410
+ } else {
411
+ $output['distance_unit'] = $this->get_default_setting( 'distance_unit' );
412
+ }
413
+
414
+ /* Check for a valid max results value, otherwise we use the default */
415
+ if ( !empty( $_POST['wpsl_search']['max_results'] ) ) {
416
+ $output['max_results'] = sanitize_text_field( $_POST['wpsl_search']['max_results'] );
417
+ } else {
418
+ $this->settings_error( 'max_results' );
419
+ $output['max_results'] = $this->get_default_setting( 'max_results' );
420
+ }
421
+
422
+ /* See if a search radius value exist, otherwise we use the default */
423
+ if ( !empty( $_POST['wpsl_search']['radius'] ) ) {
424
+ $output['search_radius'] = sanitize_text_field( $_POST['wpsl_search']['radius'] );
425
+ } else {
426
+ $this->settings_error( 'search_radius' );
427
+ $output['search_radius'] = $this->get_default_setting( 'search_radius' );
428
+ }
429
+
430
+ $output['marker_bounce'] = isset( $_POST['wpsl_map']['marker_bounce'] ) ? 1 : 0;
431
+
432
+ /* Check if we have a valid zoom level, it has to be between 1 or 12. If not set it to the default of 3 */
433
+ if ( $_POST['wpsl_map']['zoom_level'] >= 1 || $_POST['wpsl_map']['zoom_level'] <= 12 ) {
434
+ $output['zoom_level'] = $_POST['wpsl_map']['zoom_level'];
435
+ } else {
436
+ $output['zoom_level'] = $this->get_default_setting( 'zoom_level' );
437
+ }
438
+
439
+ $output['zoom_name'] = sanitize_text_field( $_POST['wpsl_map']['zoom_name'] );
440
+
441
+ /* If no location name is set to zoom to we also empty the latlng values from the hidden input field */
442
+ if ( empty( $output['zoom_name'] ) ) {
443
+ $output['zoom_latlng'] = '';
444
+ } else {
445
+ $output['zoom_latlng'] = sanitize_text_field( $_POST['wpsl_map']['zoom_latlng'] );
446
+ }
447
+
448
+ /* Check if we have a valid map type */
449
+ if ( in_array( $_POST['wpsl_map']['type'], $map_types ) ) {
450
+ $output['map_type'] = $_POST['wpsl_map']['type'];
451
+ } else {
452
+ $output['map_type'] = $this->get_default_setting( 'map_type' );
453
+ }
454
+
455
+ $output['streetview'] = isset( $_POST['wpsl_map']['streetview'] ) ? 1 : 0;
456
+ $output['pan_controls'] = isset( $_POST['wpsl_map']['pan_controls'] ) ? 1 : 0;
457
+ $output['control_position'] = ( $_POST['wpsl_map']['control_position'] == 'left' ) ? 'left' : 'right';
458
+ $output['control_style'] = ( $_POST['wpsl_map']['control_style'] == 'small' ) ? 'small' : 'large';
459
+ $output['auto_locate'] = isset( $_POST['wpsl_map']['auto_locate'] ) ? 1 : 0;
460
+
461
+ /* Check the height value of the map */
462
+ if ( absint( $_POST['wpsl_design']['height_value'] ) ) {
463
+ $output['height'] = $_POST['wpsl_design']['height_value'];
464
+ } else {
465
+ $output['height'] = $this->get_default_setting( 'height' );
466
+ }
467
+
468
+ /* Check the max-width of the infowindow */
469
+ if ( absint( $_POST['wpsl_design']['infowindow_width'] ) ) {
470
+ $output['infowindow_width'] = $_POST['wpsl_design']['infowindow_width'];
471
+ } else {
472
+ $output['infowindow_width'] = $this->get_default_setting( 'infowindow_width' );
473
+ }
474
+
475
+ /* Check the width for the search field */
476
+ if ( absint( $_POST['wpsl_design']['search_width'] ) ) {
477
+ $output['search_width'] = $_POST['wpsl_design']['search_width'];
478
+ } else {
479
+ $output['search_width'] = $this->get_default_setting( 'search_width' );
480
+ }
481
+
482
+ /* Check the width for labels */
483
+ if ( absint( $_POST['wpsl_design']['label_width'] ) ) {
484
+ $output['label_width'] = $_POST['wpsl_design']['label_width'];
485
+ } else {
486
+ $output['label_width'] = $this->get_default_setting( 'label_width' );
487
+ }
488
+
489
+ $output['results_dropdown'] = isset( $_POST['wpsl_design']['design_results'] ) ? 1 : 0;
490
+
491
+ $output['start_marker'] = wp_filter_nohtml_kses( $_POST['wpsl_map']['start_marker'] );
492
+ $output['store_marker'] = wp_filter_nohtml_kses( $_POST['wpsl_map']['store_marker'] );
493
+
494
+ $missing_labels = false;
495
+ $required_labels = array(
496
+ 'search',
497
+ 'search_btn',
498
+ 'preloader',
499
+ 'radius',
500
+ 'no_results',
501
+ 'results',
502
+ 'directions',
503
+ 'error',
504
+ 'phone',
505
+ 'fax',
506
+ 'hours',
507
+ 'start',
508
+ 'limit'
509
+ );
510
+
511
+ /**
512
+ * Labels can never be empty, so we make sure they always contain data.
513
+ * If they are empty, we use the default value
514
+ */
515
+ foreach ( $required_labels as $k => $label ) {
516
+ if ( !empty( $_POST['wpsl_label'][$label] ) ) {
517
+ $output[$label.'_label'] = sanitize_text_field( $_POST['wpsl_label'][$label] );
518
+ } else {
519
+ $output[$label.'_label'] = $this->get_default_setting( $label.'_label' );
520
+ $missing_labels = true;
521
+ }
522
+ }
523
+
524
+ if ( $missing_labels ) {
525
+ $this->settings_error( 'label_missing' );
526
+ }
527
+
528
+ return $output;
529
+ }
530
+
531
+ /**
532
+ * Validate the latlng values
533
+ *
534
+ * @since 1.0
535
+ * @param string $lat The latitude value
536
+ * @param string $lng The longitude value
537
+ * @return array|boolean $latlng The validated latlng values or false if it fails
538
+ */
539
+ public function validate_latlng( $lat, $lng ) {
540
+
541
+ if ( is_numeric( $lat ) || ( is_numeric( $lng ) ) ) {
542
+ $latlng = array(
543
+ "lat" => $lat,
544
+ "lng" => $lng
545
+ );
546
+
547
+ return $latlng;
548
+ } else {
549
+ return false;
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Geocode the store location
555
+ *
556
+ * @since 1.0
557
+ * @return array|void $response Contains the country name in the selected language, and the latlng values
558
+ */
559
+ public function geocode_location() {
560
+
561
+ $geocode_response = $this->get_latlng();
562
+
563
+ switch ( $geocode_response['status'] ) {
564
+ case 'OK':
565
+ $response = array (
566
+ "country" => $this->filter_country_name( $geocode_response ),
567
+ "latlng" => $geocode_response['results'][0]['geometry']['location']
568
+ );
569
+
570
+ return $response;
571
+ break;
572
+ case 'ZERO_RESULTS':
573
+ $msg = __( 'The Google Geocoding API returned no results for the store location. Please change the location and try again.', 'wpsl' );
574
+ break;
575
+ case 'OVER_QUERY_LIMIT':
576
+ $msg = __( 'You have reached the daily allowed geocoding limit, you can read more <a href="https://developers.google.com/maps/documentation/geocoding/#Limits">here</a>.', 'wpsl' );
577
+ break;
578
+ default:
579
+ $msg = __( 'The Google Geocoding API failed to return valid data, please try again later.', 'wpsl' );
580
+ break;
581
+ }
582
+
583
+ if ( !empty( $msg ) ) {
584
+ add_settings_error ( 'geocode', esc_attr( 'geocode' ), $msg, 'error' );
585
+ }
586
+ }
587
+
588
+ /**
589
+ * Make the API call to Google to geocode the address
590
+ *
591
+ * @since 1.0
592
+ * @return array $response The geocode response
593
+ */
594
+ public function get_latlng() {
595
+
596
+ $address = $this->store_data["street"].','.$this->store_data["city"].','.$this->store_data["zip"].','.$this->store_data["country"];
597
+ $url = "http://maps.googleapis.com/maps/api/geocode/json?address=".urlencode( $address )."&sensor=false&language=".$this->settings['api_language'];
598
+
599
+ if ( extension_loaded( "curl" ) && function_exists( "curl_init" ) ) {
600
+ $ch = curl_init();
601
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
602
+ curl_setopt( $ch, CURLOPT_URL, $url ) ;
603
+ $result = curl_exec( $ch );
604
+ curl_close( $ch );
605
+ } else {
606
+ $result = file_get_contents( $url );
607
+ }
608
+
609
+ $response = json_decode( $result, true );
610
+
611
+ return $response;
612
+ }
613
+
614
+ /**
615
+ * Filter out the country names from the geocode response
616
+ *
617
+ * Take out both the full country name, and the short country code (2 letters)
618
+ *
619
+ * @since 1.0
620
+ * @param array $response The fulle API geocode response
621
+ * @return array $country_name Both the full and short country names
622
+ */
623
+ public function filter_country_name( $response ) {
624
+
625
+ $length = count( $response[results][0][address_components] );
626
+
627
+ /* Loop over the address components untill we find the country,political part */
628
+ for ( $i = 0; $i < $length; $i++ ) {
629
+ $address_component = $response[results][0][address_components][$i][types];
630
+
631
+ if ( $address_component[0] == 'country' && $address_component[1] == 'political' ) {
632
+ $country_name['long_name'] = $response[results][0][address_components][$i][long_name];
633
+ $country_name['short_name'] = $response[results][0][address_components][$i][short_name];
634
+
635
+ break;
636
+ }
637
+ }
638
+
639
+ return $country_name;
640
+ }
641
+
642
+ /**
643
+ * Show the options of the start and store markers
644
+ *
645
+ * @since 1.0
646
+ * @return string $marker_list The complete list of available and selected markers
647
+ */
648
+ public function show_marker_options() {
649
+
650
+ $marker_list;
651
+ $marker_images = $this->get_available_markers();
652
+ $marker_locations = array(
653
+ "start",
654
+ "store"
655
+ );
656
+
657
+ foreach ( $marker_locations as $location ) {
658
+ if ( $location == 'start' ) {
659
+ $marker_list .= __( 'Start location marker:', 'wpsl' );
660
+ } else {
661
+ $marker_list .= __( 'Store location marker:', 'wpsl' );
662
+ }
663
+
664
+ if ( !empty( $marker_images ) ) {
665
+ $marker_list .= '<ul class="wpsl-marker-list">';
666
+
667
+ foreach ( $marker_images as $marker_img ) {
668
+ $marker_list .= $this->create_marker_html( $marker_img, $location );
669
+ }
670
+
671
+ $marker_list .= '</ul>';
672
+ }
673
+ }
674
+
675
+ return $marker_list;
676
+ }
677
+
678
+ /**
679
+ * Load the markers that can be used on the map
680
+ *
681
+ * @since 1.0
682
+ * @return array $marker_images A list of all the available markers
683
+ */
684
+ public function get_available_markers() {
685
+
686
+ $dir = WPSL_PLUGIN_DIR . 'img/markers/';
687
+
688
+ if ( is_dir( $dir ) ) {
689
+ if ( $dh = opendir( $dir ) ) {
690
+ while ( false !== ( $file = readdir( $dh ) ) ) {
691
+ if ( $file == '.' || $file == '..' || ( strpos( $file, '@2x' ) !== false ) ) continue;
692
+ $marker_images[] = $file;
693
+ }
694
+
695
+ closedir( $dh );
696
+ }
697
+ }
698
+
699
+ return $marker_images;
700
+ }
701
+
702
+ /**
703
+ * Create the html output for the marker list that is shown on the settings page
704
+ *
705
+ * There are two markers lists, one where the user can set the marker for the start point
706
+ * and one where a marker can be set for the store. We also check if the marker img is identical
707
+ * to the name in the option field. If so we set it to checked
708
+ *
709
+ * @since 1.0
710
+ * @param string $marker_img The filename of the marker
711
+ * @param string $location Eiter contains "start" or "store"
712
+ * @return string $marker_list A list of all the available markers
713
+ */
714
+ public function create_marker_html( $marker_img, $location ) {
715
+
716
+ $marker_list = '';
717
+
718
+ if ( $this->settings[$location.'_marker'] == $marker_img ) {
719
+ $checked = 'checked="checked"';
720
+ $css_class = 'class="wpsl-active-marker"';
721
+ } else {
722
+ $checked = '';
723
+ $css_class = '';
724
+ }
725
+
726
+ $marker_list .= '<li ' . $css_class . '>';
727
+ $marker_list .= '<img src="' . WPSL_URL . 'img/markers/' . $marker_img . '" />';
728
+ $marker_list .= '<input ' . $checked . ' type="radio" name="wpsl_map[' . $location . '_marker]" value="' . $marker_img . '" />';
729
+ $marker_list .= '</li>';
730
+
731
+ return $marker_list;
732
+ }
733
+
734
+ /**
735
+ * Create a dropdown where users can select the distance unit (km or miles),
736
+ * and set the active value to selected.
737
+ *
738
+ * @since 1.0
739
+ * @return string $dropdown The html for the distance option list
740
+ */
741
+ public function show_distance_units() {
742
+
743
+ $items = array( 'km', 'mi' );
744
+ $dropdown = '<select id="wpsl-distance-unit" name="wpsl_search[distance_unit]">';
745
+
746
+ foreach ( $items as $item => $value ) {
747
+ $selected = ( $this->settings['distance_unit'] == $value ) ? 'selected="selected"' : '';
748
+ $dropdown .= "<option value='$value' $selected>" . $value . "</option>";
749
+ }
750
+
751
+ $dropdown .= "</select>";
752
+
753
+ return $dropdown;
754
+ }
755
+
756
+ /**
757
+ * Create a dropdown where users can select the used map type
758
+ *
759
+ * @since 1.0
760
+ * @return string $dropdown The html for the map option list
761
+ */
762
+ public function show_map_types() {
763
+
764
+ $items = array(
765
+ 'roadmap',
766
+ 'satellite',
767
+ 'hybrid',
768
+ 'terrain'
769
+ );
770
+ $dropdown = '<select id="wpsl-map-type" name="wpsl_map[type]">';
771
+
772
+ foreach ( $items as $item => $value ) {
773
+ $selected = ( $this->settings['map_type'] == $value ) ? 'selected="selected"' : '';
774
+ $dropdown .= "<option value='$value' $selected>" . ucfirst( $value ) . "</option>";
775
+ }
776
+
777
+ $dropdown .= "</select>";
778
+
779
+ return $dropdown;
780
+ }
781
+
782
+ /**
783
+ * Create the dropdown to select the zoom level
784
+ *
785
+ * @since 1.0
786
+ * @return string $dropdown The html for the zoom level list
787
+ */
788
+ public function show_zoom_levels() {
789
+
790
+ $dropdown = '<select id="wpsl-zoom-level" name="wpsl_map[zoom_level]">';
791
+
792
+ for ( $i = 1; $i < 13; $i++ ) {
793
+ $selected = ( $this->settings['zoom_level'] == $i ) ? 'selected="selected"' : '';
794
+
795
+ switch ( $i ) {
796
+ case '1':
797
+ $zoom_desc = '- World view';
798
+ break;
799
+ case '3':
800
+ $zoom_desc = '- Default';
801
+ break;
802
+ case '12':
803
+ $zoom_desc = '- Roadmap view';
804
+ break;
805
+ default:
806
+ $zoom_desc = '';
807
+ }
808
+
809
+ $dropdown .= "<option value='$i' $selected>$i $zoom_desc</option>";
810
+ }
811
+
812
+ $dropdown .= "</select>";
813
+
814
+ return $dropdown;
815
+ }
816
+
817
+ /**
818
+ * Options for the language and region list
819
+ *
820
+ * @since 1.0
821
+ * @param string $list The request list type
822
+ * @return string|void $option_list The html for the selected list, or nothing if the $list contains invalud values
823
+ */
824
+ public function get_api_option_list( $list ) {
825
+
826
+ switch ( $list ) {
827
+ case 'language':
828
+ $api_option_list = array (
829
+ __('Select your language', 'wpsl') => '',
830
+ __('English', 'wpsl') => 'en',
831
+ __('Arabic', 'wpsl') => 'ar',
832
+ __('Basque', 'wpsl') => 'eu',
833
+ __('Bulgarian', 'wpsl') => 'bg',
834
+ __('Bengali', 'wpsl') => 'bn',
835
+ __('Catalan', 'wpsl') => 'ca',
836
+ __('Czech', 'wpsl') => 'cs',
837
+ __('Danish', 'wpsl') => 'da',
838
+ __('German', 'wpsl') => 'de',
839
+ __('Greek', 'wpsl') => 'el',
840
+ __('English (Australian)', 'wpsl') => 'en-AU',
841
+ __('English (Great Britain)', 'wpsl') => 'en-GB',
842
+ __('Spanish', 'wpsl') => 'es',
843
+ __('Farsi', 'wpsl') => 'fa',
844
+ __('Finnish', 'wpsl') => 'fi',
845
+ __('Filipino', 'wpsl') => 'fil',
846
+ __('French', 'wpsl') => 'fr',
847
+ __('Galician', 'wpsl') => 'gl',
848
+ __('Gujarati', 'wpsl') => 'gu',
849
+ __('Hindi', 'wpsl') => 'hi',
850
+ __('Croatian', 'wpsl') => 'hr',
851
+ __('Hungarian', 'wpsl') => 'hu',
852
+ __('Indonesian', 'wpsl') => 'id',
853
+ __('Italian', 'wpsl') => 'it',
854
+ __('Hebrew', 'wpsl') => 'iw',
855
+ __('Japanese', 'wpsl') => 'ja',
856
+ __('Kannada', 'wpsl') => 'kn',
857
+ __('Korean', 'wpsl') => 'ko',
858
+ __('Lithuanian', 'wpsl') => 'lt',
859
+ __('Latvian', 'wpsl') => 'lv',
860
+ __('Malayalam', 'wpsl') => 'ml',
861
+ __('Marathi', 'wpsl') => 'mr',
862
+ __('Dutch', 'wpsl') => 'nl',
863
+ __('Norwegian', 'wpsl') => 'no',
864
+ __('Norwegian Nynorsk', 'wpsl') => 'nn',
865
+ __('Polish', 'wpsl') => 'pl',
866
+ __('Portuguese', 'wpsl') => 'pt',
867
+ __('Portuguese (Brazil)', 'wpsl') => 'pt-BR',
868
+ __('Portuguese (Portugal)', 'wpsl') => 'pt-PT',
869
+ __('Romanian', 'wpsl') => 'ro',
870
+ __('Russian', 'wpsl') => 'ru',
871
+ __('Slovak', 'wpsl') => 'sk',
872
+ __('Slovenian', 'wpsl') => 'sl',
873
+ __('Serbian', 'wpsl') => 'sr',
874
+ __('Swedish', 'wpsl') => 'sv',
875
+ __('Tagalog', 'wpsl') => 'tl',
876
+ __('Tamil', 'wpsl') => 'ta',
877
+ __('Telugu', 'wpsl') => 'te',
878
+ __('Thai', 'wpsl') => 'th',
879
+ __('Turkish', 'wpsl') => 'tr',
880
+ __('Ukrainian', 'wpsl') => 'uk',
881
+ __('Vietnamese', 'wpsl') => 'vi',
882
+ __('Chinese (Simplified)', 'wpsl') => 'zh-CN',
883
+ __('Chinese (Traditional)' ,'wpsl') => 'zh-TW'
884
+ );
885
+ break;
886
+ case 'region':
887
+ $api_option_list = array (
888
+ __('Select your region', '') => '',
889
+ __('Afghanistan', 'wpsl') => 'af',
890
+ __('Albania', 'wpsl') => 'al',
891
+ __('Algeria', 'wpsl') => 'dz',
892
+ __('American Samoa', 'wpsl') => 'as',
893
+ __('Andorra', 'wpsl') => 'ad',
894
+ __('Anguilla', 'wpsl') => 'ai',
895
+ __('Angola', 'wpsl') => 'ao',
896
+ __('Antigua and Barbuda', 'wpsl') => 'ag',
897
+ __('Argentina', 'wpsl') => 'ar',
898
+ __('Armenia', 'wpsl') => 'am',
899
+ __('Aruba', 'wpsl') => 'aw',
900
+ __('Australia', 'wpsl') => 'au',
901
+ __('Austria', 'wpsl') => 'at',
902
+ __('Azerbaijan', 'wpsl') => 'az',
903
+ __('Bahamas', 'wpsl') => 'bs',
904
+ __('Bahrain', 'wpsl') => 'bh',
905
+ __('Bangladesh', 'wpsl') => 'bd',
906
+ __('Barbados', 'wpsl') => 'bb',
907
+ __('Belarus', 'wpsl') => 'by',
908
+ __('Belgium', 'wpsl') => 'be',
909
+ __('Belize', 'wpsl') => 'bz',
910
+ __('Benin', 'wpsl') => 'bj',
911
+ __('Bermuda', 'wpsl') => 'bm',
912
+ __('Bhutan', 'wpsl') => 'bt',
913
+ __('Bolivia', 'wpsl') => 'bo',
914
+ __('Bosnia and Herzegovina', 'wpsl') => 'ba',
915
+ __('Botswana', 'wpsl') => 'bw',
916
+ __('Brazil', 'wpsl') => 'br',
917
+ __('British Indian Ocean Territory', 'wpsl') => 'io',
918
+ __('Brunei', 'wpsl') => 'bn',
919
+ __('Bulgaria', 'wpsl') => 'bg',
920
+ __('Burkina Faso', 'wpsl') => 'bf',
921
+ __('Burundi', 'wpsl') => 'bi',
922
+ __('Cambodia', 'wpsl') => 'kh',
923
+ __('Cameroon', 'wpsl') => 'cm',
924
+ __('Canada', 'wpsl') => 'ca',
925
+ __('Cape Verde', 'wpsl') => 'cv',
926
+ __('Cayman Islands', 'wpsl') => 'ky',
927
+ __('Central African Republic', 'wpsl') => 'cf',
928
+ __('Chad', 'wpsl') => 'td',
929
+ __('Chile', 'wpsl') => 'cl',
930
+ __('China', 'wpsl') => 'cn',
931
+ __('Christmas Island', 'wpsl') => 'cx',
932
+ __('Cocos Islands', 'wpsl') => 'cc',
933
+ __('Colombia', 'wpsl') => 'co',
934
+ __('Comoros', 'wpsl') => 'km',
935
+ __('Congo', 'wpsl') => 'cg',
936
+ __('Costa Rica', 'wpsl') => 'cr',
937
+ __('Côte d\'Ivoire', 'wpsl') => 'ci',
938
+ __('Croatia', 'wpsl') => 'hr',
939
+ __('Cuba', 'wpsl') => 'cu',
940
+ __('Czech Republic', 'wpsl') => 'cz',
941
+ __('Denmark', 'wpsl') => 'dk',
942
+ __('Djibouti', 'wpsl') => 'dj',
943
+ __('Democratic Republic of the Congo', 'wpsl') => 'cd',
944
+ __('Dominica', 'wpsl') => 'dm',
945
+ __('Dominican Republic', 'wpsl') => 'do',
946
+ __('Ecuador', 'wpsl') => 'ec',
947
+ __('Egypt', 'wpsl') => 'eg',
948
+ __('El Salvador', 'wpsl') => 'sv',
949
+ __('Equatorial Guinea', 'wpsl') => 'gq',
950
+ __('Eritrea', 'wpsl') => 'er',
951
+ __('Estonia', 'wpsl') => 'ee',
952
+ __('Ethiopia', 'wpsl') => 'et',
953
+ __('Fiji', 'wpsl') => 'fj',
954
+ __('Finland', 'wpsl') => 'fi',
955
+ __('France', 'wpsl') => 'fr',
956
+ __('French Guiana', 'wpsl') => 'gf',
957
+ __('Gabon', 'wpsl') => 'ga',
958
+ __('Gambia', 'wpsl') => 'gm',
959
+ __('Germany', 'wpsl') => 'de',
960
+ __('Ghana', 'wpsl') => 'gh',
961
+ __('Greenland', 'wpsl') => 'gl',
962
+ __('Greece', 'wpsl') => 'gr',
963
+ __('Grenada', 'wpsl') => 'gd',
964
+ __('Guam', 'wpsl') => 'gu',
965
+ __('Guadeloupe', 'wpsl') => 'gp',
966
+ __('Guatemala', 'wpsl') => 'gt',
967
+ __('Guinea', 'wpsl') => 'gn',
968
+ __('Guinea-Bissau', 'wpsl') => 'gw',
969
+ __('Haiti', 'wpsl') => 'ht',
970
+ __('Honduras', 'wpsl') => 'hn',
971
+ __('Hong Kong', 'wpsl') => 'hk',
972
+ __('Hungary', 'wpsl') => 'hu',
973
+ __('Iceland', 'wpsl') => 'is',
974
+ __('India', 'wpsl') => 'in',
975
+ __('Indonesia', 'wpsl') => 'id',
976
+ __('Iran', 'wpsl') => 'ir',
977
+ __('Iraq', 'wpsl') => 'iq',
978
+ __('Ireland', 'wpsl') => 'ie',
979
+ __('Israel', 'wpsl') => 'il',
980
+ __('Italy', 'wpsl') => 'it',
981
+ __('Jamaica', 'wpsl') => 'jm',
982
+ __('Japan', 'wpsl') => 'jp',
983
+ __('Jordan', 'wpsl') => 'jo',
984
+ __('Kazakhstan', 'wpsl') => 'kz',
985
+ __('Kenya', 'wpsl') => 'ke',
986
+ __('Kuwait', 'wpsl') => 'kw',
987
+ __('Kyrgyzstan', 'wpsl') => 'kg',
988
+ __('Laos', 'wpsl') => 'la',
989
+ __('Latvia', 'wpsl') => 'lv',
990
+ __('Lebanon', 'wpsl') => 'lb',
991
+ __('Lesotho', 'wpsl') => 'ls',
992
+ __('Liberia', 'wpsl') => 'lr',
993
+ __('Libya', 'wpsl') => 'ly',
994
+ __('Liechtenstein', 'wpsl') => 'li',
995
+ __('Lithuania', 'wpsl') => 'lt',
996
+ __('Luxembourg', 'wpsl') => 'lu',
997
+ __('Macau', 'wpsl') => 'mo',
998
+ __('Macedonia', 'wpsl') => 'mk',
999
+ __('Madagascar', 'wpsl') => 'mg',
1000
+ __('Malawi', 'wpsl') => 'mw',
1001
+ __('Malaysia ', 'wpsl') => 'my',
1002
+ __('Mali', 'wpsl') => 'ml',
1003
+ __('Marshall Islands', 'wpsl') => 'mh',
1004
+ __('Martinique', 'wpsl') => 'il',
1005
+ __('Mauritania', 'wpsl') => 'mr',
1006
+ __('Mauritius', 'wpsl') => 'mu',
1007
+ __('Mexico', 'wpsl') => 'mx',
1008
+ __('Micronesia', 'wpsl') => 'fm',
1009
+ __('Moldova', 'wpsl') => 'md',
1010
+ __('Monaco' ,'wpsl') => 'mc',
1011
+ __('Mongolia', 'wpsl') => 'mn',
1012
+ __('Montenegro', 'wpsl') => 'me',
1013
+ __('Montserrat', 'wpsl') => 'ms',
1014
+ __('Morocco', 'wpsl') => 'ma',
1015
+ __('Mozambique', 'wpsl') => 'mz',
1016
+ __('Myanmar', 'wpsl') => 'mm',
1017
+ __('Namibia', 'wpsl') => 'na',
1018
+ __('Nauru', 'wpsl') => 'nr',
1019
+ __('Nepal', 'wpsl') => 'np',
1020
+ __('Netherlands', 'wpsl') => 'nl',
1021
+ __('Netherlands Antilles', 'wpsl') => 'an',
1022
+ __('New Zealand', 'wpsl') => 'nz',
1023
+ __('Nicaragua', 'wpsl') => 'ni',
1024
+ __('Niger', 'wpsl') => 'ne',
1025
+ __('Nigeria', 'wpsl') => 'ng',
1026
+ __('Niue', 'wpsl') => 'nu',
1027
+ __('Northern Mariana Islands', 'wpsl') => 'mp',
1028
+ __('Norway', 'wpsl') => 'no',
1029
+ __('Oman', 'wpsl') => 'om',
1030
+ __('Pakistan', 'wpsl') => 'pk',
1031
+ __('Panama' ,'wpsl') => 'pa',
1032
+ __('Papua New Guinea', 'wpsl') => 'pg',
1033
+ __('Paraguay' ,'wpsl') => 'py',
1034
+ __('Peru', 'wpsl') => 'pe',
1035
+ __('Philippines', 'wpsl') => 'ph',
1036
+ __('Pitcairn Islands', 'wpsl') => 'pn',
1037
+ __('Poland', 'wpsl') => 'pl',
1038
+ __('Portugal', 'wpsl') => 'pt',
1039
+ __('Qatar', 'wpsl') => 'qa',
1040
+ __('Reunion', 'wpsl') => 're',
1041
+ __('Romania', 'wpsl') => 'ro',
1042
+ __('Russia', 'wpsl') => 'ru',
1043
+ __('Rwanda', 'wpsl') => 'rw',
1044
+ __('Saint Helena', 'wpsl') => 'sh',
1045
+ __('Saint Kitts and Nevis', 'wpsl') => 'kn',
1046
+ __('Saint Vincent and the Grenadines', 'wpsl') => 'vc',
1047
+ __('Saint Lucia', 'wpsl') => 'lc',
1048
+ __('Samoa', 'wpsl') => 'ws',
1049
+ __('San Marino', 'wpsl') => 'sm',
1050
+ __('São Tomé and Príncipe', 'wpsl') => 'st',
1051
+ __('Saudi Arabia', 'wpsl') => 'sa',
1052
+ __('Senegal', 'wpsl') => 'sn',
1053
+ __('Serbia', 'wpsl') => 'rs',
1054
+ __('Seychelles', 'wpsl') => 'sc',
1055
+ __('Sierra Leone', 'wpsl') => 'sl',
1056
+ __('Singapore', 'wpsl') => 'sg',
1057
+ __('Slovakia', 'wpsl') => 'si',
1058
+ __('Solomon Islands', 'wpsl') => 'sb',
1059
+ __('Somalia', 'wpsl') => 'so',
1060
+ __('South Africa', 'wpsl') => 'za',
1061
+ __('South Korea', 'wpsl') => 'kr',
1062
+ __('Spain', 'wpsl') => 'es',
1063
+ __('Sri Lanka', 'wpsl') => 'lk',
1064
+ __('Sudan', 'wpsl') => 'sd',
1065
+ __('Swaziland', 'wpsl') => 'sz',
1066
+ __('Sweden', 'wpsl') => 'se',
1067
+ __('Switzerland', 'wpsl') => 'ch',
1068
+ __('Syria', 'wpsl') => 'sy',
1069
+ __('Taiwan', 'wpsl') => 'tw',
1070
+ __('Tajikistan', 'wpsl') => 'tj',
1071
+ __('Tanzania', 'wpsl') => 'tz',
1072
+ __('Thailand', 'wpsl') => 'th',
1073
+ __('Timor-Leste', 'wpsl') => 'tl',
1074
+ __('Tokelau' ,'wpsl') => 'tk',
1075
+ __('Togo', 'wpsl') => 'tg',
1076
+ __('Tonga', 'wpsl') => 'to',
1077
+ __('Trinidad and Tobago', 'wpsl') => 'tt',
1078
+ __('Tunisia', 'wpsl') => 'tn',
1079
+ __('Turkey', 'wpsl') => 'tr',
1080
+ __('Turkmenistan', 'wpsl') => 'tm',
1081
+ __('Tuvalu', 'wpsl') => 'tv',
1082
+ __('Uganda', 'wpsl') => 'ug',
1083
+ __('Ukraine', 'wpsl') => 'ua',
1084
+ __('United Arab Emirates', 'wpsl') => 'ae',
1085
+ __('United Kingdom', 'wpsl') => 'gb',
1086
+ __('United States', 'wpsl') => 'us',
1087
+ __('Uruguay', 'wpsl') => 'uy',
1088
+ __('Uzbekistan', 'wpsl') => 'uz',
1089
+ __('Wallis Futuna', 'wpsl') => 'wf',
1090
+ __('Venezuela', 'wpsl') => 've',
1091
+ __('Vietnam', 'wpsl') => 'vn',
1092
+ __('Yemen', 'wpsl') => 'ye',
1093
+ __('Zambia' ,'wpsl') => 'zm',
1094
+ __('Zimbabwe', 'wpsl') => 'zw'
1095
+ );
1096
+ }
1097
+
1098
+ /* Make sure we have a array with a value */
1099
+ if ( !empty( $api_option_list ) && ( is_array( $api_option_list ) ) ) {
1100
+ $i = 0;
1101
+
1102
+ foreach ( $api_option_list as $api_option_key => $api_option_value ) {
1103
+
1104
+ /* If no option value exist, set the first one as the selected */
1105
+ if ( ( $i == 0 ) && ( empty( $this->settings['api_'.$list] ) ) ) {
1106
+ $selected = 'selected="selected"';
1107
+ } else {
1108
+ $selected = ( $this->settings['api_'.$list] == $api_option_value ) ? 'selected="selected"' : '';
1109
+ }
1110
+
1111
+ $option_list .= '<option value="' . esc_attr( $api_option_value ) . '"' . $selected . '> ' . esc_html( $api_option_key ) . '</option>';
1112
+ $i++;
1113
+ }
1114
+
1115
+ return $option_list;
1116
+ }
1117
+
1118
+ }
1119
+
1120
+ /**
1121
+ * Check if we can use a font for the plugin icon
1122
+ *
1123
+ * @since 1.0
1124
+ * @param $location The location
1125
+ * @return void
1126
+ */
1127
+ private function check_icon_font_usage( $location ) {
1128
+
1129
+ global $wp_version;
1130
+
1131
+ if ( ( version_compare( $wp_version, '3.8', '>=' ) == TRUE ) ) {
1132
+ wp_enqueue_style( 'wpsl-admin-css-38', plugins_url( '/css/style-3.8.css', __FILE__ ), false );
1133
+ }
1134
+ }
1135
+
1136
+ /**
1137
+ * Add the required admin script
1138
+ *
1139
+ * @since 1.0
1140
+ * @return void
1141
+ */
1142
+ public function admin_scripts() {
1143
+ wp_enqueue_media();
1144
+ wp_enqueue_script( 'jquery-ui-dialog' );
1145
+ wp_enqueue_style( 'jquery-style', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css' );
1146
+ wp_enqueue_style( 'wpsl-admin-css', plugins_url( '/css/style.css', __FILE__ ), false );
1147
+ $this->check_icon_font_usage( 'footer' );
1148
+ wp_enqueue_script( 'wpsl-gmap', ( "//maps.google.com/maps/api/js?sensor=false&libraries=places&language=" . $this->settings['api_language'] ), false ); // we set the language here to make sure the geocode response returns the country name in the correct language
1149
+ wp_enqueue_script( 'wpsl-admin-js', plugins_url( '/js/wpsl-admin.js', __FILE__ ), array( 'jquery' ), false );
1150
+ wp_enqueue_script( 'wpsl-queue', plugins_url( '/js/ajax-queue.js', __FILE__ ), array( 'jquery' ), false );
1151
+ wp_enqueue_script( 'wpsl-retina', plugins_url( '/js/retina-1.1.0.js', __FILE__ ), array( 'jquery' ), false );
1152
+ }
1153
+ }
1154
+
1155
+ new WPSL_Admin;
1156
+ }
admin/class-store-overview.php ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( !class_exists( 'WP_List_Table' ) ) {
3
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
4
+ }
5
+
6
+ /**
7
+ * Class to create the table layout
8
+ *
9
+ * @package WordPress
10
+ * @subpackage List_Table
11
+ * @since 1.0
12
+ */
13
+ class WPSL_Store_Overview extends WP_List_Table {
14
+
15
+ /**
16
+ * Set the amount of stores that are shown on each page
17
+ * @since 1.0
18
+ * @var string
19
+ */
20
+ private $_per_page = 10;
21
+
22
+ /**
23
+ * Class constructor
24
+ */
25
+ function __construct() {
26
+
27
+ global $status, $page;
28
+
29
+ parent::__construct( array(
30
+ 'singular' => __( 'Store', 'wpsl' ),
31
+ 'plural' => __( 'Stores', 'wpsl' ),
32
+ 'ajax' => true
33
+ ) );
34
+ }
35
+
36
+ /**
37
+ * The default message that is shown when no store data exists
38
+ *
39
+ * @since 1.0
40
+ * @return string The default message
41
+ */
42
+ function no_items() {
43
+ _e( 'No stores found', 'wpsl' );
44
+ }
45
+
46
+ /**
47
+ * Set the default values for each column
48
+ *
49
+ * @since 1.0
50
+ * @param array $item Data for a single column row
51
+ * @param array $column_name The name of the column
52
+ * @return mixed The value for each column
53
+ */
54
+ function column_default( $item, $column_name ) {
55
+
56
+ switch( $column_name ) {
57
+ case 'wpsl_id':
58
+ case 'store':
59
+ case 'street':
60
+ case 'city':
61
+ case 'state':
62
+ case 'zip':
63
+ return $item[$column_name];
64
+ case 'thumb':
65
+ if ( $item['thumb'] !== '0' ) {
66
+ $thumb = '<img src="' . $item['thumb'] . '" width="36" height="36" />';
67
+ } else {
68
+ $thumb = '';
69
+ }
70
+
71
+ return $thumb;
72
+ case 'active':
73
+ return ( $item['active'] ) ? __( 'Active', 'wpsl' ) : __( 'Inactive', 'wpsl' );
74
+ case 'action':
75
+ return '<a class="button" href="' . admin_url( 'admin.php?page=wpsl_store_editor&action=edit_store&store_id=' . $item['wpsl_id'] ) . '">'. __( 'Edit', 'wpsl' ) . '</a><input type="button" class="button wpsl-delete-store-btn" name="text" value="Delete"><input name="wpsl_store_id" type="hidden" value="' . $item['wpsl_id'] . '" /><input name="wpsl_delete_nonce" type="hidden" value="' . wp_create_nonce( 'wpsl_delete_nonce_'.$item['wpsl_id'] ) . '" />';
76
+ default:
77
+ return 'wpsl_id';
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Define which columns should be sortable
83
+ *
84
+ * @since 1.0
85
+ * @return array $sortable_columns The list of sortable scolumns
86
+ */
87
+ function get_sortable_columns() {
88
+
89
+ $sortable_columns = array(
90
+ 'wpsl_id' => array( 'wpsl_id', true ), //true = already sorted ( default )
91
+ 'thumb' => array( 'thumb', false ),
92
+ 'store' => array( 'store', false ),
93
+ 'street' => array( 'street', false ),
94
+ 'city' => array( 'city', false ),
95
+ 'state' => array( 'state', false ),
96
+ 'zip' => array( 'zip', false ),
97
+ 'active' => array( 'active', false )
98
+ );
99
+
100
+ return $sortable_columns;
101
+ }
102
+
103
+ /**
104
+ * Get the list of columns
105
+ *
106
+ * @since 1.0
107
+ * @return array $columns The list of columns
108
+ */
109
+ function get_columns() {
110
+
111
+ $columns = array(
112
+ 'cb' => '<input type="checkbox" />',
113
+ 'wpsl_id' => __( 'ID', 'wpsl' ),
114
+ 'thumb' => __( 'Thumbnail', 'wpsl' ),
115
+ 'store' => __( 'Name', 'wpsl' ),
116
+ 'street' => __( 'Address', 'wpsl' ),
117
+ 'city' => __( 'City', 'wpsl' ),
118
+ 'state' => __( 'State', 'wpsl' ),
119
+ 'zip' => __( 'Zip', 'wpsl' ),
120
+ 'active' => __( 'Status', 'wpsl' ),
121
+ 'action' => __( 'Actions', 'wpsl' ),
122
+ );
123
+
124
+ return $columns;
125
+ }
126
+
127
+ /**
128
+ * Set the data for a checkbox column
129
+ *
130
+ * @since 1.0
131
+ * @param array $item The store data for this table row
132
+ * @return string The html for the checkbox with the correct value set
133
+ */
134
+ function column_cb( $item ) {
135
+ return sprintf(
136
+ '<input type="checkbox" name="store[]" value="%s" />', $item['wpsl_id']
137
+ );
138
+ }
139
+
140
+ /**
141
+ * Get the list of available bulk actions
142
+ *
143
+ * @since 1.0
144
+ * @return array $actions The list of bulk actions
145
+ */
146
+ function get_bulk_actions() {
147
+
148
+ $actions = array(
149
+ 'delete' => 'Delete',
150
+ 'activate' => 'Activate',
151
+ 'deactivate' => 'Deactivate'
152
+ );
153
+
154
+ return $actions;
155
+ }
156
+
157
+ /**
158
+ * Change the store status into either active or inactive
159
+ *
160
+ * @since 1.0
161
+ * @param string $store_ids The list of store ids
162
+ * @param string $status Is set to either activate or deactivate
163
+ * @return void
164
+ */
165
+ function update_store_status( $store_ids, $status ) {
166
+
167
+ global $wpdb;
168
+
169
+ if ( $status === 'deactivate' ) {
170
+ $active_status = 0;
171
+ $success_action_desc = __( 'deactivated', 'wpsl' );
172
+ $fail_action_desc = __( 'deactivating', 'wpsl' );
173
+ } else {
174
+ $active_status = 1;
175
+ $success_action_desc = __( 'activated', 'wpsl' );
176
+ $fail_action_desc = __( 'activating', 'wpsl' );
177
+ }
178
+
179
+ $result = $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wpsl_stores SET active = %d WHERE wpsl_id IN ( $store_ids )", $active_status ) );
180
+
181
+ if ( $result === false ) {
182
+ $state = 'error';
183
+ $msg = __( 'There was a problem ' . $fail_action_desc . ' the store(s), please try again.', 'wpsl' );
184
+ } else {
185
+ $state = 'updated';
186
+ $msg = __( 'Store(s) successfully ' . $success_action_desc , 'wpsl' );
187
+ }
188
+
189
+ add_settings_error ( 'bulk-state', esc_attr( 'bulk-state' ), $msg, $state );
190
+ }
191
+
192
+ /**
193
+ * Remove the selected stores from the db
194
+ *
195
+ * @since 1.0
196
+ * @param string $store_ids The list of store ids that should be deleted.
197
+ * @return void
198
+ */
199
+ function remove_stores( $store_ids ) {
200
+
201
+ global $wpdb;
202
+
203
+ $result = $wpdb->query( "DELETE FROM $wpdb->wpsl_stores WHERE wpsl_id IN ( $store_ids )" );
204
+
205
+ if ( $result === false ) {
206
+ $state = 'error';
207
+ $msg = __( 'There was a problem removing the store(s), please try again.', 'wpsl' );
208
+ } else {
209
+ $state = 'updated';
210
+ $msg = __( 'Store(s) successfully removed.' , 'wpsl' );
211
+ }
212
+
213
+ add_settings_error ( 'bulk-remove', esc_attr( 'bulk-remove' ), $msg, $state );
214
+ }
215
+
216
+ /**
217
+ * Process the bulk actions
218
+ *
219
+ * @since 1.0
220
+ * @return void
221
+ */
222
+ function process_bulk_action() {
223
+
224
+ if ( !current_user_can( 'manage_options' ) )
225
+ die( '-1' );
226
+
227
+ if ( isset( $_POST['_wpnonce'] ) && ! empty( $_POST['_wpnonce'] ) ) {
228
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
229
+ $action = 'bulk-' . $this->_args['plural'];
230
+
231
+ if ( !wp_verify_nonce( $nonce, $action ) )
232
+ wp_die( 'Nope! Security check failed!' );
233
+
234
+ $action = $this->current_action();
235
+
236
+ if ( !empty ( $action ) ) {
237
+ $id_list = array();
238
+
239
+ foreach ( $_POST['store'] as $store_id ) {
240
+ $id_list[] = $store_id;
241
+ }
242
+
243
+ /* Before checking which type of bulk action to run, we make sure we actually have some ids to process */
244
+ if ( !empty( $id_list ) ) {
245
+ $store_ids = esc_sql( implode( ',', wp_parse_id_list( $id_list ) ) );
246
+
247
+ switch ( $action ) {
248
+ case 'activate':
249
+ $this->update_store_status( $store_ids, 'activate' );
250
+ break;
251
+ case 'deactivate':
252
+ $this->update_store_status( $store_ids, 'deactivate' );
253
+ break;
254
+ case 'delete':
255
+ $this->remove_stores( $store_ids );
256
+ break;
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Get the required store data for the table list
265
+ *
266
+ * @since 1.0
267
+ * @return array $response The required store data
268
+ */
269
+ function get_store_list() {
270
+
271
+ global $wpdb;
272
+
273
+ /* Check if we need to run the search query or just show all the data */
274
+ if ( isset( $_POST['s'] ) ){
275
+ $search = trim( $_POST['s'] );
276
+ $result = $wpdb->get_results(
277
+ $wpdb->prepare( "SELECT wpsl_id, store, street, city, state, zip, thumb_id AS thumb, active
278
+ FROM $wpdb->wpsl_stores
279
+ WHERE store LIKE %s OR street LIKE %s OR city LIKE %s OR state LIKE %s OR zip LIKE %s",
280
+ '%' . like_escape( $search ). '%', '%' . like_escape( $search ). '%', '%' . like_escape( $search ). '%', '%' . like_escape( $search ). '%', '%' . like_escape( $search ). '%'
281
+ ), ARRAY_A
282
+ );
283
+ } else {
284
+ /* Order params */
285
+ $orderby = !empty ( $_GET["orderby"] ) ? mysql_real_escape_string ( $_GET["orderby"] ) : 'wpsl_id';
286
+ $order = !empty ( $_GET["order"] ) ? mysql_real_escape_string ( $_GET["order"] ) : 'ASC';
287
+ $order_sql = $orderby.' '.$order;
288
+
289
+ /* Pagination parameters */
290
+ $total_items = $wpdb->get_var( "SELECT COUNT(*) AS count FROM $wpdb->wpsl_stores" );
291
+ $paged = !empty ( $_GET["paged"] ) ? mysql_real_escape_string ( $_GET["paged"] ) : '';
292
+
293
+ if ( empty( $paged ) || !is_numeric( $paged ) || $paged <= 0 ) {
294
+ $paged = 1;
295
+ }
296
+
297
+ $totalpages = ceil( $total_items / $this->_per_page );
298
+
299
+ if ( !empty( $paged ) && !empty( $this->_per_page ) ){
300
+ $offset = ( $paged - 1 ) * $this->_per_page;
301
+ $limit_sql = (int)$offset.',' . (int)$this->_per_page;
302
+ }
303
+
304
+ $result = $wpdb->get_results( "SELECT wpsl_id, store, street, city, state, zip, thumb_id AS thumb, active FROM $wpdb->wpsl_stores ORDER BY $order_sql LIMIT $limit_sql", ARRAY_A );
305
+ }
306
+
307
+ $i = 0;
308
+ foreach ( $result as $k => $store_details ) {
309
+
310
+ /* Check for thumbnails */
311
+ if ( absint ( $store_details["thumb"] ) ) {
312
+ $thumb_src = wp_get_attachment_image_src( $store_details["thumb"] );
313
+ $result[$i]["thumb"] = $thumb_src[0];
314
+ }
315
+
316
+ $i++;
317
+ }
318
+
319
+ $response = array(
320
+ "data" => $result,
321
+ "count" => $total_items
322
+ );
323
+
324
+ return $response;
325
+ }
326
+
327
+ /**
328
+ * Prepares the list of items for displaying.
329
+ *
330
+ * @since 1.0
331
+ * @uses WP_List_Table::set_pagination_args()
332
+ * @return void
333
+ */
334
+ function prepare_items() {
335
+
336
+ $columns = $this->get_columns();
337
+ $hidden = array();
338
+ $sortable = $this->get_sortable_columns();
339
+
340
+ $this->process_bulk_action();
341
+ $response = $this->get_store_list();
342
+
343
+ $current_page = $this->get_pagenum();
344
+ $total_items = $response['count'];
345
+
346
+ $this->set_pagination_args( array(
347
+ 'total_items' => $total_items,
348
+ 'per_page' => $this->_per_page,
349
+ 'total_pages' => ceil( $total_items / $this->_per_page )
350
+ ) );
351
+
352
+ $this->items = $response['data'];
353
+ $this->_column_headers = array( $columns, $hidden, $sortable );
354
+ }
355
+
356
+ /**
357
+ * Display the table with the store list
358
+ *
359
+ * @since 1.0
360
+ * @uses WP_List_Table::display_tablenav()
361
+ * @uses WP_List_Table::get_table_classes()
362
+ * @uses WP_List_Table::print_column_headers()
363
+ * @uses WP_List_Table::display_rows_or_placeholder()
364
+ * @return void
365
+ */
366
+ function display() {
367
+ extract( $this->_args );
368
+
369
+ $this->display_tablenav( 'top' );
370
+ ?>
371
+ <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>" cellspacing="0">
372
+ <thead>
373
+ <tr>
374
+ <?php $this->print_column_headers(); ?>
375
+ </tr>
376
+ </thead>
377
+
378
+ <tfoot>
379
+ <tr>
380
+ <?php $this->print_column_headers( false ); ?>
381
+ </tr>
382
+ </tfoot>
383
+
384
+ <tbody id="the-list"<?php if ( $singular ) echo " data-wp-lists='list:$singular'"; ?>>
385
+ <?php $this->display_rows_or_placeholder(); ?>
386
+ </tbody>
387
+ </table>
388
+ <div id="wpsl-delete-confirmation">
389
+ <p><?php _e( 'Are you sure you want to delete this store?', 'wpsl' ); ?></p>
390
+ <p>
391
+ <input class="button-primary" type="submit" name="delete" value="<?php _e( 'Delete', 'wpsl' ); ?>" />
392
+ <input class="button-secondary dialog-cancel" type="reset" value="<?php _e( 'Cancel', 'wpsl' ); ?>" />
393
+ </p>
394
+ </div>
395
+ <?php
396
+ $this->display_tablenav( 'bottom' );
397
+ }
398
+
399
+ /**
400
+ * Send required variables to JavaScript land
401
+ */
402
+ function _js_vars() {
403
+
404
+ $args = array(
405
+ 'url' => WPSL_URL
406
+ );
407
+
408
+ printf( "<script type='text/javascript'>var wpsl_data = %s;</script>\n", json_encode( $args ) );
409
+ }
410
+
411
+ }
admin/css/style-3.8.css ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'fontello';
3
+ src: url('../font/fontello.eot?92013519');
4
+ src: url('../font/fontello.eot?92013519#iefix') format('embedded-opentype'),
5
+ url('../font/fontello.woff?92013519') format('woff'),
6
+ url('../font/fontello.ttf?92013519') format('truetype'),
7
+ url('../font/fontello.svg?92013519#fontello') format('svg');
8
+ font-weight: normal;
9
+ font-style: normal;
10
+ }
11
+
12
+ #adminmenu #toplevel_page_wpsl_store_editor .wp-menu-image:before {
13
+ content: '\e800';
14
+ display: inline-block;
15
+ font-family: "fontello" !important;
16
+ font-style: normal;
17
+ font-variant: normal;
18
+ font-weight: 400;
19
+ line-height: 1em;
20
+ text-align: center;
21
+ text-decoration: inherit;
22
+ text-transform: none;
23
+ width: 1em;
24
+ }
25
+
26
+ /* Hide the pre 3.8 menu icon */
27
+ #adminmenu #toplevel_page_wpsl_store_editor .wp-menu-image img {
28
+ display:none;
29
+ }
admin/css/style.css ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wpsl-store-overview .widefat td,
2
+ #wpsl-wrap .widefat td {
3
+ padding:12px 7px;
4
+ }
5
+
6
+ #wpsl-store-overview .column-action a {
7
+ float:left;
8
+ margin-right:5px;
9
+ }
10
+
11
+ #wpsl-store-overview .widefat td.thumb {
12
+ padding:6px 0 0 8px;
13
+ }
14
+
15
+ #wpsl-store-overview .thumb img {
16
+ border-radius:3px;
17
+ }
18
+
19
+ #wpsl-store-overview p.search-box {
20
+ margin:0 0 1em 0;
21
+ }
22
+
23
+ .column-action {
24
+ width:130px;
25
+ }
26
+ #wpsl-delete-confirmation {
27
+ display:none;
28
+ }
29
+ .wpsl-preloader {
30
+ float:right;
31
+ margin:4px 0 0 4px;
32
+ }
33
+
34
+ /* Plugin nav */
35
+ #wpsl-mainnav {
36
+ border-bottom: 1px solid #CCCCCC;
37
+ float: left;
38
+ margin-bottom: 15px;
39
+ padding-left: 7px;
40
+ width: 99.4%;
41
+ }
42
+ #wpsl-mainnav li a {
43
+ display: block;
44
+ padding: 9px 12px;
45
+ text-decoration: none;
46
+ }
47
+
48
+ #wpsl-mainnav li {
49
+ float: left;
50
+ margin: 0;
51
+ }
52
+
53
+ #wpsl-wrap.wpsl-add-stores label,
54
+ #wpsl-wrap label {
55
+ width:85px;
56
+ margin-top:6px;
57
+ }
58
+
59
+ .wpsl-radioboxes {
60
+ float:left;
61
+ margin:5px 0 6px 1px;
62
+ }
63
+ #wpsl-wrap.wpsl-add-stores label {
64
+ float:left;
65
+ }
66
+ #wpsl-wrap.wpsl-add-stores p {
67
+ overflow:hidden;
68
+ }
69
+ #wpsl-wrap.wpsl-add-stores .wpsl-radioboxes label {
70
+ float:none;
71
+ margin-right:10px;
72
+ }
73
+
74
+ #wpsl-wrap em {
75
+ display:block;
76
+ }
77
+ #wpsl-wrap textarea {
78
+ width:340px;
79
+ resize:none;
80
+ }
81
+
82
+ .wpsl-curve {
83
+ border-radius:3px;
84
+ }
85
+
86
+ #wpsl-wrap .wpsl-error {
87
+ border:1px solid #c01313;
88
+ }
89
+
90
+ .wpsl-submit-wrap,
91
+ #wpsl-thumb-wrap div {
92
+ margin-left:85px;
93
+ }
94
+ #wpsl-lookup-location {
95
+ margin-bottom:2px;
96
+ }
97
+
98
+ #wpsl-wrap input[type=text],
99
+ #wpsl-wrap input[type=email],
100
+ #wpsl-wrap input[type=url] {
101
+ width:340px;
102
+ }
103
+
104
+ #wpsl-wrap.wpsl-settings input[type=text].textinput {
105
+ width:255px;
106
+ }
107
+
108
+ .wpsl-add-store {
109
+ float:left;
110
+ width:100%;
111
+ clear:both;
112
+ }
113
+
114
+ #wpsl-wrap .metabox-holder {
115
+ float:left;
116
+ margin-right:20px;
117
+ }
118
+ #wpsl-wrap .metabox-holder.wpsl-wide {
119
+ width:100%;
120
+ padding-top:0;
121
+ }
122
+
123
+ #wpsl-wrap .wpsl-edit-header {
124
+ margin-bottom:12px;
125
+ }
126
+
127
+ #wpsl-thumb-wrap {
128
+ width:100%;
129
+ overflow:hidden;
130
+ }
131
+
132
+ #wpsl-thumb-wrap p {
133
+ margin-top:0;
134
+ }
135
+
136
+ #wpsl-thumb-wrap div {
137
+ float:left;
138
+ clear:both;
139
+ }
140
+ .wpsl-thumb-placeholder {
141
+ background: none repeat scroll 0 0 #FFFFFF;
142
+ border: 1px dashed #CCCCCC;
143
+ border-radius: 3px 3px 3px 3px;
144
+ float: left;
145
+ height: 85px;
146
+ width: 85px;
147
+ margin-bottom:10px;
148
+ }
149
+
150
+ #wpsl-wrap.wpsl-settings .metabox-holder {
151
+ width:100%;
152
+ }
153
+ #wpsl-wrap.wpsl-settings .metabox-holder h3:hover {
154
+ cursor: auto;
155
+ }
156
+ #wpsl-gmap-wrap {
157
+ float:left;
158
+ width:451px;
159
+ height:250px;
160
+ border-radius:3px;
161
+ margin-top:0;
162
+ margin-bottom:20px;
163
+ clear:both;
164
+ }
165
+
166
+ /* Markers */
167
+ .wpsl-marker-list {
168
+ overflow:hidden;
169
+ }
170
+ .wpsl-marker-list li {
171
+ float:left;
172
+ padding:10px;
173
+ margin-right:5px;
174
+ text-align:center;
175
+ }
176
+ .wpsl-marker-list li input[type="radio"] {
177
+ margin-right:0;
178
+ }
179
+ .wpsl-marker-list img {
180
+ display:block;
181
+ margin-bottom:7px;
182
+ }
183
+ .wpsl-marker-list li:hover,
184
+ .wpsl-active-marker {
185
+ background:#E4E4E4;
186
+ border-radius:5px;
187
+ cursor: pointer;
188
+ }
189
+
190
+ /* Settings page */
191
+ #wpsl-settings-form .postbox-container {
192
+ width:535px;
193
+ clear:both;
194
+ }
195
+ #wpsl-wrap .metabox-holder {
196
+ padding-top:0;
197
+ }
198
+
199
+ #wpsl-settings-form label {
200
+ position:relative;
201
+ display: inline-block;
202
+ font-weight: normal;
203
+ margin: 0 10px 0 0;
204
+ width: 220px;
205
+ }
206
+
207
+ #wpsl-save-settings {
208
+ float:left;
209
+ clear:both;
210
+ }
211
+ #wpsl-settings-form .wpsl_radioboxes label {
212
+ float:none;
213
+ margin-right: 10px;
214
+ width: auto;
215
+ }
216
+
217
+ #wpsl-faq dt {
218
+ margin-bottom:4px;
219
+ font-weight:bold;
220
+ font-size:110%;
221
+ }
222
+
223
+ #wpsl-faq dd {
224
+ margin-left: 0;
225
+ }
226
+
227
+ #wpsl-faq dl {
228
+ margin-bottom: 17px;
229
+ }
admin/font/fontello.eot ADDED
Binary file
admin/font/fontello.svg ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>Copyright (C) 2013 by original authors @ fontello.com</metadata>
5
+ <defs>
6
+ <font id="fontello" horiz-adv-x="1000" >
7
+ <font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
8
+ <missing-glyph horiz-adv-x="1000" />
9
+ <glyph glyph-name="location" unicode="&#xe800;" d="m429 493q0 59-42 101t-101 42t-101-42t-42-101t42-101t101-42t101 42t42 101z m142 0q0-61-18-100l-203-432q-9-18-27-29t-37-11t-38 11t-26 29l-204 432q-18 39-18 100q0 118 84 202t202 84t202-84t83-202z" horiz-adv-x="571.4" />
10
+ </font>
11
+ </defs>
12
+ </svg>
admin/font/fontello.ttf ADDED
Binary file
admin/font/fontello.woff ADDED
Binary file
admin/js/ajax-queue.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery.ajaxQueue - A queue for ajax requests
3
+ *
4
+ * (c) 2011 Corey Frang
5
+ * Dual licensed under the MIT and GPL licenses.
6
+ *
7
+ * Requires jQuery 1.5+
8
+ */
9
+ (function($) {
10
+
11
+ // jQuery on an empty object, we are going to use this as our Queue
12
+ var ajaxQueue = $({});
13
+
14
+ $.ajaxQueue = function( ajaxOpts ) {
15
+ var jqXHR,
16
+ dfd = $.Deferred(),
17
+ promise = dfd.promise();
18
+
19
+ // run the actual query
20
+ function doRequest( next ) {
21
+ jqXHR = $.ajax( ajaxOpts );
22
+ jqXHR.done( dfd.resolve )
23
+ .fail( dfd.reject )
24
+ .then( next, next );
25
+ }
26
+
27
+ // queue our ajax request
28
+ ajaxQueue.queue( doRequest );
29
+
30
+ // add the abort method
31
+ promise.abort = function( statusText ) {
32
+
33
+ // proxy abort to the jqXHR if it is active
34
+ if ( jqXHR ) {
35
+ return jqXHR.abort( statusText );
36
+ }
37
+
38
+ // if there wasn't already a jqXHR we need to remove from queue
39
+ var queue = ajaxQueue.queue(),
40
+ index = $.inArray( doRequest, queue );
41
+
42
+ if ( index > -1 ) {
43
+ queue.splice( index, 1 );
44
+ }
45
+
46
+ // and then reject the deferred
47
+ dfd.rejectWith( ajaxOpts.context || ajaxOpts, [ promise, statusText, "" ] );
48
+ return promise;
49
+ };
50
+
51
+ return promise;
52
+ };
53
+
54
+ })(jQuery);
admin/js/retina-1.1.0.js ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Retina.js v1.1.0
3
+ *
4
+ * Copyright 2013 Imulus, LLC
5
+ * Released under the MIT license
6
+ *
7
+ * Retina.js is an open source script that makes it easy to serve
8
+ * high-resolution images to devices with retina displays.
9
+ */
10
+ (function() {
11
+
12
+ var root = (typeof exports == 'undefined' ? window : exports);
13
+
14
+ var config = {
15
+ // Ensure Content-Type is an image before trying to load @2x image
16
+ // https://github.com/imulus/retinajs/pull/45)
17
+ check_mime_type: true
18
+ };
19
+
20
+
21
+
22
+ root.Retina = Retina;
23
+
24
+ function Retina() {}
25
+
26
+ Retina.configure = function(options) {
27
+ if (options == null) options = {};
28
+ for (var prop in options) config[prop] = options[prop];
29
+ };
30
+
31
+ Retina.init = function(context) {
32
+ if (context == null) context = root;
33
+
34
+ var existing_onload = context.onload || new Function;
35
+
36
+ context.onload = function() {
37
+ var images = document.getElementsByTagName("img"), retinaImages = [], i, image;
38
+ for (i = 0; i < images.length; i++) {
39
+ image = images[i];
40
+ retinaImages.push(new RetinaImage(image));
41
+ }
42
+ existing_onload();
43
+ }
44
+ };
45
+
46
+ Retina.isRetina = function(){
47
+ var mediaQuery = "(-webkit-min-device-pixel-ratio: 1.5),\
48
+ (min--moz-device-pixel-ratio: 1.5),\
49
+ (-o-min-device-pixel-ratio: 3/2),\
50
+ (min-resolution: 1.5dppx)";
51
+
52
+ if (root.devicePixelRatio > 1)
53
+ return true;
54
+
55
+ if (root.matchMedia && root.matchMedia(mediaQuery).matches)
56
+ return true;
57
+
58
+ return false;
59
+ };
60
+
61
+
62
+ root.RetinaImagePath = RetinaImagePath;
63
+
64
+ function RetinaImagePath(path, at_2x_path) {
65
+ this.path = path;
66
+ if (typeof at_2x_path !== "undefined" && at_2x_path !== null) {
67
+ this.at_2x_path = at_2x_path;
68
+ this.perform_check = false;
69
+ } else {
70
+ this.at_2x_path = path.replace(/\.\w+$/, function(match) { return "@2x" + match; });
71
+ this.perform_check = true;
72
+ }
73
+ }
74
+
75
+ RetinaImagePath.confirmed_paths = [];
76
+
77
+ RetinaImagePath.prototype.is_external = function() {
78
+ return !!(this.path.match(/^https?\:/i) && !this.path.match('//' + document.domain) )
79
+ }
80
+
81
+ RetinaImagePath.prototype.check_2x_variant = function(callback) {
82
+ var http, that = this;
83
+ if (this.is_external()) {
84
+ return callback(false);
85
+ } else if (!this.perform_check && typeof this.at_2x_path !== "undefined" && this.at_2x_path !== null) {
86
+ return callback(true);
87
+ } else if (this.at_2x_path in RetinaImagePath.confirmed_paths) {
88
+ return callback(true);
89
+ } else {
90
+ http = new XMLHttpRequest;
91
+ http.open('HEAD', this.at_2x_path);
92
+ http.onreadystatechange = function() {
93
+ if (http.readyState != 4) {
94
+ return callback(false);
95
+ }
96
+
97
+ if (http.status >= 200 && http.status <= 399) {
98
+ if (config.check_mime_type) {
99
+ var type = http.getResponseHeader('Content-Type');
100
+ if (type == null || !type.match(/^image/i)) {
101
+ return callback(false);
102
+ }
103
+ }
104
+
105
+ RetinaImagePath.confirmed_paths.push(that.at_2x_path);
106
+ return callback(true);
107
+ } else {
108
+ return callback(false);
109
+ }
110
+ }
111
+ http.send();
112
+ }
113
+ }
114
+
115
+
116
+
117
+ function RetinaImage(el) {
118
+ this.el = el;
119
+ this.path = new RetinaImagePath(this.el.getAttribute('src'), this.el.getAttribute('data-at2x'));
120
+ var that = this;
121
+ this.path.check_2x_variant(function(hasVariant) {
122
+ if (hasVariant) that.swap();
123
+ });
124
+ }
125
+
126
+ root.RetinaImage = RetinaImage;
127
+
128
+ RetinaImage.prototype.swap = function(path) {
129
+ if (typeof path == 'undefined') path = this.path.at_2x_path;
130
+
131
+ var that = this;
132
+ function load() {
133
+ if (! that.el.complete) {
134
+ setTimeout(load, 5);
135
+ } else {
136
+ that.el.setAttribute('width', that.el.offsetWidth);
137
+ that.el.setAttribute('height', that.el.offsetHeight);
138
+ that.el.setAttribute('src', path);
139
+ }
140
+ }
141
+ load();
142
+ }
143
+
144
+
145
+
146
+
147
+ if (Retina.isRetina()) {
148
+ Retina.init(root);
149
+ }
150
+
151
+ })();
152
+
admin/js/wpsl-admin.js ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function( $ ) {
2
+ var map, geocoder, uploadFrame,
3
+ markersArray = [];
4
+
5
+ /* Load the Google Maps */
6
+ function initializeGmap() {
7
+ var draggable, alt,
8
+ myOptions = {
9
+ zoom: 2,
10
+ center: new google.maps.LatLng( "52.378153", "4.899363" ),
11
+ mapTypeId: google.maps.MapTypeId.ROADMAP,
12
+ streetViewControl: false
13
+ };
14
+
15
+ geocoder = new google.maps.Geocoder();
16
+ map = new google.maps.Map( document.getElementById( "wpsl-gmap-wrap" ), myOptions );
17
+
18
+ checkEditStoreMarker();
19
+ }
20
+
21
+ /* If we have an existing latln value, add a marker to the map. This can only happen on the edit store page */
22
+ function checkEditStoreMarker() {
23
+ var location,
24
+ lat = $( "#wpsl-store-lat" ).val(),
25
+ lng = $( "#wpsl-store-lng" ).val();
26
+
27
+ if ( ( lat ) && ( lng ) ) {
28
+ location = new google.maps.LatLng( lat, lng );
29
+
30
+ map.setCenter( location );
31
+ map.setZoom( 16 );
32
+ addMarker( location );
33
+ }
34
+ }
35
+
36
+ /* If we have a city/country input field enable the autocomplete */
37
+ if ( $( "#wpsl-zoom-name" ).length ) {
38
+ activateAutoComplete();
39
+ }
40
+
41
+ /* Activate the autocomplete function for the city/country field */
42
+ function activateAutoComplete() {
43
+ var latlng,
44
+ input = document.getElementById( "wpsl-zoom-name" ),
45
+ options = {
46
+ types: ['geocode']
47
+ },
48
+ autocomplete = new google.maps.places.Autocomplete( input, options );
49
+
50
+ google.maps.event.addListener( autocomplete, "place_changed", function() {
51
+ latlng = autocomplete.getPlace().geometry.location;
52
+ setLatlng( latlng, "zoom" );
53
+ });
54
+ }
55
+
56
+ /* Add a new marker to the map based on the provided location (latlng) */
57
+ function addMarker( location ) {
58
+ var marker = new google.maps.Marker({
59
+ position: location,
60
+ map: map,
61
+ draggable: true
62
+ });
63
+
64
+ markersArray.push( marker );
65
+
66
+ google.maps.event.addListener( marker, "dragend", function() {
67
+ geocodeDraggedPosition( marker.getPosition() );
68
+ });
69
+ }
70
+
71
+ /* Lookup the location where the marker is dropped */
72
+ function geocodeDraggedPosition( pos ) {
73
+ geocoder.geocode({
74
+ latLng: pos
75
+ }, function ( response ) {
76
+ if ( response && response.length > 0 ) {
77
+ setLatlng( response[0].geometry.location, "store" );
78
+
79
+ /* Set the zip code, but only if no existing value exists */
80
+ setZipCode( response );
81
+ } else {
82
+ alert( "Cannot determine address at this location." );
83
+ }
84
+ });
85
+ }
86
+
87
+ /* Only update the zipcode if there is no existing value */
88
+ function setZipCode( response ) {
89
+ if ( !$( "#wpsl-store-zip" ).val() ) {
90
+ var filteredResponse = filterApiResponse( response );
91
+ $( "#wpsl-store-zip" ).val( filteredResponse.zip );
92
+ }
93
+ }
94
+
95
+ /* Lookup the provided location name with the Google Maps API */
96
+ $( "#wpsl-lookup-location" ).on( "click", function() {
97
+ codeAddress();
98
+ return false;
99
+ });
100
+
101
+ /* Geocode the user input */
102
+ function codeAddress() {
103
+ var filteredResponse,
104
+ street = $( "#wpsl-store-street" ).val(),
105
+ city = $( "#wpsl-store-city" ).val(),
106
+ zip = $( "#wpsl-store-zip" ).val(),
107
+ country = $( "#wpsl-store-country" ).val(),
108
+ address = street + ',' + city + ',' + zip + ',' + country;
109
+
110
+ /* Check we have all the requird data before attempting to geocode the address */
111
+ if ( !validatePreviewFields( street, city, zip, country ) ) {
112
+ geocoder.geocode( { 'address': address }, function( response, status ) {
113
+ if ( status === google.maps.GeocoderStatus.OK ) {
114
+
115
+ /* If we have a previous marker on the map remove it */
116
+ if ( typeof( markersArray[0] ) !== "undefined" ) {
117
+ if ( markersArray[0].draggable ) {
118
+ markersArray[0].setMap( null );
119
+ markersArray.splice(0, 1);
120
+ }
121
+ }
122
+
123
+ /* Center and zoom to the searched location */
124
+ map.setCenter( response[0].geometry.location );
125
+ map.setZoom( 16 );
126
+ addMarker( response[0].geometry.location );
127
+ setLatlng( response[0].geometry.location, "store" );
128
+
129
+ filteredResponse = filterApiResponse( response );
130
+ $( "#wpsl-store-zip" ).val( filteredResponse.zip );
131
+ $( "#wpsl-store-country" ).val( filteredResponse.country.long_name );
132
+ $( "#wpsl-country-iso" ).val( filteredResponse.country.short_name );
133
+ } else {
134
+ alert( "Geocode was not successful for the following reason: " + status );
135
+ }
136
+ });
137
+ }
138
+ }
139
+
140
+ /* Check that all required fields for the preview to work are there */
141
+ function validatePreviewFields( street, city, zip, country ) {
142
+ var error = false;
143
+
144
+ $( "#wpsl-wrap input" ).removeClass( "wpsl-error" );
145
+
146
+ if ( !street ) {
147
+ $( "#wpsl-store-street" ).addClass( "wpsl-error" );
148
+ error = true;
149
+ }
150
+
151
+ if ( !city ) {
152
+ $( "#wpsl-store-city" ).addClass( "wpsl-error" );
153
+ error = true;
154
+ }
155
+
156
+ if ( !zip ) {
157
+ $( "#wpsl-store-zip" ).addClass( "wpsl-error" );
158
+ error = true;
159
+ }
160
+
161
+ if ( !country ) {
162
+ $( "#wpsl-store-country" ).addClass( "wpsl-error" );
163
+ error = true;
164
+ }
165
+
166
+ return error;
167
+ }
168
+
169
+ /* Filter out the zipcode from the Google Maps API response */
170
+ function filterApiResponse( response ) {
171
+ var zipcode, responseType,
172
+ country = {},
173
+ collectedData = {},
174
+ addressLength = response[0].address_components.length;
175
+
176
+ /* Loop over the API response */
177
+ for ( i = 0; i < addressLength; i++ ){
178
+ responseType = response[0].address_components[i].types;
179
+
180
+ /* filter out the country name */
181
+ if ( /^country,political$/.test( responseType ) ) {
182
+ country = {
183
+ long_name : response[0].address_components[i].long_name,
184
+ short_name : response[0].address_components[i].short_name
185
+ }
186
+ }
187
+
188
+ /* filter out the postal code */
189
+ if ( ( /^postal_code$/.test( responseType ) ) || ( /^postal_code_prefix,postal_code$/.test( responseType ) ) ) {
190
+ zipcode = response[0].address_components[i].long_name;
191
+ }
192
+ }
193
+
194
+ collectedData = {
195
+ zip : zipcode,
196
+ country : country
197
+ };
198
+
199
+ return collectedData;
200
+ }
201
+
202
+ /* Update the hidden input field with the current lat/long values. */
203
+ function setLatlng( latlng, target ) {
204
+ var coordinates = stripCoordinates( latlng ),
205
+ lat = roundLatlng( coordinates[0], 6 ),
206
+ lng = roundLatlng( coordinates[1], 6 );
207
+
208
+ if ( target == "store" ) {
209
+ $( "#wpsl-store-lat" ).val( lat );
210
+ $( "#wpsl-store-lng" ).val( lng );
211
+ } else if ( target == "zoom" ) {
212
+ $( "#wpsl-latlng" ).val( lat + ',' + lng);
213
+ }
214
+ }
215
+
216
+ /* Round the latlng to 6 digits after the comma */
217
+ function roundLatlng( num, decimals ) {
218
+ return Math.round( num * Math.pow( 10, decimals ) ) / Math.pow( 10, decimals );
219
+ }
220
+
221
+ /* strip the '(' and ')' from the captured coordinates, and split them */
222
+ function stripCoordinates( coordinates ) {
223
+ var latlng = [],
224
+ selected = coordinates.toString(),
225
+ latlngStr = selected.split( ",",2 );
226
+
227
+ latlng[0] = latlngStr[0].replace( '(', '' );
228
+ latlng[1] = latlngStr[1].replace( ')', '' );
229
+
230
+ return latlng;
231
+ }
232
+
233
+ /* Handle the selection of custom thumbnails */
234
+ $( "#wpsl-thumb-wrap" ).on( "click", "#wpsl-media-upload", function( e ) {
235
+ e.preventDefault();
236
+
237
+ if ( uploadFrame ) {
238
+ uploadFrame.open();
239
+ return;
240
+ }
241
+
242
+ uploadFrame = wp.media.frames.uploadFrame = wp.media({
243
+ title: "Set store image",
244
+ frame: "select",
245
+ multiple: false,
246
+ library: {
247
+ type: "image"
248
+ },
249
+ button: {
250
+ text: "upload"
251
+ }
252
+ });
253
+
254
+ uploadFrame.on( "select", function() {
255
+ var media_attachment = uploadFrame.state().get( "selection" ).first().toJSON();
256
+ setLocationThumb( media_attachment.sizes.thumbnail.url, media_attachment.id );
257
+ });
258
+
259
+ uploadFrame.open();
260
+ });
261
+
262
+ /* Replace the thumnail with the placeholder */
263
+ $( "#wpsl-thumb-wrap" ).on( "click", "#wpsl-remove-thumb", function( e ) {
264
+ e.preventDefault();
265
+
266
+ $( "#wpsl-thumb-wrap img" ).replaceWith( "<span class='wpsl-thumb-placeholder'></span>" );
267
+ $( "#wpsl-thumb-id" ).val('');
268
+ });
269
+
270
+ /* Show the selected thumb */
271
+ function setLocationThumb( thumbUrl, thumbId ) {
272
+ var img;
273
+
274
+ if ( $( "#wpsl-thumb-wrap img" ).length ) {
275
+ $( "#wpsl-thumb-wrap img" ).attr({ 'src' : thumbUrl, 'data-img-id' : thumbId });
276
+ $( "#wpsl-thumb-id" ).val( thumbId );
277
+ } else {
278
+ img = "<img class='wpsl-curve' src='" + thumbUrl + "' width='85' height='85' />";
279
+ $( "#wpsl-thumb-wrap span" ).replaceWith( img );
280
+ $( "#wpsl-thumb-id" ).val( thumbId );
281
+ }
282
+ }
283
+
284
+ $( ".wpsl-delete-store-btn" ).removeAttr( "disabled" );
285
+
286
+ $( "#wpsl-store-overview" ).on( "click", ".wpsl-delete-store-btn", function() {
287
+ var elem = $(this),
288
+ dialogBox = $( "#wpsl-delete-confirmation" ),
289
+ cancelBtn = dialogBox.find( ".button-secondary" ),
290
+ submitBtn = dialogBox.find( ".button-primary" ),
291
+ storeId = $(this).next( "input[name='wpsl_store_id']" ).val();
292
+
293
+ dialogBox.dialog({
294
+ width: 325,
295
+ resizable : false,
296
+ modal: true,
297
+ minHeight: 0
298
+ });
299
+
300
+ $( ".ui-dialog-titlebar" ).remove();
301
+ cancelBtn.on( "click", function() {
302
+ dialogBox.dialog( "close" );
303
+ submitBtn.unbind( "click" );
304
+ dialogBox.unbind( "click" );
305
+ });
306
+
307
+ submitBtn.on( "click", function() {
308
+ dialogBox.dialog( "close" );
309
+ deleteStore( elem );
310
+ submitBtn.unbind( "click" );
311
+ dialogBox.unbind( "click" );
312
+
313
+ return false;
314
+ });
315
+
316
+ return false;
317
+ });
318
+
319
+ function deleteStore( elem ) {
320
+ var ajaxData = {},
321
+ $parentTr = elem.parents( "tr" );
322
+
323
+ showPreloader( elem );
324
+ elem.attr( "disabled", "disabled" );
325
+
326
+ ajaxData = {
327
+ action: "delete_store",
328
+ store_id: elem.parent( ".column-action" ).find( "input[name='wpsl_store_id']" ).val(),
329
+ _ajax_nonce: elem.parent( ".column-action" ).find( "input[name='wpsl_delete_nonce']" ).val()
330
+ };
331
+
332
+ jQuery.ajaxQueue({
333
+ url: ajaxurl,
334
+ data: ajaxData,
335
+ type: "POST"
336
+ }).done( function( response ) {
337
+ if ( response === -1 ) {
338
+ elem.removeAttr( "disabled" );
339
+ $( ".wpsl-preloader" ).remove();
340
+ alert( "Security check failed, reload the page and try again." );
341
+ } else if ( response.success ) {
342
+ setTimeout( function() {
343
+ $parentTr.fadeOut( "200", function() {
344
+ $parentTr.remove();
345
+ });
346
+ }, 2000);
347
+ }
348
+ });
349
+ }
350
+
351
+ $( ".wpsl-marker-list input[type=radio]" ).click( function(){
352
+ $(this).parents( ".wpsl-marker-list" ).find( "li" ).removeClass();
353
+ $(this).parent( "li" ).addClass( "wpsl-active-marker" );
354
+ });
355
+
356
+ $( ".wpsl-marker-list li" ).click( function(){
357
+ $(this).parents( ".wpsl-marker-list" ).find( "input" ).prop( "checked", false );
358
+ $(this).find( "input" ).prop( "checked", true );
359
+ $(this).siblings().removeClass();
360
+ $(this).addClass( "wpsl-active-marker" );
361
+ });
362
+
363
+ /* Insert the preloader after the button */
364
+ function showPreloader( elem ) {
365
+ if ( !elem.parent().find( ".wpsl-preloader" ).length ) {
366
+ elem.after( "<img class='wpsl-preloader' src='" + wpsl_data.url + "img/ajax-loader.gif' />" );
367
+ }
368
+ }
369
+
370
+ /* Load the map */
371
+ if ( $( "#wpsl-gmap-wrap" ).length ) {
372
+ google.maps.event.addDomListener( window, "load", initializeGmap );
373
+ }
374
+
375
+ });
admin/templates/add-store.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="wpsl-wrap" class="wrap wpsl-add-stores">
2
+ <h2>WP Store Locator</h2>
3
+ <?php settings_errors(); ?>
4
+
5
+ <ul id="wpsl-mainnav" class="nav-tab-wrapper">
6
+ <li><a class="nav-tab" href="<?php echo admin_url( 'admin.php?page=wpsl_store_editor' ); ?>"><?php _e('Current Stores', 'wpsl'); ?></a></li>
7
+ <li><a class="nav-tab nav-tab-active" href="<?php echo admin_url( 'admin.php?page=wpsl_add_store' ); ?>"><?php _e('Add Store', 'wpsl'); ?></a></li>
8
+ <li><a class="nav-tab" href="<?php echo admin_url( 'admin.php?page=wpsl_settings' ); ?>"><?php _e( 'Settings', 'wpsl' ); ?></a></li>
9
+ </ul>
10
+
11
+ <form method="post" action="" accept-charset="utf-8">
12
+ <input type="hidden" name="wpsl_actions" value="add_new_store" />
13
+ <?php wp_nonce_field( 'wpsl_add_new_store' ); ?>
14
+ <div class="wpsl-add-store">
15
+ <div class="metabox-holder">
16
+ <div class="postbox">
17
+ <h3><span><?php _e( 'Store details', 'wpsl' ); ?></span></h3>
18
+ <div class="inside">
19
+ <p>
20
+ <label for="wpsl-store-name"><?php _e( 'Store Name:', 'wpsl' ); ?></label>
21
+ <input id="wpsl-store-name" name="wpsl[store]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['store'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( !empty( $_POST['wpsl']['store'] ) ) { echo esc_attr( $_POST['wpsl']['store'] ); } ?>" />
22
+ </p>
23
+ <p>
24
+ <label for="wpsl-store-street"><?php _e( 'Street:', 'wpsl' ); ?></label>
25
+ <input id="wpsl-store-street" name="wpsl[street]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['street'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( !empty( $_POST['wpsl']['street'] ) ) { echo esc_attr( $_POST['wpsl']['street'] ); } ?>" />
26
+ </p>
27
+ <p>
28
+ <label for="wpsl-store-city"><?php _e( 'City:', 'wpsl' ); ?></label>
29
+ <input id="wpsl-store-city" name="wpsl[city]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['city'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( !empty( $_POST['wpsl']['city'] ) ) { echo esc_attr( $_POST['wpsl']['city'] ); } ?>" />
30
+ </p>
31
+ <p>
32
+ <label for="wpsl-store-state"><?php _e( 'State / Province:', 'wpsl' ); ?></label>
33
+ <input id="wpsl-store-state" name="wpsl[state]" type="text" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['state'] ) ) { echo esc_attr( $_POST['wpsl']['state'] ); } ?>" />
34
+ </p>
35
+ <p>
36
+ <label for="wpsl-store-zip"><?php _e( 'Zip Code:', 'wpsl' ); ?></label>
37
+ <input id="wpsl-store-zip" name="wpsl[zip]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['zip'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( !empty( $_POST['wpsl']['zip'] ) ) { echo esc_attr( $_POST['wpsl']['zip'] ); } ?>" />
38
+ </p>
39
+ <p>
40
+ <label for="wpsl-store-country"><?php _e( 'Country:', 'wpsl' ); ?></label>
41
+ <input id="wpsl-store-country" name="wpsl[country]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['country'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( !empty( $_POST['wpsl']['country'] ) ) { echo esc_attr( $_POST['wpsl']['country'] ); } ?>" />
42
+ <input id="wpsl-country-iso" type="hidden" name="wpsl[country-iso]" value="<?php if ( !empty( $_POST['wpsl']['country-iso'] ) ) { echo esc_attr( $_POST['wpsl']['country-iso'] ); } ?>" />
43
+ </p>
44
+ <p>
45
+ <label for="wpsl-store-lat"><?php _e( 'Latitude:', 'wpsl' ); ?></label>
46
+ <input id="wpsl-store-lat" name="wpsl[lat]" type="text" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['lat'] ) ) { echo esc_attr( $_POST['wpsl']['lat'] ); } ?>" />
47
+ </p>
48
+ <p>
49
+ <label for="wpsl-store-lng"><?php _e( 'Longitude:', 'wpsl' ); ?></label>
50
+ <input id="wpsl-store-lng" name="wpsl[lng]" type="text" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['lng'] ) ) { echo esc_attr( $_POST['wpsl']['lng'] ); } ?>" />
51
+ </p>
52
+ <p class="wpsl-submit-wrap">
53
+ <input id="wpsl-lookup-location" type="submit" name="wpsl-lookup-location" class="button-primary" value="<?php _e( 'Preview location on the map', 'wpsl' ); ?>" />
54
+ <em class="nwm-desc"><?php _e( 'You can adjust the location by dragging the marker around', 'nwm' ); ?></em>
55
+ </p>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <div id="wpsl-gmap-wrap"></div>
61
+ </div>
62
+
63
+ <div class="metabox-holder">
64
+ <div class="postbox">
65
+ <h3><span><?php _e( 'Extra details', 'wpsl' ); ?></span></h3>
66
+ <div class="inside">
67
+ <p>
68
+ <label for="wpsl-store-phone"><?php _e( 'Phone:', 'wpsl' ); ?></label>
69
+ <input id="wpsl-store-phone" name="wpsl[phone]" type="text" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['phone'] ) ) { echo esc_attr( $_POST['wpsl']['phone'] ); } ?>">
70
+ </p>
71
+ <p>
72
+ <label for="wpsl-store-fax"><?php _e( 'Fax:', 'wpsl' ); ?></label>
73
+ <input id="wpsl-store-fax" name="wpsl[fax]" type="text" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['fax'] ) ) { echo esc_attr( $_POST['wpsl']['fax'] ); } ?>">
74
+ </p>
75
+ <p>
76
+ <label for="wpsl-store-email"><?php _e( 'Email:', 'wpsl' ); ?></label>
77
+ <input id="wpsl-store-email" name="wpsl[email]" type="email" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['email'] ) ) { echo esc_attr( $_POST['wpsl']['email'] ); } ?>">
78
+ </p>
79
+ <p>
80
+ <label for="wpsl-store-url"><?php _e( 'Url:', 'wpsl' ); ?></label>
81
+ <input id="wpsl-store-url" name="wpsl[url]" type="url" class="textinput" value="<?php if ( !empty( $_POST['wpsl']['url'] ) ) { echo esc_attr( $_POST['wpsl']['url'] ); } ?>">
82
+ </p>
83
+ <p>
84
+ <label for="wpsl-store-desc"><?php _e( 'Description:', 'wpsl' ); ?></label>
85
+ <textarea id="wpsl-store-desc" name="wpsl[desc]" cols="5" rows="5"></textarea>
86
+ </p>
87
+ <p>
88
+ <label for="wpsl-store-hours"><?php _e( 'Operation Hours:', 'wpsl' ); ?></label>
89
+ <textarea id="wpsl-store-hours" name="wpsl[hours]" cols="5" rows="5"><?php if ( !empty( $_POST['wpsl']['hours'] ) ) { echo esc_textarea( $_POST['wpsl']['hours'] ); } ?></textarea>
90
+ </p>
91
+ <div id="wpsl-thumb-wrap">
92
+ <p>
93
+ <label><?php _e( 'Thumbnail:', 'wpsl' ); ?></label>
94
+ <span class="wpsl-thumb-placeholder"></span>
95
+ </p>
96
+ <div>
97
+ <input id="wpsl-media-upload" class="button-primary" type="button" name="text" value="<?php _e( 'Change thumbnail', 'wpsl' ); ?>" />
98
+ <input id="wpsl-remove-thumb" class="button-primary" type="button" name="text" value="<?php _e( 'Remove', 'wpsl' ); ?>" />
99
+ <input type="hidden" id="wpsl-thumb-id" name="wpsl[thumb-id]" value="" />
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <p><input id="wpsl-add-store" type="submit" name="wpsl-add-store" class="button-primary" value="<?php _e( 'Add Store', 'wpsl' ); ?>" /></p>
106
+ </div>
107
+ </form>
108
+
109
+ </div>
admin/templates/edit-store.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $store_id = absint( $_GET['store_id'] );
3
+ $thumb = '';
4
+
5
+ if ( $store_id ) {
6
+ $store_details = $this->get_store_data( $store_id );
7
+
8
+ if ( $store_details['thumb_id'] ) {
9
+ $thumb = wp_get_attachment_image_src( $store_details['thumb_id'], 'thumbnail' );
10
+ }
11
+ }
12
+ ?>
13
+ <div id="wpsl-wrap" class="wrap wpsl-add-stores">
14
+ <h2 class="wpsl-edit-header">Edit <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['store'] ) ) { echo esc_attr( $_POST['wpsl']['store'] ); } else { echo esc_attr( $store_details['store'] ); } ?></h2>
15
+ <?php settings_errors(); ?>
16
+
17
+ <form method="post" action="" accept-charset="utf-8">
18
+ <input type="hidden" name="wpsl_actions" value="update_store" />
19
+ <?php wp_nonce_field( 'wpsl_update_store' ); ?>
20
+ <div class="wpsl-add-store">
21
+ <div class="metabox-holder">
22
+ <div class="postbox">
23
+ <h3><span><?php _e( 'Store details', 'wpsl' ); ?></span></h3>
24
+ <div class="inside">
25
+ <p>
26
+ <label for="wpsl-store-status"><?php _e( 'Status:', 'wpsl' ); ?></label>
27
+ <span class="wpsl-radioboxes">
28
+ <input id="wpsl-store-active" type="radio" name="wpsl[active]" <?php checked( '1', $store_details['active'], true ); ?> value="1" />
29
+ <label for="wpsl-store-active"><?php _e( 'active', 'wpsl' ); ?></label>
30
+ <input id="wpsl-store-inactive" type="radio" name="wpsl[active]" <?php checked( '0', $store_details['active'], true ); ?> value="0" />
31
+ <label for="wpsl-store-inactive"><?php _e( 'inactive', 'wpsl' ); ?></label>
32
+ </span>
33
+ </p>
34
+ <p>
35
+ <label for="wpsl-store-name"><?php _e( 'Store Name:', 'wpsl' ); ?></label>
36
+ <input id="wpsl-store-name" name="wpsl[store]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['store'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['store'] ) ) { echo esc_attr( $_POST['wpsl']['store'] ); } else { echo esc_attr( $store_details['store'] ); } ?>" />
37
+ </p>
38
+ <p>
39
+ <label for="wpsl-store-street"><?php _e( 'Street:', 'wpsl' ); ?></label>
40
+ <input id="wpsl-store-street" name="wpsl[street]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['street'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['street'] ) ) { echo esc_attr( $_POST['wpsl']['street'] ); } else { echo esc_attr( $store_details['street'] ); } ?>" />
41
+ </p>
42
+ <p>
43
+ <label for="wpsl-store-city"><?php _e( 'City:', 'wpsl' ); ?></label>
44
+ <input id="wpsl-store-city" name="wpsl[city]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['city'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['city'] ) ) { echo esc_attr( $_POST['wpsl']['city'] ); } else { echo esc_attr( $store_details['city'] ); } ?>" />
45
+ </p>
46
+ <p>
47
+ <label for="wpsl-store-state"><?php _e( 'State / Province:', 'wpsl' ); ?></label>
48
+ <input id="wpsl-store-state" name="wpsl[state]" type="text" class="textinput" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['state'] ) ) { echo esc_attr( $_POST['wpsl']['state'] ); } else { echo esc_attr( $store_details['state'] ); } ?>" />
49
+ </p>
50
+ <p>
51
+ <label for="wpsl-store-zip"><?php _e( 'Zip Code:', 'wpsl' ); ?></label>
52
+ <input id="wpsl-store-zip" name="wpsl[zip]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['zip'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['zip'] ) ) { echo esc_attr( $_POST['wpsl']['zip'] ); } else { echo esc_attr( $store_details['zip'] ); } ?>" />
53
+ </p>
54
+ <p>
55
+ <label for="wpsl-store-country"><?php _e( 'Country:', 'wpsl' ); ?></label>
56
+ <input id="wpsl-store-country" name="wpsl[country]" type="text" class="textinput <?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['country'] ) ) { echo 'wpsl-error'; } ?>" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['country'] ) ) { echo esc_attr( $_POST['wpsl']['country'] ); } else { echo esc_attr( $store_details['country'] ); } ?>" />
57
+ <input id="wpsl-country-iso" type="hidden" name="wpsl[country-iso]" value="<?php echo esc_attr( $store_details['country_iso'] ); ?>" />
58
+ </p>
59
+ <p>
60
+ <label for="wpsl-store-lat"><?php _e( 'Latitude:', 'wpsl' ); ?></label>
61
+ <input id="wpsl-store-lat" name="wpsl[lat]" type="text" class="textinput" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['lat'] ) ) { echo esc_attr( $_POST['wpsl']['lat'] ); } else { echo esc_attr( $store_details['lat'] ); } ?>" />
62
+ </p>
63
+ <p>
64
+ <label for="wpsl-store-lng"><?php _e( 'Longitude:', 'wpsl' ); ?></label>
65
+ <input id="wpsl-store-lng" name="wpsl[lng]" type="text" class="textinput" value="<?php if ( ( $_POST['wpsl'] ) && empty( $_POST['wpsl']['lng'] ) ) { echo esc_attr( $_POST['wpsl']['lng'] ); } else { echo esc_attr( $store_details['lng'] ); } ?>" />
66
+ </p>
67
+ <p class="wpsl-submit-wrap">
68
+ <input id="wpsl-lookup-location" type="submit" name="wpsl-lookup-location" class="button-primary" value="<?php _e( 'Preview location on the map', 'wpsl' ); ?>" />
69
+ <em class="nwm-desc"><?php _e( 'You can adjust the location by dragging the marker around', 'nwm' ); ?></em>
70
+ </p>
71
+ </div>
72
+ </div>
73
+ </div>
74
+
75
+ <div id="wpsl-gmap-wrap"></div>
76
+ </div>
77
+
78
+ <div class="metabox-holder">
79
+ <div class="postbox">
80
+ <h3><span><?php _e( 'Extra details', 'wpsl' ); ?></span></h3>
81
+ <div class="inside">
82
+ <p>
83
+ <label for="wpsl-store-phone"><?php _e( 'Phone:', 'wpsl' ); ?></label>
84
+ <input id="wpsl-store-phone" name="wpsl[phone]" type="text" class="textinput" value="<?php if ( $_POST['wpsl']['phone'] ) { echo esc_attr( $_POST['wpsl']['phone'] ); } else { echo esc_attr( $store_details['phone'] ); } ?>">
85
+ </p>
86
+ <p>
87
+ <label for="wpsl-store-fax"><?php _e( 'Fax:', 'wpsl' ); ?></label>
88
+ <input id="wpsl-store-fax" name="wpsl[fax]" type="text" class="textinput" value="<?php if ( $_POST['wpsl']['fax'] ) { echo esc_attr( $_POST['wpsl']['fax'] ); } else { echo esc_attr( $store_details['fax'] ); } ?>">
89
+ </p>
90
+ <p>
91
+ <label for="wpsl-store-email"><?php _e( 'Email:', 'wpsl' ); ?></label>
92
+ <input id="wpsl-store-email" name="wpsl[email]" type="email" class="textinput" value="<?php if ( $_POST['wpsl']['email'] ) { echo esc_attr( $_POST['wpsl']['email'] ); } else { echo esc_attr( $store_details['email'] ); } ?>">
93
+ </p>
94
+ <p>
95
+ <label for="wpsl-store-url"><?php _e( 'Url:', 'wpsl' ); ?></label>
96
+ <input id="wpsl-store-url" name="wpsl[url]" type="url" class="textinput" value="<?php if ( $_POST['wpsl']['url'] ) { echo esc_attr( $_POST['wpsl']['url'] ); } else { echo esc_attr( $store_details['url'] ); } ?>">
97
+ </p>
98
+ <p>
99
+ <label for="wpsl-store-desc"><?php _e( 'Description:', 'wpsl' ); ?></label>
100
+ <textarea id="wpsl-store-desc" name="wpsl[desc]" cols="5" rows="5"><?php if ( $_POST['wpsl']['desc'] ) { echo esc_textarea( $_POST['wpsl']['desc'] ); } else { echo esc_textarea( $store_details['description'] ); } ?></textarea>
101
+ </p>
102
+ <p>
103
+ <label for="wpsl-store-hours"><?php _e( 'Operation Hours:', 'wpsl' ); ?></label>
104
+ <textarea id="wpsl-store-hours" name="wpsl[hours]" cols="5" rows="5"><?php if ( $_POST['wpsl']['hours'] ) { echo esc_textarea( $_POST['wpsl']['hours'] ); } else { echo esc_textarea( $store_details['hours'] ); } ?></textarea>
105
+ </p>
106
+ <div id="wpsl-thumb-wrap">
107
+ <p>
108
+ <label><?php _e( 'Thumbnail:', 'wpsl' ); ?></label>
109
+ <?php
110
+ if ( !empty( $thumb ) ) {
111
+ echo '<img class="wpsl-curve" src=" ' . esc_url ( $thumb[0] ) . '" width="85" height="85">';
112
+ } else {
113
+ ?>
114
+ <span class="wpsl-thumb-placeholder"></span>
115
+ <?php
116
+ }
117
+ ?>
118
+ </p>
119
+ <div>
120
+ <input id="wpsl-media-upload" class="button-primary" type="button" name="text" value="<?php _e( 'Change thumbnail', 'wpsl' ); ?>" />
121
+ <input id="wpsl-remove-thumb" class="button-primary" type="button" name="text" value="<?php _e( 'Remove', 'wpsl' ); ?>" />
122
+ <input type="hidden" id="wpsl-thumb-id" name="wpsl[thumb-id]" value="<?php echo esc_attr( $store_details['thumb_id'] ); ?>" />
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </div>
127
+
128
+ <p>
129
+ <input id="wpsl-update-store" type="submit" name="wpsl-update-store" class="button-primary" value="<?php _e( 'Update Store', 'wpsl' ); ?>" />
130
+ <input id="wpsl-delete-store" type="submit" name="wpsl-delete-store" class="button-primary" value="<?php _e( 'Delete Store', 'wpsl' ); ?>" />
131
+ </p>
132
+ </div>
133
+ </form>
134
+
135
+ </div>
admin/templates/faq.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <div id="wpsl-wrap" class="wrap wpsl-settings">
2
+ <h2>WP Store Locator: <?php _e( 'FAQ', 'wpsl' ); ?></h2>
3
+
4
+ <div id="wpsl-faq">
5
+ <dl>
6
+ <dt>How do I show the store locator on my page?</dt>
7
+ <dd>Add this shortcode <code>[wpsl]</code> to the page where you want to show the store locator.</dd>
8
+ </dl>
9
+ </div>
10
+ </div>
admin/templates/map-settings.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="wpsl-wrap" class="wrap wpsl-settings">
2
+ <h2>WP Store Locator: <?php _e( 'Settings', 'wpsl' ); ?></h2>
3
+
4
+ <?php global $wpdb; ?>
5
+
6
+ <?php settings_errors(); ?>
7
+ <ul id="wpsl-mainnav" class="nav-tab-wrapper">
8
+ <li><a class="nav-tab" href="<?php echo admin_url( 'admin.php?page=wpsl_store_editor' ); ?>"><?php _e( 'Current Stores', 'wpsl' ); ?></a></li>
9
+ <li><a class="nav-tab" href="<?php echo admin_url( 'admin.php?page=wpsl_add_store' ); ?>"><?php _e( 'Add Store', 'wpsl' ); ?></a></li>
10
+ <li><a class="nav-tab nav-tab-active" href="<?php echo admin_url( 'admin.php?page=wpsl_settings' ); ?>"><?php _e( 'Settings', 'wpsl' ); ?></a></li>
11
+ </ul>
12
+
13
+ <form id="wpsl-settings-form" method="post" action="options.php" accept-charset="utf-8">
14
+ <div class="postbox-container">
15
+ <div class="metabox-holder">
16
+ <div class="postbox">
17
+ <h3><span><?php _e( 'API Settings', 'wpsl' ); ?></span></h3>
18
+ <div class="inside">
19
+ <p>
20
+ <label for="wpsl-api-key"><?php _e( 'API key:', 'wpsl' ); ?> *</label>
21
+ <input type="text" value="<?php echo esc_attr( $this->settings['api_key'] ); ?>" name="wpsl_api[key]" placeholder="<?php _e( 'Optional', 'wpsl' ) ?>" class="textinput" id="wpsl-api-key">
22
+ </p>
23
+ <p>
24
+ <label for="wpsl-api-language"><?php _e( 'Map language:', 'wpsl' ); ?></label>
25
+ <select id="wpsl-api-language" name="wpsl_api[language]">
26
+ <?php
27
+ echo $this->get_api_option_list( 'language' );
28
+ ?>
29
+ </select>
30
+ </p>
31
+ <p>
32
+ <label for="wpsl-api-region"><?php _e( 'Map region:', 'wpsl' ); ?></label>
33
+ <select id="wpsl-api-region" name="wpsl_api[region]">
34
+ <?php
35
+ echo $this->get_api_option_list( 'region' );
36
+ ?>
37
+ </select>
38
+ </p>
39
+ <em><?php _e( '* A valid <a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key">API key</a> allows you to monitor the API usage <br> and is required if you need to purchase additional quota.', 'wpsl' ); ?></em>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="postbox-container">
46
+ <div class="metabox-holder">
47
+ <div class="postbox">
48
+ <h3><span><?php _e( 'Search Settings', 'wpsl' ); ?></span></h3>
49
+ <div class="inside">
50
+ <p>
51
+ <label for="wpsl-distance-unit"><?php _e( 'Distance unit:', 'wpsl' ); ?></label>
52
+ <?php echo $this->show_distance_units(); ?>
53
+ </p>
54
+ <p>
55
+ <label for="wpsl-max-results"><?php _e( 'Max search results:', 'wpsl' ); ?> *</label>
56
+ <input type="text" value="<?php echo esc_attr( $this->settings['max_results'] ); ?>" name="wpsl_search[max_results]" class="textinput" id="wpsl-max-results">
57
+ </p>
58
+ <p>
59
+ <label for="wpsl-search-radius"><?php _e( 'Search radius options:', 'wpsl' ); ?> *</label>
60
+ <input type="text" value="<?php echo esc_attr( $this->settings['search_radius'] ); ?>" name="wpsl_search[radius]" class="textinput" id="wpsl-search-radius">
61
+ </p>
62
+ <p>
63
+ <label for="wpsl-bounce"><?php _e( 'If a user hovers over the search results, the corresponding marker will bounce?', 'wpsl' ); ?></label>
64
+ <input type="checkbox" value="" <?php checked( $this->settings['marker_bounce'] == '1', true ); ?> name="wpsl_map[marker_bounce]" id="wpsl-bounce">
65
+ </p>
66
+ <em><?php _e( '* The default value is set between the ( )', 'wpsl' ); ?></em>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <div class="postbox-container">
73
+ <div class="metabox-holder">
74
+ <div class="postbox">
75
+ <h3><span><?php _e( 'Map Settings', 'wpsl' ); ?></span></h3>
76
+ <div class="inside">
77
+ <p>
78
+ <label for="wpsl-auto-locate"><?php _e( 'Attempt to auto-locate the user:', 'wpsl' ); ?></label>
79
+ <input type="checkbox" value="" <?php checked( $this->settings['auto_locate'] == '1', true ); ?> name="wpsl_map[auto_locate]" id="wpsl-auto-locate">
80
+ </p>
81
+ <p>
82
+ <label for="wpsl-zoom-level"><?php _e( 'Zoom level:', 'wpsl' ); ?></label>
83
+ <?php echo $this->show_zoom_levels(); ?>
84
+ </p>
85
+ <p>
86
+ <label for="wpsl-zoom-name"><?php _e( 'Default location to focus on:', 'wpsl' ); ?></label>
87
+ <input type="text" value="<?php echo esc_attr( $this->settings['zoom_name'] ); ?>" name="wpsl_map[zoom_name]" class="textinput" id="wpsl-zoom-name">
88
+ <input type="hidden" value="<?php echo esc_attr( $this->settings['zoom_latlng'] ); ?>" name="wpsl_map[zoom_latlng]" id="wpsl-latlng" />
89
+ </p>
90
+ <p>
91
+ <label for="wpsl-map-type"><?php _e( 'Map type:', 'wpsl' ); ?></label>
92
+ <?php echo $this->show_map_types(); ?>
93
+ </p>
94
+ <p>
95
+ <label for="wpsl-streetview"><?php _e( 'Show the street view controls?', 'wpsl' ); ?></label>
96
+ <input type="checkbox" value="" <?php checked( $this->settings['streetview'] == '1', true ); ?> name="wpsl_map[streetview]" id="wpsl-streetview">
97
+ </p>
98
+ <p>
99
+ <label for="wpsl-pan-controls"><?php _e( 'Show the pan controls?', 'wpsl' ); ?></label>
100
+ <input type="checkbox" value="" <?php checked( $this->settings['pan_controls'] == '1', true ); ?> name="wpsl_map[pan_controls]" id="wpsl-pan-controls">
101
+ </p>
102
+ <p>
103
+ <label><?php _e( 'Position of the map controls:', 'wpsl' ); ?></label>
104
+ <span class="wpsl_radioboxes">
105
+ <input type="radio" value="left" <?php checked( 'left', $this->settings['control_position'], true ); ?> name="wpsl_map[control_position]" id="wpsl-control-left">
106
+ <label for="wpsl-control-left"><?php _e( 'Left', 'wpsl' ); ?></label>
107
+ <input type="radio" value="right" <?php checked( 'right', $this->settings['control_position'], true ); ?> name="wpsl_map[control_position]" id="wpsl-control-right">
108
+ <label for="wpsl-control-right"><?php _e( 'Right', 'wpsl' ); ?></label>
109
+ </span>
110
+ </p>
111
+ <p>
112
+ <label><?php _e( 'Zoom control style:', 'wpsl' ); ?></label>
113
+ <span class="wpsl_radioboxes">
114
+ <input type="radio" value="small" <?php checked( 'small', $this->settings['control_style'], true ); ?> name="wpsl_map[control_style]" id="wpsl-small-style">
115
+ <label for="wpsl-small-style"><?php _e( 'Small', 'wpsl' ); ?></label>
116
+ <input type="radio" value="large" <?php checked( 'large', $this->settings['control_style'], true ); ?> name="wpsl_map[control_style]" id="wpsl-large-style">
117
+ <label for="wpsl-large-style"><?php _e( 'Large', 'wpsl' ); ?></label>
118
+ </span>
119
+ </p>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ <div class="postbox-container">
126
+ <div class="metabox-holder">
127
+ <div class="postbox">
128
+ <h3><span><?php _e( 'Design Settings', 'wpsl' ); ?></span></h3>
129
+ <div class="inside">
130
+ <p>
131
+ <label for="wpsl-design-height"><?php _e( 'Store Locator height', 'wpsl' ); ?></label>
132
+ <input size="3" value="<?php echo esc_attr( $this->settings['height'] ); ?>" id="wpsl-design-height" name="wpsl_design[height_value]"> px
133
+ </p>
134
+ <p>
135
+ <label for="wpsl-infowindow-width"><?php _e( 'Max width for the infowindow', 'wpsl' ); ?></label>
136
+ <input size="3" value="<?php echo esc_attr( $this->settings['infowindow_width'] ); ?>" id="wpsl-infowindow-width" name="wpsl_design[infowindow_width]"> px
137
+ </p>
138
+ <p>
139
+ <label for="wpsl-search-width"><?php _e( 'Search field width', 'wpsl' ); ?></label>
140
+ <input size="3" value="<?php echo esc_attr( $this->settings['search_width'] ); ?>" id="wpsl-search-width" name="wpsl_design[search_width]"> px
141
+ </p>
142
+ <p>
143
+ <label for="wpsl-label-width"><?php _e( 'Search and radius label width *', 'wpsl' ); ?></label>
144
+ <input size="3" value="<?php echo esc_attr( $this->settings['label_width'] ); ?>" id="wpsl-label-width" name="wpsl_design[label_width]"> px
145
+ </p>
146
+ <p>
147
+ <label for="wpsl-design-results"><?php _e( 'Show the limit results dropdown?', 'wpsl' ); ?></label>
148
+ <input type="checkbox" value="" <?php checked( $this->settings['results_dropdown'] == '1', true ); ?> name="wpsl_design[design_results]">
149
+ </p>
150
+ <em><?php _e( '* This is the text that is placed before the search input and radius dropdown', 'wpsl' ); ?></em>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <div class="postbox-container">
157
+ <div class="metabox-holder">
158
+ <div class="postbox">
159
+ <h3><span><?php _e( 'Markers', 'wpsl' ); ?></span></h3>
160
+ <div class="inside">
161
+ <?php echo $this->show_marker_options(); ?>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <div class="postbox-container">
168
+ <div class="metabox-holder">
169
+ <div class="postbox">
170
+ <h3><span><?php _e( 'Labels', 'wpsl' ); ?></span></h3>
171
+ <div class="inside">
172
+ <p>
173
+ <label for="wpsl-search"><?php _e( 'Your location:', 'wpsl' ); ?></label>
174
+ <input type="text" value="<?php echo esc_attr( $this->settings['search_label'] ); ?>" name="wpsl_label[search]" class="textinput" id="wpsl-search">
175
+ </p>
176
+ <p>
177
+ <label for="wpsl-search-radius"><?php _e( 'Search radius:', 'wpsl' ); ?></label>
178
+ <input type="text" value="<?php echo esc_attr( $this->settings['radius_label'] ); ?>" name="wpsl_label[radius]" class="textinput" id="wpsl-search-radius">
179
+ </p>
180
+ <p>
181
+ <label for="wpsl-no-results"><?php _e( 'No results found:', 'wpsl' ); ?></label>
182
+ <input type="text" value="<?php echo esc_attr( $this->settings['no_results_label'] ); ?>" name="wpsl_label[no_results]" class="textinput" id="wpsl-no-results">
183
+ </p>
184
+ <p>
185
+ <label for="wpsl-search-btn"><?php _e( 'Search:', 'wpsl' ); ?></label>
186
+ <input type="text" value="<?php echo esc_attr( $this->settings['search_btn_label'] ); ?>" name="wpsl_label[search_btn]" class="textinput" id="wpsl-search-btn">
187
+ </p>
188
+ <p>
189
+ <label for="wpsl-preloader"><?php _e( 'Searching (preloader text):', 'wpsl' ); ?></label>
190
+ <input type="text" value="<?php echo esc_attr( $this->settings['preloader_label'] ); ?>" name="wpsl_label[preloader]" class="textinput" id="wpsl-preloader">
191
+ </p>
192
+ <p>
193
+ <label for="wpsl-results"><?php _e( 'Results:', 'wpsl' ); ?></label>
194
+ <input type="text" value="<?php echo esc_attr( $this->settings['results_label'] ); ?>" name="wpsl_label[results]" class="textinput" id="wpsl-results">
195
+ </p>
196
+ <p>
197
+ <label for="wpsl-phone"><?php _e( 'Phone:', 'wpsl' ); ?></label>
198
+ <input type="text" value="<?php echo esc_attr( $this->settings['phone_label'] ); ?>" name="wpsl_label[phone]" class="textinput" id="wpsl-phone">
199
+ </p>
200
+ <p>
201
+ <label for="wpsl-fax"><?php _e( 'Fax:', 'wpsl' ); ?></label>
202
+ <input type="text" value="<?php echo esc_attr( $this->settings['fax_label'] ); ?>" name="wpsl_label[fax]" class="textinput" id="wpsl-fax">
203
+ </p>
204
+ <p>
205
+ <label for="wpsl-hours"><?php _e( 'Hours:', 'wpsl' ); ?></label>
206
+ <input type="text" value="<?php echo esc_attr( $this->settings['hours_label'] ); ?>" name="wpsl_label[hours]" class="textinput" id="wpsl-hours">
207
+ </p>
208
+ <p>
209
+ <label for="wpsl-start"><?php _e( 'Start location:', 'wpsl' ); ?></label>
210
+ <input type="text" value="<?php echo esc_attr( $this->settings['start_label'] ); ?>" name="wpsl_label[start]" class="textinput" id="wpsl-start">
211
+ </p>
212
+ <p>
213
+ <label for="wpsl-directions"><?php _e( 'Get directions:', 'wpsl' ); ?></label>
214
+ <input type="text" value="<?php echo esc_attr( $this->settings['directions_label'] ); ?>" name="wpsl_label[directions]" class="textinput" id="wpsl-directions">
215
+ </p>
216
+ <p>
217
+ <label for="wpsl-error"><?php _e( 'General error:', 'wpsl' ); ?></label>
218
+ <input type="text" value="<?php echo esc_attr( $this->settings['error_label'] ); ?>" name="wpsl_label[error]" class="textinput" id="wpsl-error">
219
+ </p>
220
+ <p>
221
+ <label for="wpsl-limit"><?php _e( 'Query limit error:', 'wpsl' ); ?> *</label>
222
+ <input type="text" value="<?php echo esc_attr( $this->settings['limit_label'] ); ?>" name="wpsl_label[limit]" class="textinput" id="wpsl-limit">
223
+ </p>
224
+ <em><?php _e( '* You can raise the <a href="https://developers.google.com/maps/documentation/javascript/usage#usage_limits">usage limit</a> by obtaining an API <a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key">key</a>, <br> and fill in the "API key" field at the top of this page.', 'wpsl' ); ?></em>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </div>
229
+
230
+ <input type="submit" value="<?php _e( 'Update Settings', 'wpsl' ); ?>" class="button-primary" name="wpsl-save-settings" id="wpsl-save-settings">
231
+ <?php settings_fields( 'wpsl_settings' ); ?>
232
+ </form>
233
+
234
+ </div>
admin/templates/stores-overview.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once WPSL_PLUGIN_DIR . 'admin/class-store-overview.php';
3
+
4
+ $store_overview = new WPSL_Store_Overview();
5
+ $store_overview->prepare_items();
6
+ ?>
7
+
8
+ <div id="wpsl-store-overview" class="wrap">
9
+ <h2>WP Store Locator</h2>
10
+ <?php settings_errors(); ?>
11
+
12
+ <ul id="wpsl-mainnav">
13
+ <li><a class="nav-tab nav-tab-active" href="<?php echo admin_url( 'admin.php?page=wpsl_store_editor' ); ?>"><?php _e( 'Current Stores', 'wpsl' ); ?></a></li>
14
+ <li><a class="nav-tab" href="<?php echo admin_url( 'admin.php?page=wpsl_add_store' ); ?>"><?php _e( 'Add Store', 'wpsl' ); ?></a></li>
15
+ <li><a class="nav-tab" href="<?php echo admin_url( 'admin.php?page=wpsl_settings' ); ?>"><?php _e( 'Settings', 'wpsl' ); ?></a></li>
16
+ </ul>
17
+
18
+ <form method="post">
19
+ <?php
20
+ $store_overview->search_box( 'search', 'search_id' );
21
+ $store_overview->display();
22
+ ?>
23
+ </form>
24
+ </div>
css/styles.css ADDED
@@ -0,0 +1,598 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* The map container */
2
+ #wpsl-gmap {
3
+ float:left;
4
+ width:66.5%;
5
+ height:350px;
6
+ }
7
+
8
+ /*
9
+ Some themes set a box-shadow or max-width for all image /
10
+ div elements, we disable it to prevent it from messing up the map
11
+ */
12
+ #wpsl-gmap div,
13
+ #wpsl-gmap img {
14
+ box-shadow:none;
15
+ max-width:none;
16
+ background:none;
17
+ }
18
+
19
+ #wpsl-wrap {
20
+ width:100%;
21
+ overflow:hidden;
22
+ clear:both;
23
+ }
24
+ #wpsl-search-wrap {
25
+ float:left;
26
+ }
27
+ #wpsl-search-input {
28
+ width:179px;
29
+ padding: 7px 12px;
30
+ font-size: 100%;
31
+ /* padding: 3% 12px;*/
32
+ }
33
+ #wpsl-search-wrap input,
34
+ #wpsl-search-btn {
35
+ border: 1px solid #d2d2d2;
36
+ border-radius: 3px;
37
+ }
38
+
39
+ #wpsl-search-btn {
40
+ padding: 7px 10px;
41
+ line-height: 1.428571429;
42
+ font-weight: normal;
43
+ color: #7c7c7c;
44
+ background-color: #e6e6e6;
45
+ background-repeat: repeat-x;
46
+ background-image: -moz-linear-gradient(top, #f4f4f4, #e6e6e6);
47
+ background-image: -ms-linear-gradient(top, #f4f4f4, #e6e6e6);
48
+ background-image: -webkit-linear-gradient(top, #f4f4f4, #e6e6e6);
49
+ background-image: -o-linear-gradient(top, #f4f4f4, #e6e6e6);
50
+ background-image: linear-gradient(top, #f4f4f4, #e6e6e6);
51
+ box-shadow: 0 1px 2px rgba(64, 64, 64, 0.1);
52
+ text-transform: none !important;
53
+ }
54
+
55
+ .wpsl-search {
56
+ margin-bottom:12px;
57
+ padding:12px 12px 0 12px;
58
+ background:#f4f3f3;
59
+ }
60
+
61
+ /* Result list */
62
+ #wpsl-result-list {
63
+ float:left;
64
+ width:33%;
65
+ margin-right:0.5%;
66
+ }
67
+
68
+ #wpsl-stores,
69
+ #wpsl-direction-details {
70
+ height:350px;
71
+ overflow-y:auto;
72
+ }
73
+ #wpsl-direction-details {display:none;}
74
+
75
+ #wpsl-result-list p {
76
+ padding-left:10px;
77
+ }
78
+ #wpsl-result-list p a {
79
+ display:block;
80
+ margin-bottom:10px;
81
+ }
82
+
83
+ #wpsl-result-list li {
84
+ padding: 10px;
85
+ border-bottom: 1px dotted #ccc;
86
+ margin-left: 0;
87
+ overflow: hidden;
88
+ list-style: none outside none !important
89
+ }
90
+ #wpsl-result-list li p {
91
+ padding-left:0;
92
+ margin-bottom:20px;
93
+ }
94
+ #wpsl-stores .wpsl-store-thumb {
95
+ float:right;
96
+ border-radius:3px;
97
+ margin:7px 0 0 10px;
98
+ width:48px;
99
+ height:48px;
100
+ padding:0;
101
+ border:none;
102
+ }
103
+ .wpsl-direction-index {
104
+ float:left;
105
+ width:8%;
106
+ margin:0 5% 0 0;
107
+ }
108
+ .wpsl-direction-txt {
109
+ float:left;
110
+ width:62%;
111
+ }
112
+ .wpsl-direction-distance {
113
+ float:left;
114
+ width:20%;
115
+ margin:0 0 0 5%;
116
+ }
117
+ .wpsl-direction-txt span {
118
+ display:block;
119
+ margin-top:10px;
120
+ }
121
+
122
+ .wpsl-street,
123
+ .wpsl-country,
124
+ .wpsl-directions {
125
+ display:block;
126
+ border-bottom:none !important;
127
+ }
128
+
129
+ /* Preloader */
130
+ #wpsl-result-list li.wpsl-preloader {
131
+ border-bottom:none;
132
+ }
133
+ .wpsl-preloader img {
134
+ float:left;
135
+ box-shadow:none !important;
136
+ border:none !important;
137
+ }
138
+
139
+ .wpsl-preloader span {
140
+ float:left;
141
+ margin:-5px 0 0 11px;
142
+ }
143
+
144
+ #wpsl-search-wrap div,
145
+ #wpsl-search-btn {
146
+ margin-right:10px;
147
+ float:left;
148
+ /* height:32px;*/
149
+ }
150
+
151
+ #wpsl-radius {margin-right:10px;}
152
+ /*#wpsl-search-wrap #wpsl-results {
153
+ margin-right: 0;
154
+ }*/
155
+ #wpsl-search-btn:hover {
156
+ cursor: pointer;
157
+ }
158
+
159
+ #wpsl-search-wrap select,
160
+ #wpsl-search select {
161
+ display:none;
162
+ }
163
+
164
+ #wpsl-search-wrap div label {
165
+ float:left;
166
+ margin-right:10px;
167
+ /* margin-top: 4px;*/
168
+ line-height: 32px;
169
+ }
170
+
171
+ #wpsl-result-list ul {
172
+ list-style: none;
173
+ margin: 0;
174
+ padding: 0;
175
+ }
176
+ .wpsl-direction-details {
177
+ display: none;
178
+ }
179
+
180
+ /* Infowindow */
181
+ #wpsl-gmap .wpsl-info-window {
182
+ max-width:225px;
183
+ }
184
+ .wpsl-info-window span {
185
+ display:block;
186
+ }
187
+ .wpsl-info-window p {
188
+ margin-bottom:10px;
189
+ }
190
+ .wpsl-store-hours {
191
+ margin-top:10px;
192
+ }
193
+ .wpsl-store-hours strong {
194
+ display:block;
195
+ }
196
+ .wpsl-info-window .wpsl-directions {
197
+ margin:10px 0;
198
+ }
199
+
200
+ /* --- EASYDROPDOWN DEFAULT THEME --- */
201
+ .wpsl-dropdown,
202
+ .wpsl-dropdown div,
203
+ .wpsl-dropdown li,
204
+ .wpsl-dropdown div::after{
205
+ -webkit-transition: all 150ms ease-in-out;
206
+ -moz-transition: all 150ms ease-in-out;
207
+ -ms-transition: all 150ms ease-in-out;
208
+ transition: all 150ms ease-in-out;
209
+ }
210
+
211
+ .wpsl-dropdown .selected::after,
212
+ .wpsl-dropdown.scrollable div::after{
213
+ -webkit-pointer-events: none;
214
+ -moz-pointer-events: none;
215
+ -ms-pointer-events: none;
216
+ pointer-events: none;
217
+ }
218
+
219
+ /* WRAPPER */
220
+ .wpsl-dropdown {
221
+ position: relative;
222
+ width: 90px;
223
+ border: 1px solid #ccc;
224
+ cursor: pointer;
225
+ background: #fff;
226
+ border-radius: 3px;
227
+ -webkit-user-select: none;
228
+ -moz-user-select: none;
229
+ user-select: none;
230
+ margin-right: 0 !important;
231
+ }
232
+
233
+ .wpsl-no-results .wpsl-dropdown {
234
+ width: 114px;
235
+ }
236
+
237
+ .wpsl-dropdown.open{
238
+ z-index: 2;
239
+ }
240
+ #wpsl-search-input:hover,
241
+ .wpsl-dropdown:hover{
242
+ box-shadow: 0 0 5px rgba(0,0,0,.15);
243
+ }
244
+ wpsl-search-input.focus,
245
+ .wpsl-dropdown.focus{
246
+ box-shadow: 0 0 5px rgba(51,102,248,.4);
247
+ }
248
+
249
+ /* CARAT */
250
+ .wpsl-dropdown .carat{
251
+ position: absolute;
252
+ right: 12px;
253
+ top: 50%;
254
+ margin-top: -4px;
255
+ border: 6px solid transparent;
256
+ border-top: 8px solid #000;
257
+ }
258
+
259
+ .wpsl-dropdown.open .carat{
260
+ margin-top: -10px;
261
+ border-top: 6px solid transparent;
262
+ border-bottom: 8px solid #000;
263
+ }
264
+
265
+ .wpsl-dropdown.disabled .carat{
266
+ border-top-color: #999;
267
+ }
268
+
269
+ /* OLD SELECT (HIDDEN) */
270
+ .wpsl-dropdown .old{
271
+ position: absolute;
272
+ left: 0;
273
+ top: 0;
274
+ height: 0;
275
+ width: 0;
276
+ overflow: hidden;
277
+ }
278
+
279
+ .wpsl-dropdown select{
280
+ position: absolute;
281
+ left: 0px;
282
+ top: 0px;
283
+ }
284
+
285
+ .wpsl-dropdown.touch .old{
286
+ width: 100%;
287
+ height: 100%;
288
+ }
289
+
290
+ .wpsl-dropdown.touch select{
291
+ width: 100%;
292
+ height: 100%;
293
+ opacity: 0;
294
+ }
295
+
296
+ /* SELECTED FEEDBACK ITEM */
297
+
298
+ /*select.wpsl-dropdown {display:none;}*/
299
+ #wpsl-search-wrap .wpsl-dropdown div {
300
+ margin-right: 0;
301
+ }
302
+ .wpsl-dropdown .selected,
303
+ .wpsl-dropdown li{
304
+ display: block;
305
+ line-height: 1;
306
+ color: #000;
307
+ padding: 9px 12px;
308
+ overflow: hidden;
309
+ white-space: nowrap;
310
+ }
311
+
312
+ .wpsl-dropdown.disabled .selected{
313
+ color: #999;
314
+ }
315
+
316
+ .wpsl-dropdown .selected::after{
317
+ content: '';
318
+ position: absolute;
319
+ right: 0;
320
+ top: 0;
321
+ bottom: 0;
322
+ width: 60px;
323
+ border-radius: 0 2px 2px 0;
324
+ }
325
+
326
+ /* DROP DOWN WRAPPER */
327
+ .wpsl-dropdown div{
328
+ position: absolute;
329
+ height: 0;
330
+ left: -1px;
331
+ right: -1px;
332
+ top: 100%;
333
+ margin-top: -1px;
334
+ background: #fff;
335
+ border: 1px solid #ccc;
336
+ border-top: 1px solid #eee;
337
+ border-radius: 0 0 3px 3px;
338
+ overflow: hidden;
339
+ opacity: 0;
340
+ }
341
+
342
+ /* Height is adjusted by JS on open */
343
+
344
+ .wpsl-dropdown.open div{
345
+ opacity: 1;
346
+ z-index: 2;
347
+ }
348
+
349
+ /* FADE OVERLAY FOR SCROLLING LISTS */
350
+
351
+ .wpsl-dropdown.scrollable div::after{
352
+ content: '';
353
+ position: absolute;
354
+ left: 0;
355
+ right: 0;
356
+ bottom: 0;
357
+ height: 50px;
358
+ box-shadow: inset 0 -50px 30px -35px #fff;
359
+ }
360
+
361
+ .wpsl-dropdown.scrollable.bottom div::after{
362
+ opacity: 0;
363
+ }
364
+
365
+ /* DROP DOWN LIST */
366
+
367
+ .wpsl-dropdown ul{
368
+ position: absolute;
369
+ left: 0;
370
+ top: 0;
371
+ height: 100%;
372
+ width: 100%;
373
+ list-style: none;
374
+ overflow: hidden;
375
+ padding:0;
376
+ margin:0;
377
+ }
378
+
379
+ .wpsl-dropdown.scrollable.open ul{
380
+ overflow-y: auto;
381
+ }
382
+
383
+ /* DROP DOWN LIST ITEMS */
384
+ .wpsl-dropdown li{
385
+ list-style: none;
386
+ padding: 8px 12px !important;
387
+ margin:0 !important;
388
+ }
389
+
390
+ /* .focus class is also added on hover */
391
+
392
+ .wpsl-dropdown li.focus{
393
+ background: #f8f9f8;
394
+ position: relative;
395
+ z-index: 3;
396
+ color: #000;
397
+ }
398
+
399
+ .wpsl-dropdown li.active{
400
+ font-weight: 700;
401
+ }
402
+
403
+ .clearfix:before,
404
+ .clearfix:after {
405
+ content: " ";
406
+ display: table;
407
+ }
408
+ .clearfix:after {
409
+ clear: both;
410
+ }
411
+
412
+ #wpsl-results .wpsl-dropdown {
413
+ width:70px;
414
+ }
415
+
416
+ .wpsl-input, .wpsl-select-wrap {
417
+ margin-bottom:10px;
418
+ }
419
+
420
+ @media (max-width: 825px) {
421
+ #wpsl-search-input {
422
+ width: 348px;
423
+ }
424
+
425
+ #wpsl-search-wrap .wpsl-dropdown {
426
+ width:114px;
427
+ }
428
+
429
+ #wpsl-search-wrap .wpsl-input {
430
+ width: 100%;
431
+ margin-bottom:10px;
432
+ }
433
+
434
+ .wpsl-no-results #wpsl-search-wrap .wpsl-input,
435
+ .wpsl-no-results #wpsl-search-input,
436
+ .wpsl-no-results .wpsl-input label,
437
+ .wpsl-no-results #wpsl-radius label {
438
+ width: auto;
439
+ }
440
+
441
+ .wpsl-input label,
442
+ #wpsl-radius label {
443
+ min-width: 100px;
444
+ }
445
+ }
446
+
447
+ @media (max-width: 720px) {
448
+ .wpsl-no-results .wpsl-dropdown {
449
+ width: 100px;
450
+ }
451
+
452
+ #wpsl-search-wrap .wpsl-dropdown {
453
+ width: 114px;
454
+ }
455
+
456
+ .wpsl-no-results #wpsl-search-input {
457
+ width: 150px !important;
458
+ }
459
+ }
460
+
461
+ @media (max-width: 700px) {
462
+ .wpsl-no-results #wpsl-search-input {
463
+ width: 250px !important;
464
+ }
465
+
466
+ .wpsl-no-results #wpsl-search-wrap .wpsl-input {
467
+ margin-bottom:10px;
468
+ }
469
+ }
470
+
471
+ @media (max-width: 675px) {
472
+ #wpsl-search-wrap #wpsl-search-btn {
473
+ float:left;
474
+ margin:0 5px 5px 0;
475
+ }
476
+
477
+ .wpsl-dropdown {
478
+ width: 100%;
479
+ }
480
+ .wpsl-search {
481
+ width: 96%;
482
+ padding: 2%;
483
+ }
484
+ .wpsl-input {
485
+ margin-right:0;
486
+ }
487
+
488
+ #wpsl-result-list,
489
+ #wpsl-gmap {
490
+ width:49.75%;
491
+ }
492
+ #wpsl-result-list,
493
+ #wpsl-gmap {
494
+ width:100%;
495
+ }
496
+ #wpsl-gmap {
497
+ margin-top:10px;
498
+ }
499
+
500
+ .wpsl-no-results #wpsl-search-wrap .wpsl-input {
501
+ width:100%;
502
+ }
503
+ .wpsl-no-results #wpsl-search-wrap .wpsl-dropdown {
504
+ width: 201px;
505
+ }
506
+
507
+ .wpsl-input, .wpsl-select-wrap, #wpsl-search-btn {
508
+ margin-bottom:0;
509
+ }
510
+ }
511
+
512
+ @media (max-width: 570px) {
513
+ #wpsl-search-input {
514
+ width: 277px;
515
+ }
516
+ #wpsl-search-wrap #wpsl-search-btn {
517
+ margin-top:15px;
518
+ }
519
+
520
+ .wpsl-no-results #wpsl-search-wrap #wpsl-search-btn {
521
+ margin-top:0;
522
+ }
523
+ }
524
+
525
+ @media (max-width: 520px) {
526
+ .wpsl-search {
527
+ width: 94%;
528
+ padding: 3%;
529
+ }
530
+ #wpsl-search-input {
531
+ width:91%;
532
+ }
533
+ #wpsl-search-wrap div,
534
+ #wpsl-search-btn {
535
+ margin-right: 0;
536
+ }
537
+
538
+ #wpsl-search-wrap div label {
539
+ display:block;
540
+ width:100%;
541
+ }
542
+
543
+ .wpsl-no-results #wpsl-search-wrap div label {
544
+ display:inline;
545
+ width: auto;
546
+ }
547
+ .wpsl-no-results #wpsl-search-input,
548
+ .wpsl-no-results #wpsl-radius,
549
+ #wpsl-results {
550
+ width:auto;
551
+ }
552
+
553
+ .wpsl-select-wrap {
554
+ width:100%;
555
+ }
556
+
557
+ #wpsl-radius, #wpsl-results {
558
+ width:50%;
559
+ }
560
+ #wpsl-radius {
561
+ margin-right: 4%;
562
+ }
563
+ #wpsl-search-wrap .wpsl-dropdown {
564
+ width: 96%;
565
+ }
566
+
567
+ .wpsl-no-results #wpsl-search-wrap #wpsl-search-btn {
568
+ margin-top:15px;
569
+ }
570
+
571
+ #wpsl-search-btn {
572
+ clear: both;
573
+ }
574
+ }
575
+
576
+ @media (max-width: 460px) {
577
+ #wpsl-search-input {
578
+ width:90%;
579
+ }
580
+ .wpsl-no-results #wpsl-search-wrap div label {
581
+ display:block;
582
+ }
583
+ .wpsl-no-results #wpsl-search-wrap .wpsl-dropdown {
584
+ width: 274px;
585
+ }
586
+ }
587
+
588
+ @media (max-width: 375px) {
589
+ #wpsl-search-input {
590
+ width:88%;
591
+ }
592
+ .wpsl-no-results #wpsl-search-input {
593
+ width:200px !important;
594
+ }
595
+ .wpsl-no-results #wpsl-search-wrap .wpsl-dropdown {
596
+ width:223px !important;
597
+ }
598
+ }
frontend/class-frontend.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Frontend class
4
+ *
5
+ * @package WP_Store_locator
6
+ * @subpackage Classes/Frontend
7
+ * @copyright Copyright (c) 2013, Tijmen Smit
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) exit;
13
+
14
+ if ( !class_exists( 'WPSL_Frontend' ) ) {
15
+ /**
16
+ * Handle the frontend of the store locator
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class WPSL_Frontend extends WP_Store_locator {
21
+
22
+ /**
23
+ * Class constructor
24
+ */
25
+ public function __construct() {
26
+ add_shortcode( 'wpsl', array( $this, 'render_store_locator' ) );
27
+ $this->settings = $this->get_settings();
28
+ }
29
+
30
+ /**
31
+ * Load the required front-end template and scripts
32
+ *
33
+ * @since 1.0
34
+ * @return void
35
+ */
36
+ public function render_store_locator() {
37
+ require_once( WPSL_PLUGIN_DIR . 'frontend/templates/default.php' );
38
+ $this->add_frontend_scripts();
39
+ }
40
+
41
+ /**
42
+ * Create the css rules based on the height / max-width that is set on the settings page
43
+ *
44
+ * @since 1.0
45
+ * @return string $css The custom css rules
46
+ */
47
+ public function get_custom_css() {
48
+ $css = '<style>' . "\r\n";
49
+ $css .= "#wpsl-stores, #wpsl-direction-details, #wpsl-gmap {height:" . esc_attr( $this->settings['height'] ) . "px !important;}" . "\r\n";
50
+ $css .= "#wpsl-gmap .wpsl-info-window {max-width:" . esc_attr( $this->settings['infowindow_width'] ) . "px !important;}" . "\r\n";
51
+ $css .= ".wpsl-input label, #wpsl-radius label {width:" . esc_attr( $this->settings['label_width'] ) . "px;}" . "\r\n";
52
+ $css .= "#wpsl-search-input {width:" . esc_attr( $this->settings['search_width'] ) . "px !important;}" . "\r\n";
53
+ $css .= '</style>' . "\r\n";
54
+
55
+ return $css;
56
+ }
57
+
58
+ /**
59
+ * Collect all the attributes (language, key, region)
60
+ * we need before making a request to the Google Maps API
61
+ *
62
+ * @since 1.0
63
+ * @param string The name of the list we need to load data for
64
+ * @return string $dropdown_list A list with the available options for the dropdown list
65
+ */
66
+ public function get_dropdown_list( $list_type ) {
67
+
68
+ $settings = explode( ',', $this->settings[$list_type] );
69
+
70
+ /* Only show the distance unit when we are dealing with the search radius */
71
+ if ( $list_type == 'search_radius' ) {
72
+ $distance_unit = ' '. esc_attr( $this->settings['distance_unit'] );
73
+ } else {
74
+ $distance_unit = '';
75
+ }
76
+
77
+ foreach ( $settings as $k => $setting_value ) {
78
+
79
+ /* The default radius has a () wrapped around it, so we check for that and filter out the () */
80
+ if ( strpos( $setting_value, '(' ) !== false ) {
81
+ $setting_value = filter_var( $setting_value, FILTER_SANITIZE_NUMBER_INT );
82
+ $selected = 'selected="selected"';
83
+ } else {
84
+ $selected = '';
85
+ }
86
+
87
+ $dropdown_list .= '<option ' . $selected . ' value="'. absint( $setting_value ) .'">'. absint( $setting_value ) . $distance_unit .'</option>';
88
+ }
89
+
90
+ return $dropdown_list;
91
+ }
92
+
93
+ /**
94
+ * Collect all the attributes (language, key, region)
95
+ * we need before making a request to the Google Maps API
96
+ *
97
+ * @since 1.0
98
+ * @return string $api_data The collected api attributes
99
+ */
100
+ public function get_gmap_api_attributes() {
101
+
102
+ $api_attributes = array( 'language', 'key', 'region' );
103
+
104
+ foreach ( $api_attributes as $api_key ) {
105
+ if ( !empty( $this->settings['api_'.$api_key] ) ) {
106
+ $api_data .= '&'.$api_key.'=' . $this->settings['api_'.$api_key];
107
+ }
108
+ }
109
+
110
+ return $api_data;
111
+ }
112
+
113
+ /**
114
+ * Create a filename with @2x in it for the selected marker color.
115
+ * So when a user selected green.png in the admin panel. The js on the front-end will end up
116
+ * loading green@2x.png to provide support for retina compatible devices.
117
+ *
118
+ * @since 1.0
119
+ * @param string $filename The name of the seleted marker
120
+ * @return string $filename The filename with @2x added to the end
121
+ */
122
+ public function create_retina_filename( $filename ) {
123
+
124
+ $filename = explode( '.', $filename );
125
+ $filename = $filename[0] . '@2x.' . $filename[1];
126
+
127
+ return $filename;
128
+ }
129
+
130
+ /**
131
+ * Load the front-end scripts and localize the required js data
132
+ *
133
+ * @since 1.0
134
+ * @return void
135
+ */
136
+ public function add_frontend_scripts( ) {
137
+ wp_enqueue_style( 'wpsl-css', WPSL_URL . 'css/styles.css', false );
138
+ wp_enqueue_script( 'wpsl-dropdown', WPSL_URL.'js/jquery.easydropdown.min.js', array( 'jquery' ) ); //not minified version is in the js folder
139
+ wp_enqueue_script( 'wpsl-gmap', ( "//maps.google.com/maps/api/js?sensor=false".$this->get_gmap_api_attributes() ),'' ,'' ,true );
140
+ wp_enqueue_script( 'wpsl-js', WPSL_URL.'js/wpsl-gmap.js', array( 'jquery' ) );
141
+
142
+ $settings = array(
143
+ 'startMarker' => $this->create_retina_filename( $this->settings['start_marker'] ),
144
+ 'storeMarker' => $this->create_retina_filename( $this->settings['store_marker'] ),
145
+ 'autoLocate' => $this->settings['auto_locate'],
146
+ 'mapType' => $this->settings['map_type'],
147
+ 'zoomLevel' => $this->settings['zoom_level'],
148
+ 'zoomLatlng' => $this->settings['zoom_latlng'],
149
+ 'streetView' => $this->settings['streetview'],
150
+ 'panControls' => $this->settings['pan_controls'],
151
+ 'controlPosition' => $this->settings['control_position'],
152
+ 'controlStyle' => $this->settings['control_style'],
153
+ 'markerBounce' => $this->settings['marker_bounce'],
154
+ 'distanceUnit' => ucfirst( $this->settings['distance_unit'] ),
155
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
156
+ 'path' => WPSL_URL,
157
+ );
158
+
159
+ $labels = array(
160
+ 'preloader' => $this->settings['preloader_label'],
161
+ 'noResults' => $this->settings['no_results_label'],
162
+ 'generalError' => $this->settings['error_label'],
163
+ 'queryLimit' => $this->settings['limit_label'],
164
+ 'directions' => $this->settings['directions_label'],
165
+ 'phone' => $this->settings['phone_label'],
166
+ 'fax' => $this->settings['fax_label'],
167
+ 'hours' => $this->settings['hours_label'],
168
+ 'startPoint' => $this->settings['start_label']
169
+ );
170
+
171
+ wp_localize_script( 'wpsl-js', 'wpslSettings', $settings );
172
+ wp_localize_script( 'wpsl-js', 'wpslLabels', $labels );
173
+ }
174
+
175
+ }
176
+
177
+ new WPSL_Frontend;
178
+
179
+ }
frontend/templates/default.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo $this->get_custom_css();
3
+
4
+ $show_results_filter = $this->settings['results_dropdown'];
5
+ $results_filter_class = ( $show_results_filter ) ? '' : 'wpsl-no-results';
6
+ ?>
7
+
8
+ <div id="wpsl-wrap">
9
+ <div class="wpsl-search clearfix <?php echo $results_filter_class; ?>">
10
+ <div id="wpsl-search-wrap">
11
+ <div class="wpsl-input">
12
+ <label for="wpsl-search-input"><?php echo esc_attr( $this->settings['search_label'] ); ?></label>
13
+ <input autocomplete="off" id="wpsl-search-input" type="text" value="" name="wpsl-search-input" />
14
+ </div>
15
+ <div class="wpsl-select-wrap">
16
+ <div id="wpsl-radius">
17
+ <label for="wpsl-radius"><?php echo esc_attr( $this->settings['radius_label'] ); ?></label>
18
+ <select autocomplete="off" class="wpsl-dropdown" name="wpsl-radius">
19
+ <?php echo $this->get_dropdown_list( 'search_radius' ); ?>
20
+ </select>
21
+ </div>
22
+ <?php if ( $show_results_filter ) { ?>
23
+ <div id="wpsl-results">
24
+ <label for="wpsl-results"><?php echo esc_attr( $this->settings['results_label'] ); ?></label>
25
+ <select autocomplete="off" class="wpsl-dropdown" name="wpsl-results">
26
+ <?php echo $this->get_dropdown_list( 'max_results' ); ?>
27
+ </select>
28
+ </div>
29
+ <?php } ?>
30
+ <input id="wpsl-search-btn" type="submit" value="<?php echo esc_attr( $this->settings['search_btn_label'] ); ?>" />
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <div id="wpsl-result-list">
36
+ <div id="wpsl-stores">
37
+ <ul></ul>
38
+ </div>
39
+ <div id="wpsl-direction-details">
40
+ <ul></ul>
41
+ </div>
42
+ </div>
43
+
44
+ <div id="wpsl-gmap"></div>
45
+ </div>
frontend/wpsl-ajax-functions.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ add_action( 'wp_ajax_store_search', 'wpsl_store_search' );
3
+ add_action( 'wp_ajax_nopriv_store_search', 'wpsl_store_search' );
4
+
5
+ /**
6
+ * Handle the ajax store search on the frontend.
7
+ *
8
+ * Search for stores that fall within the selected distance range.
9
+ * This happens by calculating the distance between the latlng of the starting point
10
+ * and the latlng from the stores.
11
+ *
12
+ * @since 1.0
13
+ * @return json The list of stores in $store_results that matches with the request or false if the query returns no results
14
+ */
15
+ function wpsl_store_search() {
16
+
17
+ global $wpdb;
18
+
19
+ $options = get_option( 'wpsl_settings' );
20
+ $distance_unit = ( $options['distance_unit'] == 'km' ) ? '6371' : '3959';
21
+ $allowed_html = '';
22
+
23
+ /* If no max results is set, we get the default value from the settings.
24
+ * The only situation when it can be empty, is when the "Show the limit results dropdown"
25
+ * checkbox is unchecked on the settings page.
26
+ */
27
+ if ( !$_GET['max_results'] ) {
28
+ $max_results = get_default_list_value( $type = 'max_results' );
29
+ } else {
30
+ $max_results = $_GET['max_results'];
31
+ }
32
+
33
+ $result = $wpdb->get_results(
34
+ $wpdb->prepare(
35
+ "
36
+ SELECT *, ( $distance_unit * acos( cos( radians( %s ) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians( %s ) ) + sin( radians( %s ) ) * sin( radians( lat ) ) ) )
37
+ AS distance FROM $wpdb->wpsl_stores
38
+ WHERE active = 1
39
+ HAVING distance < %d
40
+ ORDER BY distance LIMIT 0 ,%d
41
+ ",
42
+ $_GET['lat'],
43
+ $_GET['lng'],
44
+ $_GET['lat'],
45
+ $_GET['radius'],
46
+ $max_results
47
+ )
48
+ );
49
+
50
+ if ( $result === false ) {
51
+ wp_send_json_error();
52
+ } else {
53
+ $store_results = array();
54
+
55
+ foreach ( $result as $k => $v ) {
56
+ /* If we have a valid thumb id, get the src */
57
+ if ( absint ( $result[$k]->thumb_id ) ) {
58
+ $thumb_src = wp_get_attachment_image_src( $result[$k]->thumb_id );
59
+ $result[$k]->thumb_src = $thumb_src[0];
60
+ } else {
61
+ $result[$k]->thumb_src = '';
62
+ }
63
+
64
+ /* Sanitize the results before they are returned */
65
+ $store_results[] = array (
66
+ 'id' => wp_kses( $result[$k]->wpsl_id, $allowed_html ),
67
+ 'store' => wp_kses( $result[$k]->store, $allowed_html ),
68
+ 'street' => wp_kses( $result[$k]->street, $allowed_html ),
69
+ 'city' => wp_kses( $result[$k]->city, $allowed_html ),
70
+ 'state' => wp_kses( $result[$k]->state, $allowed_html ),
71
+ 'zip' => wp_kses( $result[$k]->zip, $allowed_html ),
72
+ 'country' => wp_kses( $result[$k]->country, $allowed_html ),
73
+ 'distance' => wp_kses( $result[$k]->distance, $allowed_html ),
74
+ 'lat' => wp_kses( $result[$k]->lat, $allowed_html ),
75
+ 'lng' => wp_kses( $result[$k]->lng, $allowed_html ),
76
+ 'description' => wpautop( wp_kses( $result[$k]->description, $allowed_html ) ),
77
+ 'phone' => wp_kses( $result[$k]->phone, $allowed_html ),
78
+ 'fax' => wp_kses( $result[$k]->fax, $allowed_html ),
79
+ 'email' => wp_kses( $result[$k]->email, $allowed_html ),
80
+ 'hours' => wpautop( wp_kses( $result[$k]->hours, $allowed_html ) ),
81
+ 'url' => esc_url( $result[$k]->url, $allowed_html ),
82
+ 'thumb' => esc_url( $result[$k]->thumb_src, $allowed_html )
83
+ );
84
+ }
85
+
86
+ wp_send_json( $store_results );
87
+ }
88
+
89
+ die();
90
+ }
91
+
92
+ /**
93
+ * Get the default selected value for a dropdown
94
+ *
95
+ * @since 1.0
96
+ * @param string $type The request list type
97
+ * @return string $response The default list value
98
+ */
99
+ function get_default_list_value( $type ) {
100
+
101
+ $settings = get_option( 'wpsl_settings' );
102
+ $list_values = explode( ',', $settings[$type] );
103
+
104
+ foreach ( $list_values as $k => $list_value ) {
105
+
106
+ /* The default radius has a () wrapped around it, so we check for that and filter out the () */
107
+ if ( strpos( $list_value, '(' ) !== false ) {
108
+ $response = filter_var( $list_value, FILTER_SANITIZE_NUMBER_INT );
109
+ break;
110
+ }
111
+ }
112
+
113
+ return $response;
114
+ }
img/ajax-loader.gif ADDED
Binary file
img/markers/blue.png ADDED
Binary file
img/markers/blue@2x.png ADDED
Binary file
img/markers/dark-blue.png ADDED
Binary file
img/markers/dark-blue@2x.png ADDED
Binary file
img/markers/dark-green.png ADDED
Binary file
img/markers/dark-green@2x.png ADDED
Binary file
img/markers/dark-orange.png ADDED
Binary file
img/markers/dark-orange@2x.png ADDED
Binary file
img/markers/green.png ADDED
Binary file
img/markers/green@2x.png ADDED
Binary file
img/markers/orange.png ADDED
Binary file
img/markers/orange@2x.png ADDED
Binary file
img/markers/pink.png ADDED
Binary file
img/markers/pink@2x.png ADDED
Binary file
img/markers/purple.png ADDED
Binary file
img/markers/purple@2x.png ADDED
Binary file
img/markers/red.png ADDED
Binary file
img/markers/red@2x.png ADDED
Binary file
img/store-locator-icon.png ADDED
Binary file
js/jquery.easydropdown.js ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * EASYDROPDOWN - A Drop-down Builder for Styleable Inputs and Menus
3
+ * Version: 2.1.3
4
+ * License: Creative Commons Attribution 3.0 Unported - CC BY 3.0
5
+ * http://creativecommons.org/licenses/by/3.0/
6
+ * This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder.
7
+ * Author: Patrick Kunka
8
+ * Copyright 2013 Patrick Kunka, All Rights Reserved
9
+ */
10
+
11
+
12
+ (function($){
13
+
14
+ function EasyDropDown(){
15
+ this.isField = true,
16
+ this.down = false,
17
+ this.inFocus = false,
18
+ this.disabled = false,
19
+ this.cutOff = false,
20
+ this.hasLabel = false,
21
+ this.keyboardMode = false,
22
+ this.nativeTouch = true,
23
+ this.wrapperClass = 'dropdown',
24
+ this.onChange = null;
25
+ };
26
+
27
+ EasyDropDown.prototype = {
28
+ constructor: EasyDropDown,
29
+ instances: [],
30
+ init: function(domNode, settings){
31
+ var self = this;
32
+
33
+ $.extend(self, settings);
34
+ self.$select = $(domNode);
35
+ self.id = domNode.id;
36
+ self.options = [];
37
+ self.$options = self.$select.find('option');
38
+ self.isTouch = 'ontouchend' in document;
39
+ self.$select.removeClass(self.wrapperClass+' dropdown');
40
+ if(self.$select.is(':disabled')){
41
+ self.disabled = true;
42
+ };
43
+ if(self.$options.length){
44
+ self.$options.each(function(i){
45
+ var $option = $(this);
46
+ if($option.is(':selected')){
47
+ self.selected = {
48
+ index: i,
49
+ title: $option.text()
50
+ }
51
+ self.focusIndex = i;
52
+ };
53
+ if($option.hasClass('label') && i == 0){
54
+ self.hasLabel = true;
55
+ self.label = $option.text();
56
+ $option.attr('value','');
57
+ } else {
58
+ self.options.push({
59
+ domNode: $option[0],
60
+ title: $option.text(),
61
+ value: $option.val(),
62
+ selected: $option.is(':selected')
63
+ });
64
+ };
65
+ });
66
+ if(!self.selected){
67
+ self.selected = {
68
+ index: 0,
69
+ title: self.$options.eq(0).text()
70
+ }
71
+ self.focusIndex = 0;
72
+ };
73
+ self.render();
74
+ };
75
+ },
76
+
77
+ render: function(){
78
+ var self = this,
79
+ touchClass = self.isTouch && self.nativeTouch ? ' touch' : '',
80
+ disabledClass = self.disabled ? ' disabled' : '';
81
+
82
+ self.$container = self.$select.wrap('<div class="'+self.wrapperClass+touchClass+disabledClass+'"><span class="old"/></div>').parent().parent();
83
+ self.$active = $('<span class="selected">'+self.selected.title+'</span>').appendTo(self.$container);
84
+ self.$carat = $('<span class="carat"/>').appendTo(self.$container);
85
+ self.$scrollWrapper = $('<div><ul/></div>').appendTo(self.$container);
86
+ self.$dropDown = self.$scrollWrapper.find('ul');
87
+ self.$form = self.$container.closest('form');
88
+ $.each(self.options, function(){
89
+ var option = this,
90
+ active = option.selected ? ' class="active"':'';
91
+ self.$dropDown.append('<li'+active+'>'+option.title+'</li>');
92
+ });
93
+ self.$items = self.$dropDown.find('li');
94
+ self.maxHeight = 0;
95
+ if(self.cutOff && self.$items.length > self.cutOff)self.$container.addClass('scrollable');
96
+ for(i = 0; i < self.$items.length; i++){
97
+ var $item = self.$items.eq(i);
98
+ self.maxHeight += $item.outerHeight();
99
+ if(self.cutOff == i+1){
100
+ break;
101
+ };
102
+ };
103
+
104
+ if(self.isTouch && self.nativeTouch){
105
+ self.bindTouchHandlers();
106
+ } else {
107
+ self.bindHandlers();
108
+ };
109
+ },
110
+
111
+ bindTouchHandlers: function(){
112
+ var self = this;
113
+ self.$container.on('click.easyDropDown',function(){
114
+ self.$select.focus();
115
+ });
116
+ self.$select.on({
117
+ change: function(){
118
+ var $selected = $(this).find('option:selected'),
119
+ title = $selected.text(),
120
+ value = $selected.val();
121
+
122
+ self.$active.text(title);
123
+ if(typeof self.onChange === 'function'){
124
+ self.onChange.call(self.$select[0],{
125
+ title: title,
126
+ value: value
127
+ });
128
+ };
129
+ },
130
+ focus: function(){
131
+ self.$container.addClass('focus');
132
+ },
133
+ blur: function(){
134
+ self.$container.removeClass('focus');
135
+ }
136
+ });
137
+ },
138
+
139
+ bindHandlers: function(){
140
+ var self = this;
141
+ self.query = '';
142
+ self.$container.on({
143
+ 'click.easyDropDown': function(){
144
+ if(!self.down && !self.disabled){
145
+ self.open();
146
+ } else {
147
+ self.close();
148
+ };
149
+ },
150
+ 'mousemove.easyDropDown': function(){
151
+ if(self.keyboardMode){
152
+ self.keyboardMode = false;
153
+ };
154
+ }
155
+ });
156
+
157
+ $('body').on('click.easyDropDown.'+self.id,function(e){
158
+ var $target = $(e.target),
159
+ classNames = self.wrapperClass.split(' ').join('.');
160
+
161
+ if(!$target.closest('.'+classNames).length && self.down){
162
+ self.close();
163
+ };
164
+ });
165
+
166
+ self.$items.on({
167
+ 'click.easyDropDown': function(){
168
+ var index = $(this).index();
169
+ self.select(index);
170
+ self.$select.focus();
171
+ },
172
+ 'mouseover.easyDropDown': function(){
173
+ if(!self.keyboardMode){
174
+ var $t = $(this);
175
+ $t.addClass('focus').siblings().removeClass('focus');
176
+ self.focusIndex = $t.index();
177
+ };
178
+ },
179
+ 'mouseout.easyDropDown': function(){
180
+ if(!self.keyboardMode){
181
+ $(this).removeClass('focus');
182
+ };
183
+ }
184
+ });
185
+
186
+ self.$select.on({
187
+ 'focus.easyDropDown': function(){
188
+ self.$container.addClass('focus');
189
+ self.inFocus = true;
190
+ },
191
+ 'blur.easyDropDown': function(){
192
+ self.$container.removeClass('focus');
193
+ self.inFocus = false;
194
+ },
195
+ 'keydown.easyDropDown': function(e){
196
+ if(self.inFocus){
197
+ self.keyboardMode = true;
198
+ var key = e.keyCode;
199
+
200
+ if(key == 38 || key == 40 || key == 32){
201
+ e.preventDefault();
202
+ if(key == 38){
203
+ self.focusIndex--
204
+ self.focusIndex = self.focusIndex < 0 ? self.$items.length - 1 : self.focusIndex;
205
+ } else if(key == 40){
206
+ self.focusIndex++
207
+ self.focusIndex = self.focusIndex > self.$items.length - 1 ? 0 : self.focusIndex;
208
+ };
209
+ if(!self.down){
210
+ self.open();
211
+ };
212
+ self.$items.removeClass('focus').eq(self.focusIndex).addClass('focus');
213
+ if(self.cutOff){
214
+ self.scrollToView();
215
+ };
216
+ self.query = '';
217
+ };
218
+ if(self.down){
219
+ if(key == 9 || key == 27){
220
+ self.close();
221
+ } else if(key == 13){
222
+ e.preventDefault();
223
+ self.select(self.focusIndex);
224
+ self.close();
225
+ return false;
226
+ } else if(key == 8){
227
+ e.preventDefault();
228
+ self.query = self.query.slice(0,-1);
229
+ self.search();
230
+ clearTimeout(self.resetQuery);
231
+ return false;
232
+ } else if(key != 38 && key != 40){
233
+ var letter = String.fromCharCode(key);
234
+ self.query += letter;
235
+ self.search();
236
+ clearTimeout(self.resetQuery);
237
+ };
238
+ };
239
+ };
240
+ },
241
+ 'keyup.easyDropDown': function(){
242
+ self.resetQuery = setTimeout(function(){
243
+ self.query = '';
244
+ },1200);
245
+ }
246
+ });
247
+
248
+ self.$dropDown.on('scroll.easyDropDown',function(e){
249
+ if(self.$dropDown[0].scrollTop >= self.$dropDown[0].scrollHeight - self.maxHeight){
250
+ self.$container.addClass('bottom');
251
+ } else {
252
+ self.$container.removeClass('bottom');
253
+ };
254
+ });
255
+
256
+ if(self.$form.length){
257
+ self.$form.on('reset.easyDropDown', function(){
258
+ var active = self.hasLabel ? self.label : self.options[0].title;
259
+ self.$active.text(active);
260
+ });
261
+ };
262
+ },
263
+
264
+ unbindHandlers: function(){
265
+ var self = this;
266
+
267
+ self.$container
268
+ .add(self.$select)
269
+ .add(self.$items)
270
+ .add(self.$form)
271
+ .add(self.$dropDown)
272
+ .off('.easyDropDown');
273
+ $('body').off('.'+self.id);
274
+ },
275
+
276
+ open: function(){
277
+ var self = this,
278
+ scrollTop = window.scrollY || document.documentElement.scrollTop,
279
+ scrollLeft = window.scrollX || document.documentElement.scrollLeft,
280
+ scrollOffset = self.notInViewport(scrollTop);
281
+
282
+ self.closeAll();
283
+ self.$select.focus();
284
+ window.scrollTo(scrollLeft, scrollTop+scrollOffset);
285
+ self.$container.addClass('open');
286
+ self.$scrollWrapper.css('height',self.maxHeight+'px');
287
+ self.down = true;
288
+ },
289
+
290
+ close: function(){
291
+ var self = this;
292
+ self.$container.removeClass('open');
293
+ self.$scrollWrapper.css('height','0px');
294
+ self.focusIndex = self.selected.index;
295
+ self.query = '';
296
+ self.down = false;
297
+ },
298
+
299
+ closeAll: function(){
300
+ var self = this,
301
+ instances = Object.getPrototypeOf(self).instances;
302
+ for(var key in instances){
303
+ var instance = instances[key];
304
+ instance.close();
305
+ };
306
+ },
307
+
308
+ select: function(index){
309
+ var self = this;
310
+
311
+ if(typeof index === 'string'){
312
+ index = self.$select.find('option[value='+index+']').index() - 1;
313
+ };
314
+
315
+ var option = self.options[index],
316
+ selectIndex = self.hasLabel ? index + 1 : index;
317
+ self.$items.removeClass('active').eq(index).addClass('active');
318
+ self.$active.text(option.title);
319
+ self.$select
320
+ .find('option')
321
+ .removeAttr('selected')
322
+ .eq(selectIndex)
323
+ .prop('selected',true)
324
+ .parent()
325
+ .trigger('change');
326
+
327
+ self.selected = {
328
+ index: index,
329
+ title: option.title
330
+ };
331
+ self.focusIndex = i;
332
+ if(typeof self.onChange === 'function'){
333
+ self.onChange.call(self.$select[0],{
334
+ title: option.title,
335
+ value: option.value
336
+ });
337
+ };
338
+ },
339
+
340
+ search: function(){
341
+ var self = this,
342
+ lock = function(i){
343
+ self.focusIndex = i;
344
+ self.$items.removeClass('focus').eq(self.focusIndex).addClass('focus');
345
+ self.scrollToView();
346
+ },
347
+ getTitle = function(i){
348
+ return self.options[i].title.toUpperCase();
349
+ };
350
+
351
+ for(i = 0; i < self.options.length; i++){
352
+ var title = getTitle(i);
353
+ if(title.indexOf(self.query) == 0){
354
+ lock(i);
355
+ return;
356
+ };
357
+ };
358
+
359
+ for(i = 0; i < self.options.length; i++){
360
+ var title = getTitle(i);
361
+ if(title.indexOf(self.query) > -1){
362
+ lock(i);
363
+ break;
364
+ };
365
+ };
366
+ },
367
+
368
+ scrollToView: function(){
369
+ var self = this;
370
+ if(self.focusIndex >= self.cutOff){
371
+ var $focusItem = self.$items.eq(self.focusIndex),
372
+ scroll = ($focusItem.outerHeight() * (self.focusIndex + 1)) - self.maxHeight;
373
+
374
+ self.$dropDown.scrollTop(scroll);
375
+ };
376
+ },
377
+
378
+ notInViewport: function(scrollTop){
379
+ var self = this,
380
+ range = {
381
+ min: scrollTop,
382
+ max: scrollTop + (window.innerHeight || document.documentElement.clientHeight)
383
+ },
384
+ menuBottom = self.$dropDown.offset().top + self.maxHeight;
385
+
386
+ if(menuBottom >= range.min && menuBottom <= range.max){
387
+ return 0;
388
+ } else {
389
+ return (menuBottom - range.max) + 5;
390
+ };
391
+ },
392
+
393
+ destroy: function(){
394
+ var self = this;
395
+ self.unbindHandlers();
396
+ self.$select.unwrap().siblings().remove();
397
+ self.$select.unwrap();
398
+ delete Object.getPrototypeOf(self).instances[self.$select[0].id];
399
+ },
400
+
401
+ disable: function(){
402
+ var self = this;
403
+ self.disabled = true;
404
+ self.$container.addClass('disabled');
405
+ self.$select.attr('disabled',true);
406
+ if(!self.down)self.close();
407
+ },
408
+
409
+ enable: function(){
410
+ var self = this;
411
+ self.disabled = false;
412
+ self.$container.removeClass('disabled');
413
+ self.$select.attr('disabled',false);
414
+ }
415
+ };
416
+
417
+ var instantiate = function(domNode, settings){
418
+ domNode.id = !domNode.id ? 'EasyDropDown'+rand() : domNode.id;
419
+ var instance = new EasyDropDown();
420
+ if(!instance.instances[domNode.id]){
421
+ instance.instances[domNode.id] = instance;
422
+ instance.init(domNode, settings);
423
+ };
424
+ },
425
+ rand = function(){
426
+ return ('00000'+(Math.random()*16777216<<0).toString(16)).substr(-6).toUpperCase();
427
+ };
428
+
429
+ $.fn.easyDropDown = function(){
430
+ var args = arguments,
431
+ dataReturn = [],
432
+ eachReturn;
433
+
434
+ eachReturn = this.each(function(){
435
+ if(args && typeof args[0] === 'string'){
436
+ var data = EasyDropDown.prototype.instances[this.id][args[0]](args[1], args[2]);
437
+ if(data)dataReturn.push(data);
438
+ } else {
439
+ instantiate(this, args[0]);
440
+ };
441
+ });
442
+
443
+ if(dataReturn.length){
444
+ return dataReturn.length > 1 ? dataReturn : dataReturn[0];
445
+ } else {
446
+ return eachReturn;
447
+ };
448
+ };
449
+
450
+ $(function(){
451
+ if(typeof Object.getPrototypeOf !== 'function'){
452
+ if(typeof 'test'.__proto__ === 'object'){
453
+ Object.getPrototypeOf = function(object){
454
+ return object.__proto__;
455
+ };
456
+ } else {
457
+ Object.getPrototypeOf = function(object){
458
+ return object.constructor.prototype;
459
+ };
460
+ };
461
+ };
462
+
463
+ $('select.dropdown').each(function(){
464
+ var json = $(this).attr('data-settings');
465
+ settings = json ? $.parseJSON(json) : {};
466
+ instantiate(this, settings);
467
+ });
468
+ });
469
+ })(jQuery);
js/jquery.easydropdown.min.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * EASYDROPDOWN - A Drop-down Builder for Styleable Inputs and Menus
3
+ * Version: 2.1.3
4
+ * License: Creative Commons Attribution 3.0 Unported - CC BY 3.0
5
+ * http://creativecommons.org/licenses/by/3.0/
6
+ * This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder.
7
+ * Author: Patrick Kunka
8
+ * Copyright 2013 Patrick Kunka, All Rights Reserved
9
+ */
10
+
11
+ (function(d){function e(){this.isField=!0;this.keyboardMode=this.hasLabel=this.cutOff=this.disabled=this.inFocus=this.down=!1;this.nativeTouch=!0;this.wrapperClass="dropdown";this.onChange=null}e.prototype={constructor:e,instances:[],init:function(a,c){var b=this;d.extend(b,c);b.$select=d(a);b.id=a.id;b.options=[];b.$options=b.$select.find("option");b.isTouch="ontouchend"in document;b.$select.removeClass(b.wrapperClass+" dropdown");b.$select.is(":disabled")&&(b.disabled=!0);b.$options.length&&(b.$options.each(function(a){var c=
12
+ d(this);c.is(":selected")&&(b.selected={index:a,title:c.text()},b.focusIndex=a);c.hasClass("label")&&0==a?(b.hasLabel=!0,b.label=c.text(),c.attr("value","")):b.options.push({domNode:c[0],title:c.text(),value:c.val(),selected:c.is(":selected")})}),b.selected||(b.selected={index:0,title:b.$options.eq(0).text()},b.focusIndex=0),b.render())},render:function(){var a=this;a.$container=a.$select.wrap('<div class="'+a.wrapperClass+(a.isTouch&&a.nativeTouch?" touch":"")+(a.disabled?" disabled":"")+'"><span class="old"/></div>').parent().parent();
13
+ a.$active=d('<span class="selected">'+a.selected.title+"</span>").appendTo(a.$container);a.$carat=d('<span class="carat"/>').appendTo(a.$container);a.$scrollWrapper=d("<div><ul/></div>").appendTo(a.$container);a.$dropDown=a.$scrollWrapper.find("ul");a.$form=a.$container.closest("form");d.each(a.options,function(){a.$dropDown.append("<li"+(this.selected?' class="active"':"")+">"+this.title+"</li>")});a.$items=a.$dropDown.find("li");a.maxHeight=0;a.cutOff&&a.$items.length>a.cutOff&&a.$container.addClass("scrollable");
14
+ for(i=0;i<a.$items.length;i++){var c=a.$items.eq(i);a.maxHeight+=c.outerHeight();if(a.cutOff==i+1)break}a.isTouch&&a.nativeTouch?a.bindTouchHandlers():a.bindHandlers()},bindTouchHandlers:function(){var a=this;a.$container.on("click.easyDropDown",function(){a.$select.focus()});a.$select.on({change:function(){var c=d(this).find("option:selected"),b=c.text(),c=c.val();a.$active.text(b);"function"===typeof a.onChange&&a.onChange.call(a.$select[0],{title:b,value:c})},focus:function(){a.$container.addClass("focus")},
15
+ blur:function(){a.$container.removeClass("focus")}})},bindHandlers:function(){var a=this;a.query="";a.$container.on({"click.easyDropDown":function(){a.down||a.disabled?a.close():a.open()},"mousemove.easyDropDown":function(){a.keyboardMode&&(a.keyboardMode=!1)}});d("body").on("click.easyDropDown."+a.id,function(c){c=d(c.target);var b=a.wrapperClass.split(" ").join(".");!c.closest("."+b).length&&a.down&&a.close()});a.$items.on({"click.easyDropDown":function(){var c=d(this).index();a.select(c);a.$select.focus()},
16
+ "mouseover.easyDropDown":function(){if(!a.keyboardMode){var c=d(this);c.addClass("focus").siblings().removeClass("focus");a.focusIndex=c.index()}},"mouseout.easyDropDown":function(){a.keyboardMode||d(this).removeClass("focus")}});a.$select.on({"focus.easyDropDown":function(){a.$container.addClass("focus");a.inFocus=!0},"blur.easyDropDown":function(){a.$container.removeClass("focus");a.inFocus=!1},"keydown.easyDropDown":function(c){if(a.inFocus){a.keyboardMode=!0;var b=c.keyCode;if(38==b||40==b||32==
17
+ b)c.preventDefault(),38==b?(a.focusIndex--,a.focusIndex=0>a.focusIndex?a.$items.length-1:a.focusIndex):40==b&&(a.focusIndex++,a.focusIndex=a.focusIndex>a.$items.length-1?0:a.focusIndex),a.down||a.open(),a.$items.removeClass("focus").eq(a.focusIndex).addClass("focus"),a.cutOff&&a.scrollToView(),a.query="";if(a.down)if(9==b||27==b)a.close();else{if(13==b)return c.preventDefault(),a.select(a.focusIndex),a.close(),!1;if(8==b)return c.preventDefault(),a.query=a.query.slice(0,-1),a.search(),clearTimeout(a.resetQuery),
18
+ !1;38!=b&&40!=b&&(c=String.fromCharCode(b),a.query+=c,a.search(),clearTimeout(a.resetQuery))}}},"keyup.easyDropDown":function(){a.resetQuery=setTimeout(function(){a.query=""},1200)}});a.$dropDown.on("scroll.easyDropDown",function(c){a.$dropDown[0].scrollTop>=a.$dropDown[0].scrollHeight-a.maxHeight?a.$container.addClass("bottom"):a.$container.removeClass("bottom")});if(a.$form.length)a.$form.on("reset.easyDropDown",function(){a.$active.text(a.hasLabel?a.label:a.options[0].title)})},unbindHandlers:function(){this.$container.add(this.$select).add(this.$items).add(this.$form).add(this.$dropDown).off(".easyDropDown");
19
+ d("body").off("."+this.id)},open:function(){var a=window.scrollY||document.documentElement.scrollTop,c=window.scrollX||document.documentElement.scrollLeft,b=this.notInViewport(a);this.closeAll();this.$select.focus();window.scrollTo(c,a+b);this.$container.addClass("open");this.$scrollWrapper.css("height",this.maxHeight+"px");this.down=!0},close:function(){this.$container.removeClass("open");this.$scrollWrapper.css("height","0px");this.focusIndex=this.selected.index;this.query="";this.down=!1},closeAll:function(){var a=
20
+ Object.getPrototypeOf(this).instances,c;for(c in a)a[c].close()},select:function(a){"string"===typeof a&&(a=this.$select.find("option[value="+a+"]").index()-1);var c=this.options[a],b=this.hasLabel?a+1:a;this.$items.removeClass("active").eq(a).addClass("active");this.$active.text(c.title);this.$select.find("option").removeAttr("selected").eq(b).prop("selected",!0).parent().trigger("change");this.selected={index:a,title:c.title};this.focusIndex=i;"function"===typeof this.onChange&&this.onChange.call(this.$select[0],
21
+ {title:c.title,value:c.value})},search:function(){var a=this,c=function(b){a.focusIndex=b;a.$items.removeClass("focus").eq(a.focusIndex).addClass("focus");a.scrollToView()};for(i=0;i<a.options.length;i++){var b=a.options[i].title.toUpperCase();if(0==b.indexOf(a.query)){c(i);return}}for(i=0;i<a.options.length;i++)if(b=a.options[i].title.toUpperCase(),-1<b.indexOf(a.query)){c(i);break}},scrollToView:function(){if(this.focusIndex>=this.cutOff){var a=this.$items.eq(this.focusIndex).outerHeight()*(this.focusIndex+
22
+ 1)-this.maxHeight;this.$dropDown.scrollTop(a)}},notInViewport:function(a){var c=a+(window.innerHeight||document.documentElement.clientHeight),b=this.$dropDown.offset().top+this.maxHeight;return b>=a&&b<=c?0:b-c+5},destroy:function(){this.unbindHandlers();this.$select.unwrap().siblings().remove();this.$select.unwrap();delete Object.getPrototypeOf(this).instances[this.$select[0].id]},disable:function(){this.disabled=!0;this.$container.addClass("disabled");this.$select.attr("disabled",!0);this.down||
23
+ this.close()},enable:function(){this.disabled=!1;this.$container.removeClass("disabled");this.$select.attr("disabled",!1)}};var f=function(a,c){a.id=a.id?a.id:"EasyDropDown"+("00000"+(16777216*Math.random()<<0).toString(16)).substr(-6).toUpperCase();var b=new e;b.instances[a.id]||(b.instances[a.id]=b,b.init(a,c))};d.fn.easyDropDown=function(){var a=arguments,c=[],b;b=this.each(function(){if(a&&"string"===typeof a[0]){var b=e.prototype.instances[this.id][a[0]](a[1],a[2]);b&&c.push(b)}else f(this,a[0])});
24
+ return c.length?1<c.length?c:c[0]:b};d(function(){"function"!==typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"===typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype});d("select.dropdown").each(function(){var a=d(this).attr("data-settings");settings=a?d.parseJSON(a):{};f(this,settings)})})})(jQuery);
js/wpsl-gmap.js ADDED
@@ -0,0 +1,555 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function( $ ) {
2
+ var geocoder, map, infowindow, directionsDisplay, directionsService,
3
+ markersArray = [],
4
+ $selects = $( "select" );
5
+
6
+ /* Load Google Maps */
7
+ function initializeGmap() {
8
+ var myOptions, zoomControlPosition, zoomControlStyle,
9
+ latLng, zoomTo, zoomLevel, mapType,
10
+ streetViewVisible = ( wpslSettings.streetView == 1 ) ? true : false;
11
+
12
+ /* If no zoom location is defined, we show the entire world */
13
+ if ( wpslSettings.zoomLatlng !== '' ) {
14
+ latLng = wpslSettings.zoomLatlng.split( ',' );
15
+ zoomTo = new google.maps.LatLng( latLng[0], latLng[1] );
16
+ zoomLevel = parseInt( wpslSettings.zoomLevel );
17
+ } else {
18
+ zoomTo = new google.maps.LatLng( 0,0 );
19
+ zoomLevel = 1;
20
+ }
21
+
22
+ geocoder = new google.maps.Geocoder();
23
+ infowindow = new google.maps.InfoWindow({
24
+ pixelOffset: new google.maps.Size( -12,0 ) //make the offset equal to the marker anchor, line 387
25
+ });
26
+ directionsDisplay = new google.maps.DirectionsRenderer();
27
+ directionsService = new google.maps.DirectionsService();
28
+
29
+ /* Set correct the position of the controls */
30
+ if ( wpslSettings.controlPosition == "right" ) {
31
+ zoomControlPosition = google.maps.ControlPosition.RIGHT_TOP
32
+ } else {
33
+ zoomControlPosition = google.maps.ControlPosition.LEFT_TOP
34
+ }
35
+
36
+ /* Set correct control style */
37
+ if ( wpslSettings.controlStyle == "small" ) {
38
+ zoomControlStyle = google.maps.ZoomControlStyle.SMALL
39
+ } else {
40
+ zoomControlStyle = google.maps.ZoomControlStyle.LARGE
41
+ }
42
+
43
+ /* Set the selected map type */
44
+ switch ( wpslSettings.mapType ) {
45
+ case "roadmap":
46
+ mapType = google.maps.MapTypeId.ROADMAP
47
+ break;
48
+ case "satellite":
49
+ mapType = google.maps.MapTypeId.SATELLITE
50
+ break;
51
+ case "hybrid":
52
+ mapType = google.maps.MapTypeId.HYBRID
53
+ break;
54
+ case "terrain":
55
+ mapType = google.maps.MapTypeId.TERRAIN
56
+ break;
57
+ default:
58
+ mapType = google.maps.MapTypeId.ROADMAP
59
+ }
60
+
61
+ myOptions = {
62
+ zoom: zoomLevel,
63
+ center: zoomTo,
64
+ mapTypeId: mapType,
65
+ panControl: false,
66
+ streetViewControl: streetViewVisible,
67
+ zoomControlOptions: {
68
+ style: zoomControlStyle,
69
+ position: zoomControlPosition
70
+ }
71
+ };
72
+
73
+ map = new google.maps.Map( document.getElementById( "wpsl-gmap" ), myOptions );
74
+
75
+ /* Check if we need to try and autolocate the user */
76
+ if ( wpslSettings.autoLocate == 1 ) {
77
+ checkGeolocation();
78
+ }
79
+
80
+ /* Style the dropdown menu */
81
+ $selects.easyDropDown({
82
+ cutOff: 10,
83
+ wrapperClass: "wpsl-dropdown"
84
+ });
85
+ }
86
+
87
+ /* Check if Geolocation is supported */
88
+ function checkGeolocation() {
89
+ if ( navigator.geolocation ) {
90
+ navigator.geolocation.getCurrentPosition( handleGeolocationQuery );
91
+ }
92
+ };
93
+
94
+ function handleGeolocationQuery( position ) {
95
+ var storeId = 0,
96
+ storeName = "",
97
+ draggable = true,
98
+ latLng = new google.maps.LatLng( position.coords.latitude, position.coords.longitude );
99
+
100
+ reverseGeocode( latLng ); //set the zipcode that belongs to the latlng in the input field
101
+ map.setCenter( latLng );
102
+ addMarker( latLng, storeId, storeName, draggable );
103
+ findStoreLocations( latLng );
104
+ };
105
+
106
+ /* Handle clicks on the search button */
107
+ $( "#wpsl-search-btn" ).on( "click", function() {
108
+ $( "#wpsl-result-list ul" ).empty();
109
+ $( "#wpsl-stores" ).show();
110
+ $( ".wpsl-direction-before, .wpsl-direction-after" ).remove();
111
+ $( "#wpsl-direction-details" ).hide();
112
+
113
+ deleteOverlays();
114
+ codeAddress();
115
+ });
116
+
117
+ /* Handle the click on the back button when the route directions are displayed */
118
+ $( "#wpsl-result-list" ).on( "click", ".wpsl-back", function() {
119
+ var i, len;
120
+
121
+ /* Remove the directions from the map */
122
+ directionsDisplay.setMap( null );
123
+
124
+ /* Restore all markers on the map */
125
+ for ( i = 0, len = markersArray.length; i < len; i++ ) {
126
+ markersArray[i].setMap( map );
127
+ }
128
+
129
+ fitBounds();
130
+
131
+ /* After the markers are restored, the direction link has lost it's click handler. So we reinstate it. */
132
+ $( ".wpsl-info-window" ).on( "click", ".wpsl-directions", function() {
133
+ renderDirections( $(this) );
134
+ return false;
135
+ });
136
+
137
+ $( ".wpsl-direction-before, .wpsl-direction-after" ).remove();
138
+ $( "#wpsl-stores" ).show();
139
+ $( "#wpsl-direction-details" ).hide();
140
+
141
+ return false;
142
+ });
143
+
144
+ function renderDirections( e ) {
145
+ var i, start, end, len, storeId;
146
+
147
+ /*
148
+ The storeId is placed on the li in the results list,
149
+ but in the marker it will be on the wrapper div. So we check which one we need to target
150
+ */
151
+ if ( e.parent( "li" ).length > 0 ) {
152
+ storeId = e.parent( "li" ).data( "store-id" );
153
+ } else {
154
+ storeId = e.parent( ".wpsl-info-window" ).data( "store-id" );
155
+ }
156
+
157
+ /* Find the latlng that belongs to the start and end point */
158
+ for ( i = 0, len = markersArray.length; i < len; i++ ) {
159
+ if ( markersArray[i].storeId == 0 ) {
160
+ start = markersArray[i].getPosition();
161
+ } else if ( markersArray[i].storeId == storeId ) {
162
+ end = markersArray[i].getPosition();
163
+ }
164
+ }
165
+
166
+ if ( start && end ) {
167
+ $( "#wpsl-direction-details ul" ).empty();
168
+ $( ".wpsl-direction-before, .wpsl-direction-after" ).remove();
169
+ calcRoute( start, end );
170
+ } else {
171
+ alert( wpslLabels.generalError );
172
+ }
173
+ }
174
+
175
+ /* Check if we need to enable the bouncing of markers when the user hovers over the result list */
176
+ if ( wpslSettings.markerBounce == 1 ) {
177
+ $( "#wpsl-stores" ).on( "mouseenter", "li", function() {
178
+ letsBounce( $(this).data( "store-id" ), "start" );
179
+ });
180
+
181
+ $( "#wpsl-stores" ).on( "mouseleave", "li", function() {
182
+ letsBounce( $(this).data( "store-id" ), "stop" );
183
+ });
184
+ }
185
+
186
+ /* Let a single marker bounce */
187
+ function letsBounce( storeId, status ) {
188
+ var storeId, status, i, len, animation = '';
189
+
190
+ if ( status == "start" ) {
191
+ animation = google.maps.Animation.BOUNCE
192
+ } else {
193
+ animation = null;
194
+ }
195
+
196
+ /* Find the correct marker to bounce based on the storeId */
197
+ for ( i = 0, len = markersArray.length; i < len; i++ ) {
198
+ if ( markersArray[i].storeId == storeId ) {
199
+ marker = markersArray[i];
200
+ marker.setAnimation( animation );
201
+ }
202
+ }
203
+ }
204
+
205
+ /* Show the directions on the map */
206
+ function calcRoute( start, end ) {
207
+ var legs, len, step, index, direction, i, j,
208
+ directionStops = "",
209
+ request = {
210
+ origin: start,
211
+ destination: end,
212
+ travelMode: google.maps.DirectionsTravelMode.DRIVING
213
+ };
214
+
215
+ directionsService.route( request, function( response, status ) {
216
+ if ( status == google.maps.DirectionsStatus.OK ) {
217
+ directionsDisplay.setMap( map );
218
+ directionsDisplay.setDirections( response );
219
+
220
+ if ( response.routes.length > 0 ) {
221
+ direction = response.routes[0];
222
+
223
+ /* Loop over the legs and steps of the directions */
224
+ for ( i = 0; i < direction.legs.length; i++ ) {
225
+ legs = direction.legs[i];
226
+
227
+ for ( j = 0, len = legs.steps.length; j < len; j++ ) {
228
+ step = legs.steps[j];
229
+ index = j+1;
230
+ directionStops = directionStops + "<li><div class='wpsl-direction-index'>" + index + "</div><div class='wpsl-direction-txt'>" + step.instructions + "</div><div class='wpsl-direction-distance'>" + step.distance.text + "</div></li>";
231
+ }
232
+ }
233
+
234
+ $( "#wpsl-direction-details ul" ).append( directionStops ).before( "<p class='wpsl-direction-before'><a class='wpsl-back' href='#'>Back</a>" + direction.legs[0].distance.text + " - " + direction.legs[0].duration.text + "</p>" ).after( "<p class='wpsl-direction-after'>" + response.routes[0].copyrights + "</p>" );
235
+ $( "#wpsl-direction-details" ).show();
236
+
237
+ /* Remove all other markers from the map */
238
+ for ( i = 0, len = markersArray.length; i < len; i++ ) {
239
+ markersArray[i].setMap( null );
240
+ }
241
+
242
+ $( "#wpsl-stores" ).hide();
243
+ }
244
+ }
245
+ });
246
+ }
247
+
248
+ /* Geocode the user input */
249
+ function codeAddress() {
250
+ var latLng, storeId, storeName,
251
+ address = $( "#wpsl-search-input" ).val();
252
+
253
+ geocoder.geocode( { 'address': address}, function( response, status ) {
254
+ if ( status == google.maps.GeocoderStatus.OK ) {
255
+ latLng = response[0].geometry.location;
256
+
257
+ /* Remove any previous markers and add a new one */
258
+ deleteOverlays();
259
+ addMarker( latLng, storeId = 0, storeName = "", draggable = true );
260
+
261
+ /* Try to find stores that match the radius, location criteria */
262
+ findStoreLocations( latLng );
263
+ } else {
264
+ geocodeNotification( status );
265
+ }
266
+ }
267
+ )};
268
+
269
+ /* Geocode the user input and set the returned zipcode in the input field */
270
+ function reverseGeocode( latLng ) {
271
+ var latLng, zipCode;
272
+
273
+ geocoder.geocode( {'latLng': latLng}, function( response, status ) {
274
+ if ( status == google.maps.GeocoderStatus.OK ) {
275
+ zipCode = filterApiResponse( response );
276
+
277
+ if ( zipCode !== "" ) {
278
+ $( "#wpsl-search-input" ).val( zipCode );
279
+ }
280
+ } else {
281
+ geocodeNotification( status );
282
+ }
283
+ }
284
+ )};
285
+
286
+ /* Filter out the zipcode from the response */
287
+ function filterApiResponse( response ) {
288
+ var zipcode, responseType,
289
+ addressLength = response[0].address_components.length;
290
+
291
+ /* Loop over the API response */
292
+ for ( i = 0; i < addressLength; i++ ){
293
+ responseType = response[0].address_components[i].types;
294
+
295
+ /* filter out the postcode */
296
+ if ( ( /^postal_code$/.test( responseType ) ) || ( /^postal_code_prefix,postal_code$/.test( responseType ) ) ) {
297
+ zipcode = response[0].address_components[i].long_name;
298
+ }
299
+ }
300
+
301
+ return zipcode;
302
+ }
303
+
304
+ function findStoreLocations( startLatLng ) {
305
+ var location,
306
+ center = map.getCenter(),
307
+ infoWindowData = {},
308
+ storeData = "",
309
+ draggable = false,
310
+ $storeList = $( "#wpsl-stores ul" ),
311
+ preloader = wpslSettings.path + "img/ajax-loader.gif",
312
+ ajaxData = {
313
+ action: "store_search",
314
+ lat: startLatLng.lat(),
315
+ lng: startLatLng.lng(),
316
+ max_results: $( "#wpsl-results select" ).val(),
317
+ radius: $( "#wpsl-radius select" ).val()
318
+ };
319
+
320
+ /* Add the preloader */
321
+ $storeList.empty().append( "<li class='wpsl-preloader'><img src='" + preloader + "'/><span>" + wpslLabels.preloader + "</span></li>" );
322
+
323
+ $.get( wpslSettings.ajaxurl, ajaxData, function( response ) {
324
+
325
+ /* Remove the preloaders and no results msg */
326
+ $( ".wpsl-preloader, .no-results" ).remove();
327
+
328
+ if ( response.success !== false ) {
329
+ if ( response.length > 0 ) {
330
+ $.each( response, function( index ) {
331
+ infoWindowData = {
332
+ store: response[index].store,
333
+ street: response[index].street,
334
+ city: response[index].city,
335
+ state: response[index].state,
336
+ zip: response[index].zip,
337
+ description: response[index].description,
338
+ phone: response[index].phone,
339
+ fax: response[index].fax,
340
+ url: response[index].url,
341
+ email: response[index].email,
342
+ hours: response[index].hours,
343
+ thumb: response[index].thumb
344
+ };
345
+
346
+ location = new google.maps.LatLng( response[index].lat, response[index].lng );
347
+ addMarker( location, response[index].id, infoWindowData, draggable );
348
+ storeData = storeData + storeHtml( response[index] );
349
+ });
350
+
351
+ $( "#wpsl-result-list" ).off( "click", ".wpsl-directions" );
352
+ $storeList.append( storeData );
353
+
354
+ $( "#wpsl-result-list" ).on( "click", ".wpsl-directions", function() {
355
+ renderDirections( $(this) );
356
+ return false;
357
+ });
358
+
359
+ } else {
360
+ $storeList.html( "<li class='no-results'>" + wpslLabels.noResults + "</li>" );
361
+ }
362
+
363
+ fitBounds();
364
+ } else {
365
+ alert( wpslLabels.generalError );
366
+ }
367
+ });
368
+ }
369
+
370
+ /* Add a new marker to the map based on the provided location (latlng) */
371
+ function addMarker( location, storeId, infoWindowData, draggable ) {
372
+ var markerPath, mapIcon;
373
+
374
+ if ( storeId === 0 ) {
375
+ markerPath = wpslSettings.path + "img/markers/" + wpslSettings.startMarker;
376
+ } else {
377
+ markerPath = wpslSettings.path + "img/markers/" + wpslSettings.storeMarker;
378
+ }
379
+
380
+ var mapIcon = {
381
+ url: markerPath,
382
+ size: new google.maps.Size( 48,70 ), //original format
383
+ scaledSize: new google.maps.Size( 24,35 ), //retina format
384
+ origin: new google.maps.Point( 0,0 ),
385
+ anchor: new google.maps.Point( 12,35 )
386
+ };
387
+
388
+ var infoWindowContent,
389
+ marker = new google.maps.Marker({
390
+ position: location,
391
+ map: map,
392
+ optimized: false, //fixes markers flashing while bouncing
393
+ title: infoWindowData.store,
394
+ draggable: draggable,
395
+ storeId: storeId,
396
+ icon: mapIcon
397
+ });
398
+
399
+ google.maps.event.addListener( marker, "click", function() {
400
+ if ( storeId != 0 ) {
401
+ infoWindowContent = createInfoWindowHtml( infoWindowData, storeId );
402
+ infowindow.setContent( infoWindowContent );
403
+ } else {
404
+ infowindow.setContent( wpslLabels.startPoint ); //setting van maken
405
+ }
406
+ infowindow.open( map, marker );
407
+
408
+ //pixelOffset: new google.maps.Size(0, 60)
409
+
410
+ $( ".wpsl-info-window" ).on( "click", ".wpsl-directions", function() {
411
+ renderDirections( $(this) );
412
+ return false;
413
+ });
414
+ });
415
+
416
+ /* Store the marker for later use */
417
+ markersArray.push( marker );
418
+
419
+ if ( draggable ) {
420
+ google.maps.event.addListener( marker, "dragend", function( event ) {
421
+ map.setCenter( event.latLng );
422
+ reverseGeocode( event.latLng );
423
+ findStoreLocations( event.latLng );
424
+ });
425
+ }
426
+ }
427
+
428
+ /* Create the data for the infowindows on Google Maps */
429
+ function createInfoWindowHtml( infoWindowData, storeId ) {
430
+ var storeHeader,
431
+ windowContent = "<div data-store-id='" + storeId + "' class='wpsl-info-window'>";
432
+
433
+ /* Check if we need to turn the store name into a link or not */
434
+ if ( ( typeof( infoWindowData.url ) !== "undefined" ) && ( infoWindowData.url !== "" ) ) {
435
+ storeHeader = "<a href='" + infoWindowData.url + "'><strong>" + infoWindowData.store + "</strong></a>";
436
+ } else {
437
+ storeHeader = "<strong>" + infoWindowData.store + "</strong>";
438
+ }
439
+
440
+ windowContent += "<p>" + storeHeader + "<span>" + infoWindowData.street + "</span><span>" + infoWindowData.city + "</span></p>";
441
+
442
+ if ( ( typeof( infoWindowData.description ) !== "undefined" ) && ( infoWindowData.description !== "" ) ) {
443
+ windowContent += "<p>" + infoWindowData.description + "</p>";
444
+ }
445
+
446
+ /* If no data exist for either the phone / fax / email then just don't show them */
447
+ if ( ( typeof( infoWindowData.phone ) !== "undefined" ) && ( infoWindowData.phone !== "" ) ) {
448
+ windowContent += "<span><strong>" + wpslLabels.phone + "</strong>: " + infoWindowData.phone + "</span>";
449
+ }
450
+
451
+ if ( ( typeof( infoWindowData.fax ) !== "undefined" ) && ( infoWindowData.fax !== "" ) ) {
452
+ windowContent += "<span><strong>" + wpslLabels.fax + "</strong>: " + infoWindowData.fax + "</span>";
453
+ }
454
+
455
+ if ( ( typeof( infoWindowData.email ) !== "undefined" ) && ( infoWindowData.email !== "" ) ) {
456
+ windowContent += "<span><strong>Email</strong>: " + infoWindowData.email + "</span>";
457
+ }
458
+
459
+ if ( ( typeof( infoWindowData.hours ) !== "undefined" ) && ( infoWindowData.hours !== "" ) ) {
460
+ windowContent += "<div class='wpsl-store-hours'><strong>" + wpslLabels.hours + "</strong> " + infoWindowData.hours + "</div>";
461
+ }
462
+
463
+ windowContent += "<a class='wpsl-directions' href='#'>" + wpslLabels.directions + "</a>";
464
+ windowContent += "</div>";
465
+
466
+ return windowContent;
467
+ }
468
+
469
+ function storeHtml( response ) {
470
+ var html, storeImg = "",
471
+ id = response.id,
472
+ store = response.store,
473
+ street = response.street,
474
+ city = response.city,
475
+ state = response.state,
476
+ zip = response.zip,
477
+ thumb = response.thumb,
478
+ country = response.country,
479
+ distance = parseFloat( response.distance ).toFixed(1) + " " + wpslSettings.distanceUnit;
480
+
481
+ if ( ( typeof( thumb ) !== "undefined" ) && ( thumb !== "" ) ) {
482
+ storeImg = "<img class='wpsl-store-thumb' src='" + thumb + "' width='48' height='48' alt='" + store + "' />";
483
+ }
484
+
485
+ html = "<li data-store-id='" + id + "'><div><p>" + storeImg + "<strong>" + store + "</strong><span class='wpsl-street'>" + street + "</span>" + city + " " + state + " " + zip + "<span class='wpsl-country'>" + country + "</p></div>" + distance + "<a class='wpsl-directions' href='#'>" + wpslLabels.directions + "</a></li>";
486
+
487
+ return html;
488
+ }
489
+
490
+ /* Zoom the map so that all markers fit in the window */
491
+ function fitBounds() {
492
+ var i, markerLen,
493
+ maxZoom = 12,
494
+ bounds = new google.maps.LatLngBounds();
495
+
496
+ /* Make sure we don't zoom to far */
497
+ google.maps.event.addListenerOnce( map, "bounds_changed", function( event ) {
498
+ if ( this.getZoom() > maxZoom ) {
499
+ this.setZoom( maxZoom );
500
+ }
501
+ });
502
+
503
+ for ( i = 0, markerLen = markersArray.length; i < markerLen; i++ ) {
504
+ bounds.extend ( markersArray[i].position );
505
+ }
506
+
507
+ map.fitBounds( bounds );
508
+ }
509
+
510
+ /* Remove all existing markers and route lines from the map */
511
+ function deleteOverlays() {
512
+ directionsDisplay.setMap( null );
513
+
514
+ /* Remove all the markers from the map, and empty the array */
515
+ if ( markersArray ) {
516
+ for ( i = 0, markerLen = markersArray.length; i < markerLen; i++ ) {
517
+ markersArray[i].setMap( null );
518
+ }
519
+
520
+ markersArray.length = 0;
521
+ }
522
+ }
523
+
524
+ /* Handle the geocode errors */
525
+ function geocodeNotification( status ) {
526
+ var msg;
527
+
528
+ switch ( status ) {
529
+ case "ZERO_RESULTS":
530
+ msg = wpslLabels.noResults;
531
+ break;
532
+ case "OVER_QUERY_LIMIT":
533
+ msg = wpslLabels.queryLimit;
534
+ break;
535
+ default:
536
+ msg = wpslLabels.generalError;
537
+ break;
538
+ }
539
+
540
+ alert( msg );
541
+ }
542
+
543
+ /* Trigger the search when the user presses "enter" on the keyboard */
544
+ $( "#wpsl-search-input" ).keydown( function ( event ) {
545
+ var keypressed = event.keyCode || event.which;
546
+ if ( keypressed == 13 ) {
547
+ $( "#wpsl-search-btn" ).trigger( "click" );
548
+ }
549
+ });
550
+
551
+ if ( $("#wpsl-gmap").length ) {
552
+ google.maps.event.addDomListener( window, "load", initializeGmap );
553
+ }
554
+
555
+ });
readme.txt ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Store Locator ===
2
+ Contributors: tijmensmit
3
+ Tags: google maps, store locator, business locations, geocoding, stores, geo
4
+ Requires at least: 3.5
5
+ Tested up to: 3.8
6
+ Stable tag: 1.0
7
+ License: GPLv3
8
+ License URI: http://www.gnu.org/licenses/gpl.html
9
+
10
+ An easy to use location management system that enables users to search for nearby physical stores
11
+
12
+ == Description ==
13
+
14
+ WP Store Locator is a powerful and easy to use location management system.
15
+ You can customize the appearance of the map and provide custom labels for entry fields.
16
+ Users can filter the results by radius and see driving directions to the nearby stores in
17
+ the language that is set in the admin panel.
18
+
19
+ = Features include: =
20
+
21
+ * Manage an unlimited numbers of stores
22
+ * Set an unique thumbnail for each store
23
+ * Provide extra details for stores like the phone, fax, email, url, description and opening hours
24
+ * You can drag the marker to the exact spot on the 'Add Store' page
25
+ * Show the driving distances in either km or miles
26
+ * Choose from nine retina ready marker icons
27
+ * Show Google Maps in different languages, this also influences the language for the driving directions
28
+ * Show the driving directions to the stores
29
+ * Users can filter the returned results by radius or max results
30
+ * Customize the max results and search radius values that users can select
31
+ * Customize map settings like the terrain type, location of the map controls and the default zoom level
32
+ * Auto-locate the location of the user and show nearby stores
33
+ * Labels can be translated in any language through the settings panel
34
+
35
+ == Installation ==
36
+
37
+ 1. Upload the `wp-store-locator` folder to the `/wp-content/plugins/` directory
38
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
39
+ 1. Add your stores under 'Store Locator' -> Add Store
40
+ 1. Add the map to a page with this shortcode: [wpsl]
41
+
42
+ == Frequently Asked Questions ==
43
+
44
+ = How do I add the store locator to a page? =
45
+
46
+ Add this shortcode [wpsl] to the page where you want to display the store locator.
47
+
48
+ == Screenshots ==
49
+
50
+ 1. Front-end of the plugin
51
+ 2. The driving directions from the user location to the selected store
52
+ 3. The 'Add Store' screen
53
+ 4. The plugin settings
54
+ 5. Overview from the current stores
55
+
56
+ == Changelog ==
57
+
58
+ = 1.0 =
59
+ * Initial release
screenshot-1.jpg ADDED
Binary file
screenshot-2.jpg ADDED
Binary file
screenshot-3.jpg ADDED
Binary file
screenshot-4.jpg ADDED
Binary file
screenshot-5.jpg ADDED
Binary file
uninstall.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( !defined( 'ABSPATH' ) && !defined( 'WP_UNINSTALL_PLUGIN ') ) {
3
+ exit;
4
+ }
5
+
6
+ function wpsl_uninstall() {
7
+
8
+ global $wpdb;
9
+
10
+ $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'wpsl_stores' );
11
+
12
+ delete_option( 'wpsl_version' );
13
+ delete_option( 'wpsl_settings' );
14
+
15
+ }
16
+
17
+ /* Delete the tables and options from the db */
18
+ wpsl_uninstall();
19
+ ?>
wp-store-locator.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: WP Store Locator
4
+ Plugin URI:
5
+ Description: An easy to use location management system that enables users to search for nearby physical stores
6
+ Author: Tijmen Smit
7
+ Author URI: http://tijmensmit.com/
8
+ Version: 1.0
9
+ Text Domain: wpsl
10
+ Domain Path: /languages/
11
+ License: GPLv3
12
+
13
+ WP Store Locator
14
+ Copyright (C)2013, Tijmen Smit - info@tijmensmit.com
15
+
16
+ This program is free software: you can redistribute it and/or modify
17
+ it under the terms of the GNU General Public License as published by
18
+ the Free Software Foundation, either version 3 of the License, or
19
+ (at your option) any later version.
20
+
21
+ This program is distributed in the hope that it will be useful,
22
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ GNU General Public License for more details.
25
+
26
+ You should have received a copy of the GNU General Public License
27
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
28
+
29
+ @package WP_Store_locator
30
+ @category Core
31
+ @author Tijmen Smit
32
+ @version 1.0
33
+ */
34
+
35
+ if ( !class_exists( 'WP_Store_locator' ) ) {
36
+
37
+ class WP_Store_locator {
38
+
39
+ /**
40
+ * Stores the current plugin settings
41
+ * @since 1.0
42
+ * @var array
43
+ */
44
+ public $settings = array();
45
+
46
+ /**
47
+ * Stores the default plugin settings
48
+ * @since 1.0
49
+ * @var array
50
+ */
51
+ public $default_settings = array(
52
+ 'api_key' => '',
53
+ 'api_language' => 'en',
54
+ 'api_region' => '',
55
+ 'distance_unit' => 'km',
56
+ 'max_results' => '(25),50,75,100',
57
+ 'search_radius' => '10,25,(50),100,200,500',
58
+ 'marker_bounce' => '1',
59
+ 'auto_locate' => '1',
60
+ 'zoom_level' => '3',
61
+ 'zoom_name' => '',
62
+ 'zoom_latlng' => '',
63
+ 'height' => '350',
64
+ 'map_type' => 'roadmap',
65
+ 'pan_controls' => '0',
66
+ 'streetview' => '0',
67
+ 'results_dropdown' => '1',
68
+ 'infowindow_width' => '225',
69
+ 'search_width' => '179',
70
+ 'label_width' => '95',
71
+ 'control_position' => 'left',
72
+ 'control_style' => 'small',
73
+ 'start_marker' => 'red.png',
74
+ 'store_marker' => 'blue.png',
75
+ 'start_label' => 'Start location',
76
+ 'search_label' => 'Your location',
77
+ 'search_btn_label' => 'Search',
78
+ 'preloader_label' => 'Searching...',
79
+ 'radius_label' => 'Search radius',
80
+ 'no_results_label' => 'No results found',
81
+ 'results_label' => 'Results',
82
+ 'directions_label' => 'Directions',
83
+ 'error_label' => 'Something went wrong, please try again!',
84
+ 'limit_label' => 'API usage limit reached',
85
+ 'phone_label' => 'Phone',
86
+ 'fax_label' => 'Fax',
87
+ 'hours_label' => 'Hours'
88
+ );
89
+
90
+ /**
91
+ * Class constructor
92
+ */
93
+ function __construct() {
94
+
95
+ $this->define_constants();
96
+ $this->define_tables();
97
+
98
+ if ( is_admin() ) {
99
+ require_once( WPSL_PLUGIN_DIR . 'admin/class-admin.php' );
100
+
101
+ load_plugin_textdomain( 'wpsl', false, basename( dirname( __FILE__ ) ) . '/languages' );
102
+
103
+ register_activation_hook( __FILE__, array( $this, 'install' ) );
104
+ } else {
105
+ require_once( WPSL_PLUGIN_DIR . 'frontend/class-frontend.php' );
106
+ }
107
+
108
+ //if placed in the if else together with the class-frontend, the ajax hook always fails?
109
+ require_once( WPSL_PLUGIN_DIR . 'frontend/wpsl-ajax-functions.php' );
110
+ }
111
+
112
+ /**
113
+ * Setup plugin constants
114
+ *
115
+ * @since 1.0
116
+ * @return void
117
+ */
118
+ public function define_constants() {
119
+
120
+ if ( !defined( 'WPSL_VERSION_NUM' ) )
121
+ define( 'WPSL_VERSION_NUM', '1.0' );
122
+
123
+ if ( !defined( 'WPSL_URL' ) )
124
+ define( 'WPSL_URL', plugin_dir_url( __FILE__ ) );
125
+
126
+ if ( !defined( 'WPSL_BASENAME' ) )
127
+ define( 'WPSL_BASENAME', plugin_basename( __FILE__ ) );
128
+
129
+ if ( ! defined( 'WPSL_PLUGIN_DIR' ) )
130
+ define( 'WPSL_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
131
+ }
132
+
133
+ private function define_tables() {
134
+
135
+ global $wpdb;
136
+
137
+ $wpdb->wpsl_stores = $wpdb->prefix . 'wpsl_stores';
138
+ }
139
+
140
+ /**
141
+ * Run the install functions
142
+ *
143
+ * @since 1.0
144
+ * @return void
145
+ */
146
+ public function install() {
147
+ $this->create_tables();
148
+ $this->set_default_settings();
149
+
150
+ update_option( 'wpsl_version', WPSL_VERSION_NUM );
151
+ }
152
+
153
+ /**
154
+ * Create the required tables
155
+ *
156
+ * @since 1.0
157
+ * @return void
158
+ */
159
+ public function create_tables() {
160
+
161
+ global $wpdb;
162
+
163
+ $charset_collate = '';
164
+
165
+ if ( !empty( $wpdb->charset ) )
166
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
167
+ if ( !empty( $wpdb->collate ) )
168
+ $charset_collate .= " COLLATE $wpdb->collate";
169
+
170
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->wpsl_stores'" ) != $wpdb->wpsl_stores ) {
171
+ $sql = "CREATE TABLE " . $wpdb->wpsl_stores . " (
172
+ wpsl_id int(10) unsigned NOT NULL auto_increment,
173
+ store varchar(255) NULL,
174
+ street varchar(255) NULL,
175
+ city varchar(255) NULL,
176
+ state varchar(255) NULL,
177
+ zip varchar(100) NULL,
178
+ country varchar(255) NULL,
179
+ country_iso tinytext NOT NULL,
180
+ lat float(10,6) NOT NULL,
181
+ lng float(10,6) NOT NULL,
182
+ description text NULL,
183
+ phone varchar(100) NULL,
184
+ fax varchar(100) NULL,
185
+ url varchar(255) NULL,
186
+ email varchar(255) NULL,
187
+ hours varchar(255) NULL,
188
+ thumb_id bigint(20) unsigned NOT NULL,
189
+ active tinyint(1) NULL default 1,
190
+ PRIMARY KEY (wpsl_id)
191
+ ) $charset_collate;";
192
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
193
+ dbDelta( $sql );
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Set the default pluging settings
199
+ *
200
+ * @since 1.0
201
+ * @return void
202
+ */
203
+ public function set_default_settings() {
204
+
205
+ $settings = get_option( 'wpsl_settings' );
206
+
207
+ if ( !$settings ) {
208
+ update_option( 'wpsl_settings', $this->default_settings );
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Get the current plugin settings
214
+ *
215
+ * @since 1.0
216
+ * @return array $setting The current plugin settings
217
+ */
218
+ public function get_settings() {
219
+
220
+ $settings = get_option( 'wpsl_settings' );
221
+
222
+ if ( !$settings ) {
223
+ update_option( 'wpsl_settings', $this->default_settings );
224
+ $settings = $this->default_settings;
225
+ }
226
+
227
+ return $settings;
228
+ }
229
+ }
230
+
231
+ $wpsl = new WP_Store_locator();
232
+ }
233
+
234
+ ?>