PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.1
7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 6.0.5 All 36 releases
mlsimport / admin / js / mlsimport-admin.js

mlsimport-admin.js in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.1.1, at admin/js/mlsimport-admin.js

821 lines 30.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * MLSImport — admin settings/import screen behaviour.
3 *
4 * Drives the plugin's admin UI: shows/hides the credential fieldsets that match
5 * the selected MLS provider, starts/stops imports (global and per-import-task),
6 * polls the server for import log/progress, clears caches, batch-deletes
7 * properties by taxonomy term, and provides the front-end MLS autocomplete.
8 * All server calls go through admin-ajax (ajaxurl / mlsimport_vars.ajax_url).
9 *
10 * PHP's Provider Family module supplies the exact credential fields for the
11 * selected MLS; this file only applies their visibility.
12 */
13 jQuery( document ).ready(
14 function ($) {
15 'use strict';
16
17 // Handles for the two polling timers (global import + per-item import).
18 var log_refresh_interval;
19 var log_refresh_interval_per_item;
20 // Poll intervals in milliseconds.
21 var timer = 2000;
22 var timer_per_item = 4000;
23
24 // If the Import tab is the active tab on load, begin polling the global import log.
25 if (jQuery( '#nav-tab-import' ).hasClass( 'nav-tab-active' )) {
26 log_refresh_interval = setInterval( mlsimport_log_interval, timer );
27 }
28
29 // Start checking logs only after an import actually begins
30 // to avoid showing a completed message on initial page load.
31
32 //mlsimport_autocomplte_mls_selection();
33 /**
34 * Show / hide extra input field - cities and counties
35 */
36
37 mslimport_show_extra_options();
38
39 /**
40 * Show / hide input tokens on load
41 */
42 mlsimport_token_on_load();
43
44 /**
45 * Show / hide input tokens on change
46 */
47
48 // When the chosen MLS changes, use the provider fields supplied by PHP.
49 jQuery( '#mlsimport_mls_name' ).on(
50 'change',
51 function () {
52 mlsimport_token_on_load();
53 }
54 );
55
56 /**
57 * Stop Import per item
58 */
59
60 // Stop a single import task: halt client-side polling, then tell the server to stop.
61 jQuery( '#mlsimport_stop_item' ).on(
62 'click',
63 function () {
64 console.log( 'mlsimport-stop' );
65 // Import task post id and the shared item-actions nonce.
66 var post_id = jQuery( this ).attr( 'data-post_id' );
67 var nonce = jQuery('#mlsimport_item_actions').val();
68 // stop refreshing logs immediately on the client side
69 if (typeof log_refresh_interval_per_item !== 'undefined') {
70 clearInterval( log_refresh_interval_per_item );
71 }
72 jQuery.ajax(
73 {
74 type: 'POST',
75 url: ajaxurl,
76 data: {
77 'action' : 'mlsimport_stop_import_per_item',
78 'post_id' : post_id,
79 'security' : nonce
80 // AJAX action + task id + nonce for the stop request.
81 },
82 success: function (data) {
83 console.log( data );
84 jQuery( '#mlsimport_item_status' ).empty().append( 'Import stopped!' );
85 },
86 error: function (errorThrown) {
87 console.log( errorThrown );
88 }
89 }
90 );// end ajax
91 }
92 );
93
94 /**
95 * Start Import per item
96 */
97
98 // Start a single import task. Two triggers share this handler: the normal
99 // "start" button and the onboarding "run test" button (which forces a small
100 // 5-item onboarding import). Kicks off the per-item log poll, then the import.
101 jQuery( '#mlsimport-start_item,#mlsimport-run-test' ).on(
102 'click',
103 function (event) {
104 console.log( 'mlsimport-start' );
105 // Gather the task's identifiers and requested batch size from the DOM.
106 var ajaxurl = mlsimport_vars.ajax_url;
107 var post_id = jQuery( this ).attr( 'data-post_id' );
108 var post_number = jQuery( this ).attr( 'data-post-number' );
109 var how_many = jQuery( '#mlsimport_item_how_many' ).val();
110 var is_onboard = 0;
111 var nonce = jQuery('#mlsimport_item_actions').val();
112 // Reset the status line to a "starting" message.
113 jQuery( '#mlsimport_item_status' ).empty();
114 jQuery( '#mlsimport_item_status' ).append( "Starting the import. Please stand by!" );
115
116
117 // Onboarding "run test" path: disable the button, show a spinner and a
118 // progress bar, and force a fixed 5-item onboarding import.
119 if (event.target.id === 'mlsimport-run-test') {
120 // Do something only when #mlsimport-run-test is clicked
121
122
123
124 jQuery(this).prop('disabled', true);
125 jQuery('#mlsimport-test-spinner').show();
126 jQuery('.mlsimport-status-message')
127 .removeClass('pending')
128 .addClass('progress')
129 .html('<p><?php _e("Starting import... Please wait.", "mlsimport"); ?></p>' +
130 '<div class="mlsimport-progress-bar"><div class="mlsimport-progress-bar-inner" style="width: 20%"></div></div>');
131
132 how_many=5;
133 is_onboard=1;
134
135
136 } else {
137 // Handle #mlsimport-start_item click
138 console.log('Start item clicked');
139 }
140
141
142
143
144 clearInterval( log_refresh_interval_per_item );
145 log_refresh_interval_per_item = setInterval( mlsimport_log_interval_per_item, timer_per_item );
146
147
148 jQuery.ajax(
149 {
150 type: 'POST',
151 url: ajaxurl,
152 data: {
153 'action' : 'mlsimport_move_files_per_item',
154 'post_id' : post_id,
155 'how_many' : how_many,
156 'post_number' : post_number,
157 'is_onboard' : is_onboard,
158 'security' : nonce,
159 },
160 success: function (data) {
161 console.log( data );
162 if ( data && data.success === false && data.message ) {
163 jQuery( '#mlsimport_item_status' ).empty().append( data.message );
164 jQuery( '#mlsimport-start_item,#mlsimport-run-test' ).prop( 'disabled', true );
165 }
166
167 },
168 error: function (errorThrown) {
169 console.log( errorThrown );
170 var message = '';
171 if ( errorThrown.responseJSON && errorThrown.responseJSON.message ) {
172 message = errorThrown.responseJSON.message;
173 } else if ( errorThrown.responseText ) {
174 try {
175 var parsed = JSON.parse( errorThrown.responseText );
176 message = parsed.message || errorThrown.statusText;
177 } catch (e) {
178 message = errorThrown.statusText;
179 }
180 } else {
181 message = errorThrown.statusText;
182 }
183 jQuery( '#mlsimport_item_status' ).empty().append( message );
184 jQuery( '#mlsimport-start_item,#mlsimport-run-test' ).prop( 'disabled', true );
185 }
186 }
187 );// end ajax
188
189 }
190 );
191
192 /**
193 * delete cache
194 */
195
196 // Clear cache tool: ask the server to delete cached MLS data.
197 jQuery( '#mlsimport-clear-cache' ).on(
198 'click',
199 function () {
200 var ajaxurl = mlsimport_vars.ajax_url;
201 var nonce = jQuery('#mlsimport_tool_actions').val();
202
203 jQuery( '#mlsimport-clear-cache' ).val( 'Deleting...' );
204
205 jQuery.ajax(
206 {
207 type: 'POST',
208 url: ajaxurl,
209 data: {
210 'action' : 'mlsimport_delete_cache',
211 'security' : nonce
212 },
213 success: function (data) {
214 console.log( data );
215 jQuery( '#mlsimport-clear-cache' ).val( 'Deleted!' );
216
217 },
218 error: function (errorThrown) {
219 console.log( errorThrown );
220 }
221 }
222 );// end ajax
223 }
224 );
225
226 // Clear fields data tool: ask the server to wipe the stored field mapping data.
227 jQuery( '#mlsimport-clear-fields-data' ).on(
228 'click',
229 function () {
230 var ajaxurl = mlsimport_vars.ajax_url;
231 var nonce = jQuery('#mlsimport_tool_actions').val();
232
233 jQuery( '#mlsimport-clear-fields-data' ).val( 'Deleting...' );
234
235 jQuery.ajax(
236 {
237 type: 'POST',
238 url: ajaxurl,
239 data: {
240 'action' : 'mlsimport_clear_fields_data',
241 'security' : nonce
242 },
243 success: function (data) {
244 console.log( data );
245 jQuery( '#mlsimport-clear-fields-data' ).val( 'Deleted!' );
246
247 },
248 error: function (errorThrown) {
249 console.log( errorThrown );
250 }
251 }
252 );// end ajax
253 }
254 );
255
256 /**
257 * delete properties
258 */
259
260 // Load taxonomy terms when taxonomy is selected
261 // Populates the dependent term dropdown via AJAX for the chosen taxonomy.
262 jQuery( '#mlsimport_delete_category' ).on( 'change', function () {
263 var taxonomy = jQuery( this ).val();
264 var $termSelect = jQuery( '#mlsimport_delete_category_term' );
265 var nonce = jQuery( '#mlsimport_tool_actions' ).val();
266
267 // Clear and disable the term select until fresh terms arrive.
268 $termSelect.empty().prop( 'disabled', true );
269
270 // No taxonomy chosen: prompt the user and stop.
271 if ( ! taxonomy ) {
272 $termSelect.append( '<option value="" disabled>Select a taxonomy first</option>' );
273 return;
274 }
275
276 $termSelect.append( '<option value="" disabled>Loading...</option>' );
277
278 jQuery.ajax( {
279 type: 'POST',
280 url: mlsimport_vars.ajax_url,
281 dataType: 'json',
282 data: {
283 action: 'mlsimport_get_taxonomy_terms',
284 taxonomy: taxonomy,
285 security: nonce
286 },
287 success: function ( response ) {
288 $termSelect.empty();
289 if ( response.success && response.data.length ) {
290 jQuery.each( response.data, function ( i, term ) {
291 $termSelect.append(
292 '<option value="' + term.slug + '">' + term.name + ' (' + term.count + ')</option>'
293 );
294 } );
295 $termSelect.prop( 'disabled', false );
296 } else {
297 $termSelect.append( '<option value="" disabled>No terms found</option>' );
298 }
299 },
300 error: function () {
301 $termSelect.empty().append( '<option value="" disabled>Error loading terms</option>' );
302 }
303 } );
304 } );
305
306 // Batched delete
307 // Global flag the Stop button flips to end the recursive batch loop.
308 window.mlsimportDeleteStopped = false;
309
310 /**
311 * Delete matching properties one server batch at a time, recursing until done.
312 *
313 * @param {string} taxonomy Selected taxonomy slug.
314 * @param {Array} terms Selected term slugs to match.
315 * @param {string} nonce Tool-actions nonce.
316 * @param {number} totalDeleted Running count carried across batches.
317 */
318 function mlsimportDeleteBatch( taxonomy, terms, nonce, totalDeleted ) {
319 // Abort the loop if the user pressed Stop.
320 if ( window.mlsimportDeleteStopped ) {
321 jQuery( '#mlsimport-delete-notification' ).text( 'Stopped. Deleted ' + totalDeleted + ' properties.' );
322 mlsimportDeleteResetUI();
323 return;
324 }
325
326 jQuery.ajax( {
327 type: 'POST',
328 url: mlsimport_vars.ajax_url,
329 dataType: 'json',
330 data: {
331 action: 'mlsimport_delete_properties',
332 mlsimport_delete_category: taxonomy,
333 'mlsimport_delete_category_term[]': terms,
334 security: nonce
335 },
336 success: function ( response ) {
337 if ( ! response.success ) {
338 jQuery( '#mlsimport-delete-notification' ).text( response.data || 'Error' );
339 mlsimportDeleteResetUI();
340 return;
341 }
342
343 // Accumulate deleted count and derive an overall percentage for the bar.
344 var data = response.data;
345 totalDeleted += data.deleted;
346 var totalProperties = totalDeleted + data.remaining;
347 var pct = totalProperties > 0 ? Math.round( ( totalDeleted / totalProperties ) * 100 ) : 100;
348
349 jQuery( '#mlsimport-delete-progress-bar' ).css( 'width', pct + '%' );
350 jQuery( '#mlsimport-delete-progress-text' ).text( totalDeleted + ' / ' + totalProperties + ' deleted' );
351 jQuery( '#mlsimport-delete-notification' ).text( 'Deleting... ' + totalDeleted + ' deleted so far.' );
352
353 // Server signals completion, otherwise recurse for the next batch.
354 if ( data.done ) {
355 jQuery( '#mlsimport-delete-notification' ).text( 'Done! Deleted ' + totalDeleted + ' properties.' );
356 jQuery( '#mlsimport-delete-progress-bar' ).css( 'width', '100%' );
357 mlsimportDeleteResetUI();
358 } else {
359 mlsimportDeleteBatch( taxonomy, terms, nonce, totalDeleted );
360 }
361 },
362 error: function ( xhr ) {
363 console.log( xhr );
364 jQuery( '#mlsimport-delete-notification' ).text( 'Error during deletion. Deleted ' + totalDeleted + ' so far.' );
365 mlsimportDeleteResetUI();
366 }
367 } );
368 }
369
370 /**
371 * Restore the delete controls to their idle state (show start, hide stop).
372 */
373 function mlsimportDeleteResetUI() {
374 jQuery( '#mlsimport-delete-prop' ).show();
375 jQuery( '#mlsimport-delete-stop' ).hide();
376 }
377
378 // Start button: validate selections, confirm, then kick off the batch loop.
379 jQuery( '#mlsimport-delete-prop' ).on( 'click', function () {
380 var taxonomy = jQuery( '#mlsimport_delete_category' ).val();
381 var terms = jQuery( '#mlsimport_delete_category_term' ).val();
382 var nonce = jQuery( '#mlsimport_tool_actions' ).val();
383
384 if ( ! taxonomy ) {
385 jQuery( '#mlsimport-delete-notification' ).text( 'Please select a taxonomy.' );
386 return;
387 }
388 if ( ! terms || ! terms.length ) {
389 jQuery( '#mlsimport-delete-notification' ).text( 'Please select at least one term.' );
390 return;
391 }
392
393 // Require explicit confirmation before an irreversible bulk delete.
394 if ( ! confirm( 'Are you sure you want to delete all properties matching the selected terms? This cannot be undone.' ) ) {
395 return;
396 }
397
398 // Reset stop flag and switch the UI into the "deleting" state.
399 window.mlsimportDeleteStopped = false;
400 jQuery( '#mlsimport-delete-prop' ).hide();
401 jQuery( '#mlsimport-delete-stop' ).show();
402 jQuery( '#mlsimport-delete-progress' ).show();
403 jQuery( '#mlsimport-delete-progress-bar' ).css( 'width', '0%' );
404 jQuery( '#mlsimport-delete-progress-text' ).text( 'Starting...' );
405 jQuery( '#mlsimport-delete-notification' ).text( 'Deleting...' );
406
407 mlsimportDeleteBatch( taxonomy, terms, nonce, 0 );
408 } );
409
410 // Stop button: set the flag so the batch loop ends after the current request.
411 jQuery( '#mlsimport-delete-stop' ).on( 'click', function () {
412 window.mlsimportDeleteStopped = true;
413 jQuery( '#mlsimport-delete-notification' ).text( 'Stopping after current batch...' );
414 } );
415
416 /**
417 * Stop import
418 */
419
420 // Stop the global (all-tasks) import: tell the server to stop moving files,
421 // re-show the start button and stop the log poll.
422 jQuery( '#mlsimport_stop' ).on(
423 'click',
424 function () {
425 var ajaxurl = mlsimport_vars.ajax_url;
426
427 console.log( 'stop files' );
428 jQuery.ajax(
429 {
430 type: 'POST',
431 url: ajaxurl,
432 data: {
433 'action' : 'mlsimport_stop_moving_files',
434 },
435 success: function (data) {
436 console.log( data );
437 jQuery( '#aws-move-start' ).show();
438 clearInterval( log_refresh_interval );
439
440 },
441 error: function (errorThrown) {
442 console.log( errorThrown );
443 }
444 }
445 );// end ajax
446 }
447 );
448
449 /**
450 * Start Import
451 */
452
453 // Start the global import: show the progress UI, trigger the server move,
454 // then (re)start polling the global log.
455 jQuery( '#mlsimport-start' ).on(
456 'click',
457 function () {
458 console.log( 'mlsimport-start' );
459 var ajaxurl = mlsimport_vars.ajax_url;
460 aws_show_progress();
461
462 jQuery.ajax(
463 {
464 type: 'POST',
465 url: ajaxurl,
466 data: {
467 'action' : 'mlsimport_move_files'
468 },
469 success: function (data) {
470 console.log( data );
471 console.log( 'starting loggers' );
472 clearInterval( log_refresh_interval );
473 log_refresh_interval = setInterval( mlsimport_log_interval, timer );
474
475 },
476 error: function (errorThrown) {
477 console.log( errorThrown );
478 }
479 }
480 );// end ajax
481
482 }
483 );
484
485 /**
486 * Log import
487 *
488 * Polled on an interval during a global import: fetches the latest log text
489 * and remaining-file count, updates the log container and progress bar, and
490 * stops the poll when the server reports the run is done.
491 */
492
493 function mlsimport_log_interval()
494 {
495
496 // Total files to process (from the progress element) is the bar's denominator.
497 var progress_total = jQuery( '#mlsimport_monster_myProgress' ).attr( 'data-total' );
498 progress_total = parseInt( progress_total );
499 var remain_images = progress_total;
500 var done_images = 0;
501 var bar_width = 0;
502 jQuery.ajax(
503 {
504 type: 'POST',
505 url: ajaxurl,
506 dataType: 'json',
507 data: {
508 'action' : 'mlsimport_move_files_to_aws_logger',
509 },
510 success: function (data) {
511
512 // Done and no more logs: mark COMPLETED and stop polling.
513 if (data.is_done === 'done' && data.logs === '' ) {
514 jQuery( '#log_container' ).prepend( 'COMPLETED' );
515 jQuery( '#log_container' ).append( 'COMPLETED' );
516 clearInterval( log_refresh_interval );
517 } else if (data.logs !== '') {
518 // Still running: replace the log text and remaining-file count.
519 jQuery( '#log_container' ).empty().prepend( data.logs );
520 jQuery( '#aws_more_files' ).empty().text( data.current_files_no );
521
522 // Derive processed count and update the progress bar width.
523 remain_images = parseInt( data.current_files_no );
524 done_images = progress_total - remain_images;
525
526 bar_width = done_images * 100 / progress_total;
527 bar_width = parseFloat( bar_width );
528
529 jQuery( '#mlsimport_myBar' ).css( 'width',bar_width + '%' );
530 }
531 },
532 error: function (errorThrown) {
533 console.log( errorThrown );
534 }
535 }
536 );// end ajax
537 }
538
539 /**
540 * Log import per item
541 *
542 * Polled on an interval during a single import task: reads progress/total,
543 * updates that task's status text and progress bar, and stops the poll when
544 * the server reports the task is done.
545 */
546
547 function mlsimport_log_interval_per_item()
548 {
549 console.log( 'mlsimport_log_interval_per_item' );
550 // Task id and item-actions nonce for the logger request.
551 var item_id = jQuery( '#mlsimport-start_item' ).attr( 'data-post_id' );
552 var nonce = jQuery('#mlsimport_item_actions').val();
553 jQuery.ajax(
554 {
555 type: 'POST',
556 url: ajaxurl,
557 dataType: 'json',
558 data: {
559 'action' : 'mlsimport_logger_per_item',
560 'post_id' : item_id,
561 'security' : nonce
562 },
563 success: function (data) {
564 console.log( data );
565 // Imported-so-far vs total, used to size the progress bar.
566 var progress = parseInt( data.mlsimport_progress_properties );
567 var total = parseInt( data.mlsimport_task_to_import );
568 // Only update the bar when both numbers are valid.
569 if ( ! isNaN( progress ) && ! isNaN( total ) && total > 0 ) {
570 var width = progress * 100 / total;
571 jQuery( '#mlsimport_item_progress .mlsimport-progress-bar-inner' ).css( 'width', width + '%' );
572 }
573 // Only the server's run state can end polling. An empty log is
574 // valid while an Action Scheduler worker is still waiting.
575 if (data.is_done === 'done') {
576 console.log( 'kill interval' );
577
578 clearInterval( log_refresh_interval_per_item );
579 var message = "Ready to import!";
580 if (data.status === 'completed') {
581 message = "Import completed!";
582 jQuery( '#mlsimport_item_progress .mlsimport-progress-bar-inner' ).css( 'width', '100%' );
583 } else if (data.status === 'stopped') {
584 message = "Import stopped!";
585 } else if (data.status === 'failed') {
586 var runError = data.result && data.result.error ? ': ' + data.result.error : '';
587 message = "Import failed" + runError;
588 }
589 jQuery( '#mlsimport_item_status' ).empty().append( message );
590
591 }else if(data.is_done==='wip'){
592 // Work-in-progress: show current property number and memory usage.
593 console.log('we do wip');
594 var runningMessage = data.status === 'waiting'
595 ? 'Waiting for the import worker to start.'
596 : 'Importing property: '+data.mlsimport_progress_properties+' of '+data.mlsimport_task_to_import+'. Memory used: '+data.memory+' MB.';
597 jQuery( '#mlsimport_item_status' ).empty().append( runningMessage );
598
599 } else if (data.logs !== '') {
600 // Otherwise surface whatever raw log text the server returned.
601 console.log('we do logs');
602 jQuery( '#mlsimport_item_status' ).empty().append( data.logs );
603
604
605 jQuery('.mlsimport-import-summary').append(
606 '<p><strong><?php _e("Status:", "mlsimport"); ?></strong> ' +
607 '<span class="mlsimport-status-success">'+data.logs+'/span></p>'
608 );
609
610 }
611 },
612 error: function (errorThrown) {
613 console.log( errorThrown );
614 }
615 }
616 );// end ajax
617 }
618
619 /**
620 * SHow progress bar -
621 *
622 * Prepares the global import progress UI: clears the log, resolves how many
623 * files remain to move, reveals the progress wrapper and hides the start button.
624 */
625
626 function aws_show_progress()
627 {
628
629 // Start with an empty log container.
630 jQuery( '#log_container' ).empty();
631
632 // Read the count of files to move from the primary counter element.
633 var files_to_move = jQuery( '#aws_move' ).text();
634 files_to_move = parseInt( files_to_move );
635
636 console.log( 'files_to_move ' + files_to_move );
637
638 // Fall back to the secondary counter if the primary one is not a number.
639 if ( isNaN( parseFloat( files_to_move ) ) ) {
640 files_to_move = jQuery( '#aws_more_files' ).text();
641 }
642 console.log( 'files_to_move2 ' + files_to_move );
643 files_to_move = parseInt( files_to_move );
644
645 jQuery( '#mlsimport_myProgress_wrapper' ).show();
646 jQuery( '.aws_to_move' ).empty().html( '<strong>We start importing. Please wait.</strong>' );
647 jQuery( '#aws-move-start' ).hide();
648 }
649
650 /**
651 * Check / unchechek fields
652 */
653
654 // Select-all / select-none buttons for the import field checkboxes. The
655 // data-import value on the clicked button decides which set to (un)check.
656 jQuery( '.mls_import_selec_all_class' ).on(
657 'click',
658 function () {
659 var trigger_type = jQuery( this ).attr( 'data-import' );
660 console.log( 'trigger type ' + trigger_type );
661
662 // Branch on the button's mode: check/uncheck the import or admin field group.
663 if (trigger_type === 'import_select') {
664
665 jQuery( '.mlsimport_select_import_all' ).prop( 'checked', true );
666 } else if (trigger_type === 'import_select_none') {
667
668 jQuery( '.mlsimport_select_import_all' ).prop( 'checked', false );
669 } else if (trigger_type === 'import_admin') {
670 jQuery( '.mlsimport_select_import_admin_all' ).prop( 'checked', true );
671 } else if (trigger_type === 'import_admin_none') {
672 jQuery( '.mlsimport_select_import_admin_all' ).prop( 'checked', false );
673 }
674 }
675 );
676
677 }
678 );
679
680
681
682 /**
683 * Wire the "show extra options" toggles (e.g. cities/counties inputs).
684 *
685 * Each hidden-field button toggles the visibility of the input wrapper inside
686 * its parent fieldset.
687 */
688 function mslimport_show_extra_options()
689 {
690 // On click, find the enclosing fieldset and toggle its input wrapper.
691 jQuery( '.mlsimport_hidden_field_button' ).on(
692 'click',
693 function () {
694 var parent = jQuery( this ).closest( '.mlsimport-fieldset' );
695 console.log( parent )
696 parent.find( '.mlsimport-input-wrapper' ).toggle();
697 }
698 );
699 }
700
701
702
703
704 /**
705 * Trigger a server-side fetch of the selected MLS's metadata from the SaaS API,
706 * then reload the page so the newly available fields show up.
707 */
708 function mlsimport_saas_get_metadata()
709 {
710
711 console.log( 'mlsimport_saas_get_metadata' );
712 // Nonce and admin-ajax endpoint for the metadata request.
713 var nonce = jQuery('#mlsimport_saas_get_metadata').val();
714 var ajaxurl = mlsimport_vars.ajax_url;
715 jQuery.ajax(
716 {
717 type: 'POST',
718 url: ajaxurl,
719 data: {
720 'action' : 'mlsimport_saas_get_metadata_function',
721 'security' : nonce
722 },
723 success: function (data) {
724 console.log( data );
725 jQuery( '.mlsimport_populate_warning' ).remove();
726 location.reload( true );
727 },
728 error: function (errorThrown) {
729 console.log( errorThrown );
730 }
731 }
732 );// end ajax
733
734 }
735
736
737 /**
738 * Show only the selected MLS's credential fields using PHP-generated config.
739 * JavaScript contains no provider rules; it applies the final field-name list.
740 */
741 function mlsimport_token_on_load()
742 {
743 // PHP resolves the selected MLS to final credential field names. JavaScript
744 // only applies visibility and has no provider names or numeric ranges.
745 var providerConfig = window.mlsimport_vars && mlsimport_vars.provider_families
746 ? mlsimport_vars.provider_families
747 : { by_mls_id: {}, all_credential_fields: [] };
748 var selectedId = String( jQuery( '#mlsimport_mls_name' ).val() || '' );
749 var family = providerConfig.by_mls_id[ selectedId ];
750
751 providerConfig.all_credential_fields.forEach(
752 function (field) {
753 jQuery( '.fieldset_' + field ).hide();
754 }
755 );
756 if ( family && Array.isArray( family.credential_fields ) ) {
757 family.credential_fields.forEach(
758 function (field) {
759 jQuery( '.fieldset_' + field ).show();
760 }
761 );
762 }
763 }
764
765
766
767 /**
768 * Front-end MLS name autocomplete.
769 *
770 * Attaches a jQuery UI autocomplete to the visible MLS-name input. As the user
771 * types, matches come from the supplied list; picking one stores the human label
772 * in the front field and the numeric id in the hidden field, then refreshes the
773 * credential fieldsets via mlsimport_token_on_load().
774 *
775 * @param {Array} autofill Source list of { label, value } MLS entries.
776 */
777 function mlsimport_autocomplte_mls_selection(autofill){
778
779 console.log('mlsimport_autocomplte_mls_selection');
780 console.log(typeof jQuery.ui.autocomplete); // should be "function"
781 console.log(autofill);
782
783
784 jQuery( "#mlsimport_mls_name_front" ).autocomplete({
785 // Data source and minimum characters before suggestions appear.
786 source: autofill,
787 minLength: 3,
788 // Tag the dropdown widget so it can be styled by the plugin CSS.
789 open: function(event, ui) {
790 jQuery(this).autocomplete("widget").addClass("mlsimport-autocomplete-menu");
791 },
792 // On committed change, store label + id and refresh credential fields.
793 change( event, ui ){
794 console.log(ui);
795 jQuery("#mlsimport_mls_name_front").val(ui.item.label);
796 jQuery("#mlsimport_mls_name").val(ui.item.value);
797 mlsimport_token_on_load();
798 },
799 focus: function(event, ui) {
800 jQuery("#mlsimport_mls_name_front").val(ui.item.label);
801 jQuery("#mlsimport_mls_name").val(ui.item.value); mlsimport_token_on_load();
802 return false;
803 },
804 select: function(event, ui) {
805 jQuery("#mlsimport_mls_name_front").val(ui.item.label);
806 jQuery("#mlsimport_mls_name").val(ui.item.value); mlsimport_token_on_load();
807 return false;
808 },
809 // When no matches come back, inject a single "No results found" entry.
810 response: function(event, ui) {
811 if (!ui.content.length) {
812 var noResult = { value:"",label:"No results found" };
813 ui.content.push(noResult);
814 //$("#message").text("No results found");
815 } else {
816 // $("#message").empty();
817 }
818 }
819 });
820 }
821