| 1 |
/** |
| 2 |
* MLS Import Field Selector JavaScript |
| 3 |
* |
| 4 |
* Handles client-side functionality for the MLS field selection interface: |
| 5 |
* - Filtering and searching fields |
| 6 |
* - Drag and drop reordering |
| 7 |
* - Bulk actions |
| 8 |
* - Interactive UI elements |
| 9 |
* |
| 10 |
* @package MLSImport |
| 11 |
* @subpackage MLSImport/js |
| 12 |
* @since 1.0.0 |
| 13 |
*/ |
| 14 |
|
| 15 |
|
| 16 |
(function($) { |
| 17 |
'use strict'; |
| 18 |
|
| 19 |
/** |
| 20 |
* Initialize all field selector functionality once the document is ready |
| 21 |
*/ |
| 22 |
$(document).ready(function() { |
| 23 |
initProgressiveLoading(); |
| 24 |
initializeFilters(); |
| 25 |
initializeBulkActions(); |
| 26 |
initializeDragAndDrop(); |
| 27 |
initRowReordering(); |
| 28 |
initializeFieldSorting(); |
| 29 |
preventEnterSubmission(); |
| 30 |
}); |
| 31 |
|
| 32 |
/** |
| 33 |
* Prevent form submission when pressing Enter inside label or postmeta inputs |
| 34 |
*/ |
| 35 |
function preventEnterSubmission() { |
| 36 |
jQuery(document).on('keydown', '.mlsimport-label-input, .mlsimport-postmeta-input', function(e) { |
| 37 |
if (e.key === 'Enter') { |
| 38 |
e.preventDefault(); |
| 39 |
} |
| 40 |
}); |
| 41 |
} |
| 42 |
|
| 43 |
|
| 44 |
/** |
| 45 |
* Initialize filter and search functionality |
| 46 |
*/ |
| 47 |
function initializeFilters() { |
| 48 |
// Field search functionality |
| 49 |
$('#mlsimport-field-search') |
| 50 |
.on('keydown', function(e) { |
| 51 |
// Prevent form submission when pressing Enter inside the search box |
| 52 |
if (e.key === 'Enter') { |
| 53 |
e.preventDefault(); |
| 54 |
} |
| 55 |
}) |
| 56 |
.on('keyup', function() { |
| 57 |
const searchTerm = $(this).val().toLowerCase(); |
| 58 |
|
| 59 |
if (searchTerm.length > 2 || searchTerm.length === 0) { |
| 60 |
filterFieldsBySearch(searchTerm); |
| 61 |
} |
| 62 |
}); |
| 63 |
|
| 64 |
// Import status filter functionality |
| 65 |
$('#mlsimport-import-filter').on('change', function() { |
| 66 |
const filterValue = $(this).val(); |
| 67 |
filterFieldsByImportStatus(filterValue); |
| 68 |
}); |
| 69 |
|
| 70 |
// Add alphabetical filters if present |
| 71 |
$('.mlsimport-alpha-filter').on('click', function(e) { |
| 72 |
e.preventDefault(); |
| 73 |
|
| 74 |
// Remove active class from all alpha filters |
| 75 |
$('.mlsimport-alpha-filter').removeClass('active'); |
| 76 |
|
| 77 |
// Add active class to clicked filter |
| 78 |
$(this).addClass('active'); |
| 79 |
|
| 80 |
const letter = $(this).data('letter'); |
| 81 |
filterFieldsByAlphabet(letter); |
| 82 |
}); |
| 83 |
|
| 84 |
// Reset filters button |
| 85 |
$('#mlsimport-reset-filters').on('click', function(e) { |
| 86 |
e.preventDefault(); |
| 87 |
resetAllFilters(); |
| 88 |
}); |
| 89 |
|
| 90 |
// Pagination links |
| 91 |
$('.mlsimport-page-link').on('click', function(e) { |
| 92 |
e.preventDefault(); |
| 93 |
|
| 94 |
const page = $(this).data('page'); |
| 95 |
navigateToPage(page); |
| 96 |
}); |
| 97 |
} |
| 98 |
|
| 99 |
/** |
| 100 |
* Filter table rows based on search term |
| 101 |
* |
| 102 |
* @param {string} searchTerm - The term to search for |
| 103 |
*/ |
| 104 |
function filterFieldsBySearch(searchTerm) { |
| 105 |
// If search is empty, show all rows (respecting other filters) |
| 106 |
if (searchTerm === '') { |
| 107 |
$('.mlsimport-field-row').show(); |
| 108 |
return; |
| 109 |
} |
| 110 |
|
| 111 |
// Hide all rows first |
| 112 |
$('.mlsimport-field-row').hide(); |
| 113 |
|
| 114 |
// Show rows that match the search term |
| 115 |
$('.mlsimport-field-row').each(function() { |
| 116 |
const fieldName = $(this).data('field-key').toLowerCase(); |
| 117 |
|
| 118 |
if (fieldName.indexOf(searchTerm) !== -1) { |
| 119 |
$(this).show(); |
| 120 |
} |
| 121 |
}); |
| 122 |
|
| 123 |
// Update the "no results" message |
| 124 |
updateNoResultsMessage(); |
| 125 |
} |
| 126 |
|
| 127 |
/** |
| 128 |
* Filter table rows based on import status |
| 129 |
* |
| 130 |
* @param {string} status - The import status to filter by ('all', 'selected', 'not_selected', 'mandatory') |
| 131 |
*/ |
| 132 |
function filterFieldsByImportStatus(status) { |
| 133 |
// If all, show all rows |
| 134 |
if (status === 'all') { |
| 135 |
$('.mlsimport-field-row').show(); |
| 136 |
return; |
| 137 |
} |
| 138 |
|
| 139 |
// Hide all rows first |
| 140 |
$('.mlsimport-field-row').hide(); |
| 141 |
|
| 142 |
// Show rows based on import status |
| 143 |
$('.mlsimport-field-row').each(function() { |
| 144 |
const isChecked = $(this).find('.mlsimport-import-checkbox').prop('checked'); |
| 145 |
const isMandatory = $(this).data('is-mandatory') === 'true'; |
| 146 |
|
| 147 |
if (status === 'mandatory' && isMandatory) { |
| 148 |
$(this).show(); |
| 149 |
} else if (status === 'selected' && (isChecked || isMandatory)) { |
| 150 |
$(this).show(); |
| 151 |
} else if (status === 'not_selected' && !isChecked && !isMandatory) { |
| 152 |
$(this).show(); |
| 153 |
} |
| 154 |
}); |
| 155 |
|
| 156 |
// Update the "no results" message |
| 157 |
updateNoResultsMessage(); |
| 158 |
} |
| 159 |
|
| 160 |
/** |
| 161 |
* Filter table rows based on first letter |
| 162 |
* |
| 163 |
* @param {string} letter - The first letter to filter by |
| 164 |
*/ |
| 165 |
function filterFieldsByAlphabet(letter) { |
| 166 |
// If letter is empty or 'all', show all rows |
| 167 |
if (!letter || letter === 'all') { |
| 168 |
$('.mlsimport-field-row').show(); |
| 169 |
return; |
| 170 |
} |
| 171 |
|
| 172 |
// Hide all rows first |
| 173 |
$('.mlsimport-field-row').hide(); |
| 174 |
|
| 175 |
// Show rows that start with the specified letter |
| 176 |
$('.mlsimport-field-row').each(function() { |
| 177 |
const fieldName = $(this).data('field-key'); |
| 178 |
const firstLetter = fieldName.charAt(0).toUpperCase(); |
| 179 |
|
| 180 |
if (firstLetter === letter.toUpperCase()) { |
| 181 |
$(this).show(); |
| 182 |
} |
| 183 |
}); |
| 184 |
|
| 185 |
// Update the "no results" message |
| 186 |
updateNoResultsMessage(); |
| 187 |
} |
| 188 |
|
| 189 |
/** |
| 190 |
* Reset all filters and show all fields |
| 191 |
*/ |
| 192 |
function resetAllFilters() { |
| 193 |
// Reset search input |
| 194 |
$('#mlsimport-field-search').val(''); |
| 195 |
|
| 196 |
// Reset import filter dropdown |
| 197 |
$('#mlsimport-import-filter').val('all'); |
| 198 |
|
| 199 |
// Reset alphabetical filter |
| 200 |
$('.mlsimport-alpha-filter').removeClass('active'); |
| 201 |
$('.mlsimport-alpha-filter[data-letter="all"]').addClass('active'); |
| 202 |
|
| 203 |
// Show all rows |
| 204 |
$('.mlsimport-field-row').show(); |
| 205 |
|
| 206 |
// Hide the "no results" message |
| 207 |
$('.mlsimport-no-results').hide(); |
| 208 |
} |
| 209 |
|
| 210 |
/** |
| 211 |
* Check if there are any visible rows and show/hide the "no results" message |
| 212 |
*/ |
| 213 |
function updateNoResultsMessage() { |
| 214 |
const visibleRows = $('.mlsimport-field-row:visible').length; |
| 215 |
|
| 216 |
if (visibleRows === 0) { |
| 217 |
// If no results message doesn't exist, create it |
| 218 |
if ($('.mlsimport-no-results').length === 0) { |
| 219 |
const colspan = $('.mlsimport-fields-table thead th').length; |
| 220 |
const message = $('<tr class="mlsimport-no-results"><td colspan="' + colspan + '">No fields found matching your criteria.</td></tr>'); |
| 221 |
$('#mlsimport-fields-table-body').append(message); |
| 222 |
} else { |
| 223 |
$('.mlsimport-no-results').show(); |
| 224 |
} |
| 225 |
} else { |
| 226 |
$('.mlsimport-no-results').hide(); |
| 227 |
} |
| 228 |
} |
| 229 |
|
| 230 |
/** |
| 231 |
* Navigate to a specific page |
| 232 |
* |
| 233 |
* @param {number} page - The page number to navigate to |
| 234 |
*/ |
| 235 |
function navigateToPage(page) { |
| 236 |
// This would typically reload the page with the new page parameter |
| 237 |
// For this implementation, we'll use JavaScript to update the form and submit |
| 238 |
|
| 239 |
// Create or update a hidden input for the page |
| 240 |
if ($('input[name="mlsimport_page"]').length > 0) { |
| 241 |
$('input[name="mlsimport_page"]').val(page); |
| 242 |
} else { |
| 243 |
$('<input>').attr({ |
| 244 |
type: 'hidden', |
| 245 |
name: 'mlsimport_page', |
| 246 |
value: page |
| 247 |
}).appendTo('.mlsimport-fields-form'); |
| 248 |
} |
| 249 |
|
| 250 |
// Submit the form |
| 251 |
$('.mlsimport-fields-form').submit(); |
| 252 |
} |
| 253 |
|
| 254 |
/** |
| 255 |
* Initialize bulk action functionality |
| 256 |
*/ |
| 257 |
function initializeBulkActions() { |
| 258 |
// Select All for Import checkboxes (only non-mandatory fields) |
| 259 |
jQuery('#mlsimport-select-all-import').on('click', function(e) { |
| 260 |
e.preventDefault(); |
| 261 |
bulkSaveImportSelections(true); |
| 262 |
}); |
| 263 |
|
| 264 |
jQuery('#mlsimport-select-none-import').on('click', function(e) { |
| 265 |
e.preventDefault(); |
| 266 |
bulkSaveImportSelections(false); |
| 267 |
}); |
| 268 |
|
| 269 |
// Select All for Admin Only checkboxes |
| 270 |
jQuery('#mlsimport-select-all-admin').on('click', function(e) { |
| 271 |
e.preventDefault(); |
| 272 |
bulkSaveAdminSelections(true); |
| 273 |
}); |
| 274 |
|
| 275 |
// Select None for Admin Only checkboxes |
| 276 |
jQuery('#mlsimport-select-none-admin').on('click', function(e) { |
| 277 |
e.preventDefault(); |
| 278 |
bulkSaveAdminSelections(false); |
| 279 |
}); |
| 280 |
|
| 281 |
// Update stats when a checkbox is clicked |
| 282 |
jQuery('.mlsimport-import-checkbox').on('change', function() { |
| 283 |
updateFieldStats(); |
| 284 |
}); |
| 285 |
} |
| 286 |
|
| 287 |
|
| 288 |
/** |
| 289 |
* Update the field statistics displayed at the top |
| 290 |
*/ |
| 291 |
window.updateFieldStats = function() { |
| 292 |
const totalFields = $('.mlsimport-field-row').length; |
| 293 |
const mandatoryFields = $('.mlsimport-field-row[data-is-mandatory="true"]').length; |
| 294 |
const selectedFields = $('.mlsimport-import-checkbox:checked').length + mandatoryFields; |
| 295 |
|
| 296 |
// Count fields with empty labels that are selected for import |
| 297 |
let missingLabels = 0; |
| 298 |
|
| 299 |
// Count for non-mandatory checked fields with missing labels |
| 300 |
$('.mlsimport-import-checkbox:checked').each(function() { |
| 301 |
const row = $(this).closest('tr'); |
| 302 |
const labelValue = row.find('.mlsimport-label-input').val(); |
| 303 |
|
| 304 |
if (!labelValue || labelValue.trim() === '') { |
| 305 |
missingLabels++; |
| 306 |
} |
| 307 |
}); |
| 308 |
|
| 309 |
// Count for mandatory fields with missing labels |
| 310 |
$('.mlsimport-field-row[data-is-mandatory="true"]').each(function() { |
| 311 |
const labelValue = $(this).find('.mlsimport-label-input').val(); |
| 312 |
|
| 313 |
if (!labelValue || labelValue.trim() === '') { |
| 314 |
missingLabels++; |
| 315 |
} |
| 316 |
}); |
| 317 |
|
| 318 |
// Update the stats display |
| 319 |
$('.mlsimport-field-stats li').eq(0).text(totalFields + ' fields total'); |
| 320 |
$('.mlsimport-field-stats li').eq(1).text(selectedFields + ' marked for import'); |
| 321 |
$('.mlsimport-field-stats li').eq(2).text(missingLabels + ' missing labels'); |
| 322 |
} |
| 323 |
|
| 324 |
/** |
| 325 |
* Initialize drag and drop functionality for reordering fields |
| 326 |
*/ |
| 327 |
function initializeDragAndDrop() { |
| 328 |
|
| 329 |
// Check if jQuery UI sortable is available |
| 330 |
if ($.fn.sortable) { |
| 331 |
|
| 332 |
// Make the entire row draggable. Interactive elements like |
| 333 |
// inputs and buttons remain excluded via the default `cancel` |
| 334 |
// option so they can still be used without initiating a drag. |
| 335 |
$('#mlsimport-fields-table-body').sortable({ |
| 336 |
cancel: 'input, textarea, button, select', |
| 337 |
helper: function(e, tr) { |
| 338 |
// Create a helper that maintains cell widths |
| 339 |
const $originals = tr.children(); |
| 340 |
const $helper = tr.clone(); |
| 341 |
|
| 342 |
$helper.children().each(function(index) { |
| 343 |
$(this).width($originals.eq(index).width()); |
| 344 |
}); |
| 345 |
|
| 346 |
return $helper; |
| 347 |
}, |
| 348 |
update: function(event, ui) { |
| 349 |
|
| 350 |
|
| 351 |
const $movedRow = jQuery(ui.item); |
| 352 |
const $prevRow = $movedRow.prev('.mlsimport-field-row'); |
| 353 |
|
| 354 |
const movingOrder = parseInt($movedRow.attr('data-field-order'), 10); |
| 355 |
|
| 356 |
// If no previous row exists, it's the first position |
| 357 |
if (!$prevRow.length) { |
| 358 |
|
| 359 |
saveFieldPosition(movingOrder, 0, 'before'); |
| 360 |
} else { |
| 361 |
// Normal case - moving after another row |
| 362 |
const prevOrder = parseInt($prevRow.attr('data-field-order'), 10); |
| 363 |
saveFieldPosition(movingOrder, prevOrder, 'after'); |
| 364 |
} |
| 365 |
|
| 366 |
refreshRowPositions(); |
| 367 |
} |
| 368 |
|
| 369 |
}); |
| 370 |
|
| 371 |
// Add visual cue for draggable rows |
| 372 |
$('.mlsimport-field-row td').css('cursor', 'move'); |
| 373 |
|
| 374 |
} else { |
| 375 |
console.error('jQuery UI sortable not available. Drag and drop ordering is disabled.'); |
| 376 |
} |
| 377 |
} |
| 378 |
|
| 379 |
|
| 380 |
|
| 381 |
|
| 382 |
})(jQuery); |
| 383 |
|
| 384 |
|
| 385 |
|
| 386 |
function initVirtualScrolling() { |
| 387 |
|
| 388 |
|
| 389 |
// Store all rows |
| 390 |
let allRows = jQuery('.mlsimport-field-row').toArray(); |
| 391 |
|
| 392 |
|
| 393 |
let rowsPerPage = 50; |
| 394 |
let visibleCount = rowsPerPage; |
| 395 |
let loading = false; |
| 396 |
|
| 397 |
// Make sure only initial rows are visible by hiding everything and then showing first 50 |
| 398 |
jQuery('.mlsimport-field-row').hide(); |
| 399 |
|
| 400 |
for (let i = 0; i < Math.min(rowsPerPage, allRows.length); i++) { |
| 401 |
jQuery(allRows[i]).show(); |
| 402 |
} |
| 403 |
|
| 404 |
// Add loading indicator if there are more than 50 rows |
| 405 |
if (allRows.length > rowsPerPage) { |
| 406 |
jQuery('<div class="mlsimport-loading" style="text-align: center; padding: 20px; margin-top: 20px; background: #f0f0f0; border-top: 1px solid #ddd;">Scroll down to load more fields...</div>') |
| 407 |
.insertAfter('.mlsimport-fields-table'); |
| 408 |
|
| 409 |
// Detect scroll |
| 410 |
jQuery(window).on('scroll', function() { |
| 411 |
if (loading) return; |
| 412 |
|
| 413 |
// Check if user has scrolled near the bottom |
| 414 |
let scrollPosition = jQuery(window).scrollTop() + jQuery(window).height(); |
| 415 |
let documentHeight = jQuery(document).height(); |
| 416 |
|
| 417 |
if (scrollPosition > documentHeight - 300) { |
| 418 |
loadMoreRows(); |
| 419 |
} |
| 420 |
}); |
| 421 |
} |
| 422 |
|
| 423 |
// Function to load more rows |
| 424 |
function loadMoreRows() { |
| 425 |
loading = true; |
| 426 |
jQuery('.mlsimport-loading').text('Loading more fields...').show(); |
| 427 |
|
| 428 |
// Simulate loading delay for visual feedback |
| 429 |
setTimeout(function() { |
| 430 |
// Show next batch of rows |
| 431 |
let endIndex = Math.min(visibleCount + rowsPerPage, allRows.length); |
| 432 |
|
| 433 |
for (let i = visibleCount; i < endIndex; i++) { |
| 434 |
jQuery(allRows[i]).show(); |
| 435 |
} |
| 436 |
|
| 437 |
visibleCount = endIndex; |
| 438 |
loading = false; |
| 439 |
|
| 440 |
// Update or remove loading message |
| 441 |
if (visibleCount >= allRows.length) { |
| 442 |
jQuery('.mlsimport-loading').remove(); // Remove instead of just changing text |
| 443 |
} else { |
| 444 |
jQuery('.mlsimport-loading').text('Scroll down to load more fields...'); |
| 445 |
} |
| 446 |
}, 300); |
| 447 |
} |
| 448 |
} |
| 449 |
|
| 450 |
// Complete row movement implementation with debugging |
| 451 |
function initRowReordering() { |
| 452 |
|
| 453 |
|
| 454 |
// Attach click handlers to up buttons |
| 455 |
jQuery('.mlsimport-move-up').on('click', function(e) { |
| 456 |
e.preventDefault(); |
| 457 |
e.stopPropagation(); // Prevent event bubbling |
| 458 |
var $row = jQuery(this).closest('.mlsimport-field-row'); |
| 459 |
|
| 460 |
moveRowUp($row); |
| 461 |
}); |
| 462 |
|
| 463 |
// Attach click handlers to down buttons |
| 464 |
jQuery('.mlsimport-move-down').on('click', function(e) { |
| 465 |
e.preventDefault(); |
| 466 |
e.stopPropagation(); // Prevent event bubbling |
| 467 |
var $row = jQuery(this).closest('.mlsimport-field-row'); |
| 468 |
|
| 469 |
moveRowDown($row); |
| 470 |
}); |
| 471 |
|
| 472 |
// Attach click handlers to move top buttons |
| 473 |
jQuery('.mlsimport-move-top').on('click', function(e) { |
| 474 |
e.preventDefault(); |
| 475 |
e.stopPropagation(); |
| 476 |
var $row = jQuery(this).closest('.mlsimport-field-row'); |
| 477 |
|
| 478 |
moveRowTop($row); |
| 479 |
}); |
| 480 |
|
| 481 |
// Attach click handlers to move bottom buttons |
| 482 |
jQuery('.mlsimport-move-bottom').on('click', function(e) { |
| 483 |
e.preventDefault(); |
| 484 |
e.stopPropagation(); |
| 485 |
var $row = jQuery(this).closest('.mlsimport-field-row'); |
| 486 |
|
| 487 |
moveRowBottom($row); |
| 488 |
}); |
| 489 |
} |
| 490 |
|
| 491 |
|
| 492 |
let moveUpTimer = null; |
| 493 |
let originalMovingOrder = null; |
| 494 |
let movingFieldKey = null; |
| 495 |
|
| 496 |
function moveRowUp($row) { |
| 497 |
const $prev = $row.prev('.mlsimport-field-row'); |
| 498 |
if (!$prev.length) { |
| 499 |
|
| 500 |
return; |
| 501 |
} |
| 502 |
|
| 503 |
try { |
| 504 |
const $button = $row.find('.mlsimport-move-up'); |
| 505 |
const oldTop = $button.offset().top; |
| 506 |
|
| 507 |
// Get the field key (unique identifier for the row) |
| 508 |
const fieldKey = $row.attr('data-field-key'); |
| 509 |
|
| 510 |
// If this is a new field being moved (not continuation of previous moves) |
| 511 |
if (fieldKey !== movingFieldKey) { |
| 512 |
movingFieldKey = fieldKey; |
| 513 |
originalMovingOrder = parseInt($row.attr('data-field-order'), 10); |
| 514 |
|
| 515 |
} |
| 516 |
|
| 517 |
|
| 518 |
|
| 519 |
// Get current orders for the swap |
| 520 |
const movingOrder = parseInt($row.attr('data-field-order'), 10); |
| 521 |
const targetOrder = parseInt($prev.attr('data-field-order'), 10); |
| 522 |
|
| 523 |
// Swap order attributes |
| 524 |
$row.attr('data-field-order', targetOrder); |
| 525 |
$prev.attr('data-field-order', movingOrder); |
| 526 |
|
| 527 |
// Move the row |
| 528 |
$row.insertBefore($prev); |
| 529 |
refreshRowPositions(); |
| 530 |
highlightRow($row); |
| 531 |
|
| 532 |
const newTop = $button.offset().top; |
| 533 |
const deltaY = newTop - oldTop; |
| 534 |
window.scrollBy(0, deltaY); |
| 535 |
|
| 536 |
// Clear existing timer |
| 537 |
if (moveUpTimer !== null) { |
| 538 |
clearTimeout(moveUpTimer); |
| 539 |
} |
| 540 |
|
| 541 |
// Set new timer |
| 542 |
moveUpTimer = setTimeout(function() { |
| 543 |
|
| 544 |
|
| 545 |
// Find the row by field key |
| 546 |
const $movedRow = jQuery(`.mlsimport-field-row[data-field-key="${movingFieldKey}"]`); |
| 547 |
|
| 548 |
if ($movedRow.length) { |
| 549 |
// Get the previous row |
| 550 |
const $prevRow = $movedRow.prev('.mlsimport-field-row'); |
| 551 |
|
| 552 |
if ($prevRow.length) { |
| 553 |
const prevOrder = parseInt($prevRow.attr('data-field-order'), 10); |
| 554 |
|
| 555 |
saveFieldPosition(originalMovingOrder, prevOrder, 'before'); |
| 556 |
} else { |
| 557 |
// Row is at the top |
| 558 |
|
| 559 |
saveFieldPosition(originalMovingOrder, 0, 'before'); |
| 560 |
} |
| 561 |
} |
| 562 |
|
| 563 |
// Reset tracking variables |
| 564 |
moveUpTimer = null; |
| 565 |
originalMovingOrder = null; |
| 566 |
movingFieldKey = null; |
| 567 |
}, 1000); |
| 568 |
|
| 569 |
} catch (e) { |
| 570 |
console.error("Error moving row up:", e); |
| 571 |
} |
| 572 |
} |
| 573 |
|
| 574 |
|
| 575 |
|
| 576 |
|
| 577 |
let moveDownTimer = null; |
| 578 |
let originalDownOrder = null; |
| 579 |
let movingDownFieldKey = null; |
| 580 |
|
| 581 |
// Queue for saving field positions so requests don't overlap |
| 582 |
let positionSaving = false; |
| 583 |
let positionQueue = []; |
| 584 |
|
| 585 |
function moveRowDown($row) { |
| 586 |
// Get the next row specifically with the same class |
| 587 |
var $next = $row.next('.mlsimport-field-row'); |
| 588 |
|
| 589 |
|
| 590 |
if ($next.length) { |
| 591 |
try { |
| 592 |
// Save exact mouse position on screen |
| 593 |
var mouseY = window.event.clientY; |
| 594 |
var mouseX = window.event.clientX; |
| 595 |
var $button = $row.find('.mlsimport-move-down'); |
| 596 |
var oldOffset = $button.offset(); |
| 597 |
var oldTop = oldOffset.top; |
| 598 |
|
| 599 |
// Get the field key (unique identifier for the row) |
| 600 |
const fieldKey = $row.attr('data-field-key'); |
| 601 |
|
| 602 |
// If this is a new field being moved (not continuation of previous moves) |
| 603 |
if (fieldKey !== movingDownFieldKey) { |
| 604 |
movingDownFieldKey = fieldKey; |
| 605 |
originalDownOrder = parseInt($row.attr('data-field-order'), 10); |
| 606 |
|
| 607 |
} |
| 608 |
|
| 609 |
// Get current order values |
| 610 |
var movingOrder = parseInt($row.attr('data-field-order'), 10); |
| 611 |
var targetOrder = parseInt($next.attr('data-field-order'), 10); |
| 612 |
|
| 613 |
// Swap the data-field-order values |
| 614 |
$row.attr('data-field-order', targetOrder); |
| 615 |
$next.attr('data-field-order', movingOrder); |
| 616 |
|
| 617 |
// Move the row in the UI |
| 618 |
$row.insertAfter($next); |
| 619 |
refreshRowPositions(); |
| 620 |
|
| 621 |
// Highlight to confirm movement |
| 622 |
highlightRow($row); |
| 623 |
|
| 624 |
// Get new position |
| 625 |
var newOffset = $button.offset(); |
| 626 |
|
| 627 |
// Find how much the position changed |
| 628 |
var deltaY = newOffset.top - oldTop; |
| 629 |
|
| 630 |
// Adjust scroll to keep relative position |
| 631 |
window.scrollBy(0, deltaY); |
| 632 |
|
| 633 |
// Clear existing timer |
| 634 |
if (moveDownTimer !== null) { |
| 635 |
clearTimeout(moveDownTimer); |
| 636 |
} |
| 637 |
|
| 638 |
// Set new timer |
| 639 |
moveDownTimer = setTimeout(function() { |
| 640 |
|
| 641 |
|
| 642 |
// Find the row by field key |
| 643 |
const $movedRow = jQuery(`.mlsimport-field-row[data-field-key="${movingDownFieldKey}"]`); |
| 644 |
|
| 645 |
if ($movedRow.length) { |
| 646 |
// Get the next row |
| 647 |
const $nextRow = $movedRow.next('.mlsimport-field-row'); |
| 648 |
|
| 649 |
if ($nextRow.length) { |
| 650 |
let nextOrder = parseInt($nextRow.attr('data-field-order'), 10); |
| 651 |
nextOrder=nextOrder-1; |
| 652 |
|
| 653 |
saveFieldPosition(originalDownOrder, nextOrder, 'after'); |
| 654 |
} else { |
| 655 |
// Row is at the bottom |
| 656 |
|
| 657 |
const lastOrder = parseInt($movedRow.attr('data-field-order'), 10); |
| 658 |
saveFieldPosition(originalDownOrder, lastOrder, 'after'); |
| 659 |
} |
| 660 |
} |
| 661 |
|
| 662 |
// Reset tracking variables |
| 663 |
moveDownTimer = null; |
| 664 |
originalDownOrder = null; |
| 665 |
movingDownFieldKey = null; |
| 666 |
}, 1000); |
| 667 |
|
| 668 |
} catch (e) { |
| 669 |
console.error("Error moving row down:", e); |
| 670 |
} |
| 671 |
} else { |
| 672 |
console.log("No next row found, can't move down"); |
| 673 |
} |
| 674 |
} |
| 675 |
|
| 676 |
|
| 677 |
|
| 678 |
|
| 679 |
|
| 680 |
|
| 681 |
|
| 682 |
|
| 683 |
function saveFieldPosition(movingOrder, targetOrder, position) { |
| 684 |
positionQueue.push({ movingOrder, targetOrder, position }); |
| 685 |
processPositionQueue(); |
| 686 |
} |
| 687 |
|
| 688 |
function processPositionQueue() { |
| 689 |
if (positionSaving || positionQueue.length === 0 || window.mlsimportSaving) { |
| 690 |
return; |
| 691 |
} |
| 692 |
|
| 693 |
positionSaving = true; |
| 694 |
window.mlsimportSaving = true; |
| 695 |
const item = positionQueue.shift(); |
| 696 |
|
| 697 |
jQuery('.mlsimport-move-up, .mlsimport-move-down').prop('disabled', true); |
| 698 |
let nonce = ''; |
| 699 |
if (jQuery('#mlsimport_field_selector_nonce').length > 0) { |
| 700 |
nonce = jQuery('#mlsimport_field_selector_nonce').val(); |
| 701 |
} else if (typeof mlsimport_params !== 'undefined' && mlsimport_params.nonce) { |
| 702 |
nonce = mlsimport_params.nonce; |
| 703 |
} |
| 704 |
|
| 705 |
const $notification = jQuery('<div class="mlsimport-notification mlsimport-notification-info">Saving field position...</div>'); |
| 706 |
jQuery('body').append($notification).fadeIn(); |
| 707 |
|
| 708 |
jQuery.ajax({ |
| 709 |
url: mlsimport_params.ajax_url, |
| 710 |
type: 'POST', |
| 711 |
data: { |
| 712 |
action: 'mlsimport_save_field_position', |
| 713 |
security: nonce, |
| 714 |
moving_index: item.movingOrder, |
| 715 |
target_index: item.targetOrder, |
| 716 |
position: item.position |
| 717 |
}, |
| 718 |
success: function(response) { |
| 719 |
if (response.success) { |
| 720 |
showNotification('Field position saved.', 'success'); |
| 721 |
} else { |
| 722 |
showNotification('Error saving position: ' + (response.data || 'Unknown error'), 'error'); |
| 723 |
} |
| 724 |
}, |
| 725 |
error: function() { |
| 726 |
showNotification('Server error while saving position.', 'error'); |
| 727 |
}, |
| 728 |
complete: function() { |
| 729 |
jQuery('.mlsimport-move-up, .mlsimport-move-down').prop('disabled', false); |
| 730 |
$notification.remove(); |
| 731 |
positionSaving = false; |
| 732 |
window.mlsimportSaving = false; |
| 733 |
processPositionQueue(); |
| 734 |
if (typeof window.processSaveQueue === 'function') { |
| 735 |
window.processSaveQueue(); |
| 736 |
} |
| 737 |
} |
| 738 |
}); |
| 739 |
} |
| 740 |
|
| 741 |
|
| 742 |
// Highlight function |
| 743 |
function highlightRow($row) { |
| 744 |
$row.css('background-color', '#ffffd0'); |
| 745 |
setTimeout(function() { |
| 746 |
$row.css('background-color', ''); |
| 747 |
}, 500); |
| 748 |
} |
| 749 |
|
| 750 |
// Refresh data-field-order attributes and debug positions |
| 751 |
function refreshRowPositions() { |
| 752 |
jQuery('#mlsimport-fields-table-body .mlsimport-field-row').each(function(index) { |
| 753 |
jQuery(this).attr('data-field-order', index); |
| 754 |
jQuery(this).find('.field-position').text((index + 1) + '. '); |
| 755 |
}); |
| 756 |
} |
| 757 |
|
| 758 |
// Move a row directly to the top |
| 759 |
function moveRowTop($row) { |
| 760 |
const $first = jQuery('.mlsimport-field-row').first(); |
| 761 |
if ($row.is($first)) return; |
| 762 |
|
| 763 |
const $button = $row.find('.mlsimport-move-top'); |
| 764 |
const oldTop = $button.offset().top; |
| 765 |
const originalOrder = parseInt($row.attr('data-field-order'), 10); |
| 766 |
|
| 767 |
$row.insertBefore($first); |
| 768 |
refreshRowPositions(); |
| 769 |
highlightRow($row); |
| 770 |
|
| 771 |
const newTop = $button.offset().top; |
| 772 |
window.scrollBy(0, newTop - oldTop); |
| 773 |
|
| 774 |
saveFieldPosition(originalOrder, 0, 'before'); |
| 775 |
} |
| 776 |
|
| 777 |
// Move a row directly to the bottom |
| 778 |
function moveRowBottom($row) { |
| 779 |
const $last = jQuery('.mlsimport-field-row').last(); |
| 780 |
if ($row.is($last)) return; |
| 781 |
|
| 782 |
const $button = $row.find('.mlsimport-move-bottom'); |
| 783 |
const oldTop = $button.offset().top; |
| 784 |
const originalOrder = parseInt($row.attr('data-field-order'), 10); |
| 785 |
const lastOrder = parseInt($last.attr('data-field-order'), 10); |
| 786 |
|
| 787 |
$row.insertAfter($last); |
| 788 |
refreshRowPositions(); |
| 789 |
highlightRow($row); |
| 790 |
|
| 791 |
const newTop = $button.offset().top; |
| 792 |
window.scrollBy(0, newTop - oldTop); |
| 793 |
|
| 794 |
saveFieldPosition(originalOrder, lastOrder, 'after'); |
| 795 |
} |
| 796 |
|
| 797 |
|
| 798 |
/** |
| 799 |
* Handles the sorting dropdown functionality |
| 800 |
*/ |
| 801 |
function initializeFieldSorting() { |
| 802 |
// Add change event handler to the sorting dropdown |
| 803 |
jQuery('#mlsimport-field-sort').on('change', function() { |
| 804 |
const sortValue = jQuery(this).val(); |
| 805 |
sortFields(sortValue); |
| 806 |
}); |
| 807 |
} |
| 808 |
|
| 809 |
/** |
| 810 |
* Initialize tooltips for row action buttons using jQuery UI |
| 811 |
*/ |
| 812 |
function initializeMoveButtonTooltips() { |
| 813 |
if (jQuery.fn.tooltip) { |
| 814 |
jQuery(document).tooltip({ |
| 815 |
items: '.mlsimport-row-actions .mlsimport-move-btn', |
| 816 |
classes: { 'ui-tooltip': 'mlsimport-move-tooltip' } |
| 817 |
}); |
| 818 |
} else { |
| 819 |
console.warn('jQuery UI tooltip not available.'); |
| 820 |
} |
| 821 |
} |
| 822 |
|
| 823 |
/** |
| 824 |
* Sort fields based on selected criteria |
| 825 |
* |
| 826 |
* @param {string} sortBy - The field to sort by |
| 827 |
*/ |
| 828 |
// Debug and fix for label sorting |
| 829 |
function sortFields(sortBy) { |
| 830 |
|
| 831 |
const $tbody = jQuery('#mlsimport-fields-table-body'); |
| 832 |
let $rows = $tbody.find('tr.mlsimport-field-row').toArray(); |
| 833 |
|
| 834 |
// Split sort value into criteria and direction |
| 835 |
const [criteria, direction] = sortBy.split('_'); |
| 836 |
|
| 837 |
|
| 838 |
// Sort the rows based on the selected criteria |
| 839 |
$rows.sort(function(a, b) { |
| 840 |
const $a = jQuery(a); |
| 841 |
const $b = jQuery(b); |
| 842 |
let result = 0; |
| 843 |
|
| 844 |
// Debug the actual elements to make sure we're accessing correctly |
| 845 |
// if (criteria === 'label') { |
| 846 |
|
| 847 |
// } |
| 848 |
|
| 849 |
switch (criteria) { |
| 850 |
case 'label': |
| 851 |
// Sort by the label input value |
| 852 |
let labelA = $a.find('.mlsimport-label-input').val() || ''; |
| 853 |
let labelB = $b.find('.mlsimport-label-input').val() || ''; |
| 854 |
|
| 855 |
labelA = labelA.toLowerCase(); |
| 856 |
labelB = labelB.toLowerCase(); |
| 857 |
|
| 858 |
if (labelA === '' && labelB !== '') return 1; |
| 859 |
if (labelA !== '' && labelB === '') return -1; |
| 860 |
if (labelA === '' && labelB === '') { |
| 861 |
return $a.data('field-key').toLowerCase().localeCompare($b.data('field-key').toLowerCase()); |
| 862 |
} |
| 863 |
|
| 864 |
result = labelA.localeCompare(labelB); |
| 865 |
break; |
| 866 |
|
| 867 |
case 'postmeta': |
| 868 |
let pmA = ($a.find('.mlsimport-postmeta-input').val() || '').toLowerCase(); |
| 869 |
let pmB = ($b.find('.mlsimport-postmeta-input').val() || '').toLowerCase(); |
| 870 |
result = pmA.localeCompare(pmB); |
| 871 |
break; |
| 872 |
|
| 873 |
case 'category': |
| 874 |
let catA = ($a.find('.mlsimport-field-taxonomy select option:selected').text() || '').toLowerCase(); |
| 875 |
let catB = ($b.find('.mlsimport-field-taxonomy select option:selected').text() || '').toLowerCase(); |
| 876 |
result = catA.localeCompare(catB); |
| 877 |
break; |
| 878 |
|
| 879 |
case 'import': |
| 880 |
let impA = $a.find('.mlsimport-import-checkbox').is(':checked'); |
| 881 |
let impB = $b.find('.mlsimport-import-checkbox').is(':checked'); |
| 882 |
if (impA === impB) { |
| 883 |
result = 0; |
| 884 |
} else { |
| 885 |
result = impA ? -1 : 1; // selected first |
| 886 |
} |
| 887 |
break; |
| 888 |
|
| 889 |
case 'hidden': |
| 890 |
let hidA = $a.find('.mlsimport-admin-checkbox').is(':checked'); |
| 891 |
let hidB = $b.find('.mlsimport-admin-checkbox').is(':checked'); |
| 892 |
if (hidA === hidB) { |
| 893 |
result = 0; |
| 894 |
} else { |
| 895 |
result = hidA ? -1 : 1; // selected first |
| 896 |
} |
| 897 |
break; |
| 898 |
|
| 899 |
default: |
| 900 |
const defaultNameA = $a.data('field-key').toLowerCase(); |
| 901 |
const defaultNameB = $b.data('field-key').toLowerCase(); |
| 902 |
result = defaultNameA.localeCompare(defaultNameB); |
| 903 |
break; |
| 904 |
} |
| 905 |
|
| 906 |
// Apply direction for ascending/descending sorts or selected/unselected |
| 907 |
if (criteria === 'import' || criteria === 'hidden') { |
| 908 |
if (direction === 'unselected') { |
| 909 |
result = -result; |
| 910 |
} |
| 911 |
} else if (direction === 'desc') { |
| 912 |
result = -result; |
| 913 |
} |
| 914 |
|
| 915 |
return result; |
| 916 |
}); |
| 917 |
|
| 918 |
// Reattach the sorted rows to the table |
| 919 |
jQuery.each($rows, function(index, row) { |
| 920 |
$tbody.append(row); |
| 921 |
}); |
| 922 |
|
| 923 |
// Visual feedback that sorting occurred |
| 924 |
$tbody.fadeOut(100).fadeIn(100); |
| 925 |
} |
| 926 |
|
| 927 |
/** |
| 928 |
* Show a notification message to the user |
| 929 |
* |
| 930 |
* @param {string} message - The message to display |
| 931 |
* @param {string} type - The type of message ('success', 'error', 'info') |
| 932 |
*/ |
| 933 |
function showNotification(message, type) { |
| 934 |
console.log('doing notification'); |
| 935 |
// Create notification element if it doesn't exist |
| 936 |
if (jQuery('.mlsimport-notification').length === 0) { |
| 937 |
jQuery('<div class="mlsimport-notification"></div>').appendTo('body'); |
| 938 |
} |
| 939 |
|
| 940 |
// Set message and type |
| 941 |
jQuery('.mlsimport-notification') |
| 942 |
.attr('class', 'mlsimport-notification mlsimport-notification-' + type) |
| 943 |
.text(message) |
| 944 |
.fadeIn() |
| 945 |
.delay(3000) |
| 946 |
.fadeOut(); |
| 947 |
} |
| 948 |
|
| 949 |
/** |
| 950 |
* Save import selections for all visible fields in bulk |
| 951 |
* |
| 952 |
* @param {boolean} checked - Whether checkboxes should be checked or not |
| 953 |
*/ |
| 954 |
function bulkSaveImportSelections(checked) { |
| 955 |
const fields = {}; |
| 956 |
|
| 957 |
jQuery('.mlsimport-import-checkbox:not([disabled]):visible').each(function() { |
| 958 |
const $checkbox = jQuery(this); |
| 959 |
const $row = $checkbox.closest('.mlsimport-field-row'); |
| 960 |
const fieldKey = $row.data('field-key'); |
| 961 |
|
| 962 |
$checkbox.prop('checked', checked); |
| 963 |
fields[fieldKey] = checked ? 1 : 0; |
| 964 |
|
| 965 |
// Add saving indicator similar to progressive-save.js |
| 966 |
addBulkSavingIndicator($row, '.mlsimport-field-import'); |
| 967 |
}); |
| 968 |
|
| 969 |
if (Object.keys(fields).length === 0) { |
| 970 |
updateFieldStats(); |
| 971 |
return; |
| 972 |
} |
| 973 |
|
| 974 |
jQuery.ajax({ |
| 975 |
url: mlsimport_params.ajax_url, |
| 976 |
type: 'POST', |
| 977 |
data: { |
| 978 |
action: 'mlsimport_save_bulk_import', |
| 979 |
security: mlsimport_params.nonce, |
| 980 |
fields: fields |
| 981 |
}, |
| 982 |
success: function(response) { |
| 983 |
const status = response.success ? 'success' : 'error'; |
| 984 |
jQuery.each(fields, function(key) { |
| 985 |
const $row = jQuery('.mlsimport-field-row[data-field-key="' + key + '"]'); |
| 986 |
updateBulkSavingIndicator($row, status, '.mlsimport-field-import'); |
| 987 |
}); |
| 988 |
updateFieldStats(); |
| 989 |
}, |
| 990 |
error: function() { |
| 991 |
jQuery.each(fields, function(key) { |
| 992 |
const $row = jQuery('.mlsimport-field-row[data-field-key="' + key + '"]'); |
| 993 |
updateBulkSavingIndicator($row, 'error', '.mlsimport-field-import'); |
| 994 |
}); |
| 995 |
} |
| 996 |
}); |
| 997 |
} |
| 998 |
|
| 999 |
/** |
| 1000 |
* Save admin visibility selections for all visible fields in bulk |
| 1001 |
* |
| 1002 |
* @param {boolean} checked - Whether checkboxes should be checked or not |
| 1003 |
*/ |
| 1004 |
function bulkSaveAdminSelections(checked) { |
| 1005 |
const fields = {}; |
| 1006 |
|
| 1007 |
jQuery('.mlsimport-admin-checkbox:visible').each(function() { |
| 1008 |
const $checkbox = jQuery(this); |
| 1009 |
const $row = $checkbox.closest('.mlsimport-field-row'); |
| 1010 |
const fieldKey = $row.data('field-key'); |
| 1011 |
|
| 1012 |
$checkbox.prop('checked', checked); |
| 1013 |
fields[fieldKey] = checked ? 1 : 0; |
| 1014 |
|
| 1015 |
addBulkSavingIndicator($row, '.mlsimport-field-admin'); |
| 1016 |
}); |
| 1017 |
|
| 1018 |
if (Object.keys(fields).length === 0) { |
| 1019 |
updateFieldStats(); |
| 1020 |
return; |
| 1021 |
} |
| 1022 |
|
| 1023 |
jQuery.ajax({ |
| 1024 |
url: mlsimport_params.ajax_url, |
| 1025 |
type: 'POST', |
| 1026 |
data: { |
| 1027 |
action: 'mlsimport_save_bulk_admin', |
| 1028 |
security: mlsimport_params.nonce, |
| 1029 |
fields: fields |
| 1030 |
}, |
| 1031 |
success: function(response) { |
| 1032 |
const status = response.success ? 'success' : 'error'; |
| 1033 |
jQuery.each(fields, function(key) { |
| 1034 |
const $row = jQuery('.mlsimport-field-row[data-field-key="' + key + '"]'); |
| 1035 |
updateBulkSavingIndicator($row, status, '.mlsimport-field-admin'); |
| 1036 |
}); |
| 1037 |
updateFieldStats(); |
| 1038 |
}, |
| 1039 |
error: function() { |
| 1040 |
jQuery.each(fields, function(key) { |
| 1041 |
const $row = jQuery('.mlsimport-field-row[data-field-key="' + key + '"]'); |
| 1042 |
updateBulkSavingIndicator($row, 'error', '.mlsimport-field-admin'); |
| 1043 |
}); |
| 1044 |
} |
| 1045 |
}); |
| 1046 |
} |
| 1047 |
|
| 1048 |
// Helper to add saving indicator for bulk updates |
| 1049 |
function addBulkSavingIndicator($row, columnSelector) { |
| 1050 |
const $element = $row.find(columnSelector); |
| 1051 |
$element.find('.save-indicator').remove(); |
| 1052 |
$element.append('<span class="save-indicator" style="margin-left: 5px; box-sizing: border-box; display: inline-block; width: 16px; height: 16px; border: 2px solid #635BFF; border-radius: 50%; border-top-color: transparent; animation: mlsimport-spin 1s linear infinite;"></span>'); |
| 1053 |
|
| 1054 |
if (!jQuery('#mlsimport-spin-animation').length) { |
| 1055 |
jQuery('head').append('<style id="mlsimport-spin-animation">@keyframes mlsimport-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style>'); |
| 1056 |
} |
| 1057 |
} |
| 1058 |
|
| 1059 |
// Helper to update indicator after bulk save |
| 1060 |
function updateBulkSavingIndicator($row, status, columnSelector) { |
| 1061 |
const $element = $row.find(columnSelector); |
| 1062 |
const $indicator = $element.find('.save-indicator'); |
| 1063 |
|
| 1064 |
if (status === 'success') { |
| 1065 |
$indicator.css({ |
| 1066 |
'border': 'none', |
| 1067 |
'animation': 'none', |
| 1068 |
'color': '#46b450', |
| 1069 |
'font-size': '16px' |
| 1070 |
}).html('✓'); |
| 1071 |
|
| 1072 |
setTimeout(function() { |
| 1073 |
$indicator.fadeOut(500, function() { |
| 1074 |
$indicator.remove(); |
| 1075 |
}); |
| 1076 |
}, 1000); |
| 1077 |
} else { |
| 1078 |
$indicator.css({ |
| 1079 |
'border': 'none', |
| 1080 |
'animation': 'none', |
| 1081 |
'color': '#dc3232', |
| 1082 |
'font-size': '16px' |
| 1083 |
}).html('✕'); |
| 1084 |
} |
| 1085 |
} |
| 1086 |
|
| 1087 |
/** |
| 1088 |
* Progressive Field Loading - Replaces the virtual scrolling with timed display |
| 1089 |
* This shows fields one by one with a smooth animation regardless of scrolling |
| 1090 |
*/ |
| 1091 |
function initProgressiveLoading() { |
| 1092 |
console.log("Progressive loading initialization started"); |
| 1093 |
|
| 1094 |
// Store all rows |
| 1095 |
let allRows = jQuery('.mlsimport-field-row').toArray(); |
| 1096 |
console.log("Found " + allRows.length + " total rows"); |
| 1097 |
|
| 1098 |
// Hide all rows initially - ensure none are visible at start |
| 1099 |
jQuery('.mlsimport-field-row').hide().css('opacity', 0); |
| 1100 |
|
| 1101 |
// Create progress bar container |
| 1102 |
const progressContainer = jQuery('<div class="mlsimport-loading-progress" style="position: sticky; top: 32px; z-index: 100; padding: 10px; background: #f9f9f9; border-bottom: 1px solid #ddd; text-align: center;"></div>'); |
| 1103 |
const progressText = jQuery('<div class="mlsimport-loading-text">Loading fields: <span class="mlsimport-loading-count">0</span> of ' + allRows.length + '</div>'); |
| 1104 |
const progressBar = jQuery('<div class="mlsimport-progress-bar" style="height: 10px; background: #eee; margin-top: 5px; border-radius: 5px;"><div class="mlsimport-progress-fill" style="width: 0%; height: 100%; background: #635BFF; border-radius: 5px; transition: width 0.3s;"></div></div>'); |
| 1105 |
|
| 1106 |
progressContainer.append(progressText).append(progressBar); |
| 1107 |
|
| 1108 |
// Add progress container at the top of the table |
| 1109 |
jQuery('.mlsimport-field-selector-container').prepend(progressContainer); |
| 1110 |
|
| 1111 |
// Variables for loading control |
| 1112 |
let loadedCount = 0; |
| 1113 |
let batchSize = 10; // How many rows to show at once |
| 1114 |
let interval = 7; // Milliseconds between batches (adjust for speed) |
| 1115 |
let isLoading = true; |
| 1116 |
|
| 1117 |
// Function to update progress display |
| 1118 |
function updateProgress(count) { |
| 1119 |
const percentage = Math.floor((count / allRows.length) * 100); |
| 1120 |
jQuery('.mlsimport-loading-count').text(count); |
| 1121 |
jQuery('.mlsimport-progress-fill').css('width', percentage + '%'); |
| 1122 |
|
| 1123 |
// If complete, remove progress or change to completion message |
| 1124 |
if (count >= allRows.length) { |
| 1125 |
setTimeout(function() { |
| 1126 |
progressContainer.fadeOut(500, function() { |
| 1127 |
progressContainer.remove(); |
| 1128 |
}); |
| 1129 |
}, 1000); |
| 1130 |
} |
| 1131 |
} |
| 1132 |
|
| 1133 |
// Start loading rows with a slight delay |
| 1134 |
setTimeout(function() { |
| 1135 |
const loadingInterval = setInterval(function() { |
| 1136 |
if (!isLoading) { |
| 1137 |
clearInterval(loadingInterval); |
| 1138 |
return; |
| 1139 |
} |
| 1140 |
|
| 1141 |
// Load next batch of rows |
| 1142 |
let endIndex = Math.min(loadedCount + batchSize, allRows.length); |
| 1143 |
|
| 1144 |
for (let i = loadedCount; i < endIndex; i++) { |
| 1145 |
jQuery(allRows[i]) |
| 1146 |
.css('opacity', 0) |
| 1147 |
.show() |
| 1148 |
.animate({opacity: 1}, 300); |
| 1149 |
} |
| 1150 |
|
| 1151 |
loadedCount = endIndex; |
| 1152 |
updateProgress(loadedCount); |
| 1153 |
|
| 1154 |
// Check if we're done |
| 1155 |
if (loadedCount >= allRows.length) { |
| 1156 |
isLoading = false; |
| 1157 |
clearInterval(loadingInterval); |
| 1158 |
console.log("All fields have been loaded"); |
| 1159 |
|
| 1160 |
|
| 1161 |
|
| 1162 |
} |
| 1163 |
}, interval); |
| 1164 |
}, 500); // Small initial delay before starting |
| 1165 |
|
| 1166 |
// No control buttons needed - just let the loading progress automatically |
| 1167 |
} |