HEX
Server: LiteSpeed
System: Linux premium140.web-hosting.com 4.18.0-553.89.1.lve.el8.x86_64 #1 SMP Wed Dec 10 13:58:50 UTC 2025 x86_64
User: ukqcurpj (1011)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/ukqcurpj/www/wp-content/plugins/paid-memberships-pro/js/pmpro-dashboard.js
/**
 * PMPro Dashboard - Updated for 3-column grid
 * This code allows users to reorder dashboard widgets via drag and drop.
 * It also disables the default WordPress postbox functionality to prevent conflicts.
 * This script uses jQuery UI's sortable functionality to manage the drag-and-drop behavior.
 * It stores the initial positions of the widgets and animates them to their new positions when reordered.
 * The new order is saved via AJAX when the user stops dragging a widget.
 */

jQuery(document).ready(function () {

	// ARIA live region helper for announcements
	function ensureLiveRegion() {
		if (!jQuery('#pmpro-dashboard-live-region').length) {
			jQuery('body').append('<div id="pmpro-dashboard-live-region" role="status" aria-live="polite" aria-atomic="true" style="position:absolute;left:-9999px;height:1px;width:1px;overflow:hidden;"></div>');
		}
	}

	// Function to announce drag actions
	function announceDrag($item, action) {
		ensureLiveRegion();
		const label = $item.find('.hndle').text().trim() || $item.attr('id');
		const text = label + ' ' + action;
		jQuery('#pmpro-dashboard-live-region').text(text);
	}

	let pmproDashboardPositions = {};
	let isAnimating = false;
	
	const dashboardForm = jQuery('#dashboard-widgets', '#pmpro-dashboard-form');

	// Store initial positions
	function storePositions() {
		pmproDashboardPositions = {};
		dashboardForm.children('.postbox').each(function() {
			if (this.id) {
				pmproDashboardPositions[this.id] = this.getBoundingClientRect();
			}
		});
	}

	// Animate elements to their new positions
	function animateToNewPositions(excludeElement) {
		if (isAnimating) return;
		isAnimating = true;

		dashboardForm.children('.postbox').not(excludeElement).each(function() {
			const oldRect = pmproDashboardPositions[this.id];
			const newRect = this.getBoundingClientRect();
			
			if (!oldRect) return;
			
			const dx = oldRect.left - newRect.left;
			const dy = oldRect.top - newRect.top;
			
			if (Math.abs(dx) > 1 || Math.abs(dy) > 1) {
				// Apply transform instantly
				this.style.transition = 'none';
				this.style.transform = `translate(${dx}px, ${dy}px)`;
				
				// Force reflow
				this.offsetHeight;
				
				// Animate back to natural position
				this.style.transition = 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
				this.style.transform = 'translate(0, 0)';
			}
		});

		// Clean up after animation
		setTimeout(() => {
			dashboardForm.children('.postbox').each(function() {
				this.style.transition = '';
				this.style.transform = '';
			});
			isAnimating = false;
		}, 300);
	}

	dashboardForm.sortable({
		items: '.postbox[role="listitem"]',
		handle: '.hndle',
		cursor: 'move',
		opacity: 0.8,
		placeholder: 'ui-sortable-placeholder',
		tolerance: 'pointer',
		distance: 10, // Reduce distance for easier drag initiation
		delay: 8, // Reduce delay for more responsive feel
		containment: 'parent', // Less restrictive containment
		start: function(event, ui) {
			// Store positions before drag starts
			storePositions();
			ui.item.addClass('pmpro-dragging');
			ui.item.attr('aria-grabbed', 'true');
			announceDrag(ui.item, 'picked up');
			
			// Copy grid column span from dragged item to placeholder
			const columnSpan = ui.item.css('grid-column');
			const spanClass = ui.item.attr('class').match(/pmpro-colspan-(\d+)/);
			
			if (spanClass) {
				// Ensure we don't exceed 3 columns in the new grid
				const spanValue = Math.min(parseInt(spanClass[1]));
				ui.placeholder.addClass('pmpro-colspan-' + spanValue);
			} else if (columnSpan && columnSpan !== 'auto') {
				ui.placeholder.css('grid-column', columnSpan);
			}
			
			// Set placeholder height to match dragged item
			const itemHeight = ui.item.height();
			ui.placeholder.css({
				'height': itemHeight + 'px',
				'box-sizing': 'border-box'
			});
		},

		change: function(event, ui) {
			// Only animate when placeholder position actually changes
			if (!isAnimating) {
				requestAnimationFrame(() => {
					animateToNewPositions(ui.item);
					// Update positions after animation starts
					setTimeout(() => {
						storePositions();
					}, 10);
				});
			}
		},

		over: function(event, ui) {
			// Force placeholder visibility when over the sortable area
			ui.placeholder.show();
		},

		out: function(event, ui) {
			// Keep placeholder visible even when temporarily outside
			ui.placeholder.show();
		},

		beforeStop: function(event, ui) {
			// Clean up any ongoing animations
			dashboardForm.children('.postbox').each(function() {
				this.style.transition = '';
				this.style.transform = '';
			});
		},

		stop: function(event, ui) {
			ui.item.removeClass('pmpro-dragging');
			ui.item.attr('aria-grabbed', 'false');
			announceDrag(ui.item, 'dropped');
			
			// Clean up placeholder classes and styles - Updated for 3-column grid
			ui.placeholder.removeClass(function (index, className) {
				return (className.match(/(^|\s)pmpro-colspan-\S+/g) || []).join(' ');
			});
			ui.placeholder.css({
				'grid-column': '',
				'height': ''
			});
			
			isAnimating = false;
		},

		update: function(event, ui) {
			// Add drop animation
			ui.item.addClass('pmpro-just-dropped');
			setTimeout(function() {
				ui.item.removeClass('pmpro-just-dropped');
			}, 250);

			// Get all metabox IDs in their new order
			var newOrder = [];
			jQuery('.postbox', dashboardForm).each(function() {
				var id = jQuery(this).attr('id');
				if (id) {
					newOrder.push(id);
				}
			});
						
			// Save the new order via AJAX
			if (newOrder.length > 0) {
				var nonceValue = jQuery('#pmpro_metabox_nonce').val();

				if (!nonceValue) {
					console.error('Nonce field not found or empty');
					return;
				}

				// Save the new sort order
				savePosition(newOrder, nonceValue);
			}
		}
	});

	/**
	 * Keyboard Accessibility for Dragging
	 * Allows users to pick up, move, and drop items using keyboard keys.
	 * Uses space/enter to pick up and drop, arrow keys to move.
	 */
	let $dragged = null;

	jQuery('#dashboard-widgets').on('keydown', '.hndle', function(e) {
		const $item = jQuery(this).closest('.postbox[role="listitem"]');

		if (($dragged === null) && (e.key === ' ' || e.key === 'Enter')) {
			// Pick up the item
			e.preventDefault();
			$dragged = $item;
			$item.attr('aria-grabbed', 'true').addClass('pmpro-dragged-by-keyboard');
			announceDrag($item, 'Picked up (use arrows to move, enter/space to drop)');
		} else if ($dragged && $item[0] === $dragged[0]) {
			// While holding an item, allow up/down/left/right to move
			if (['ArrowUp', 'ArrowLeft'].includes(e.key)) {
				e.preventDefault();
				let $prev = $item.prevAll('.postbox[role="listitem"]').first();
				if ($prev.length) {
					$prev.before($item);
					announceDrag($item, 'moved');
					$item.find('.hndle').focus();
				}
			} else if (['ArrowDown', 'ArrowRight'].includes(e.key)) {
				e.preventDefault();
				let $next = $item.nextAll('.postbox[role="listitem"]').first();
				if ($next.length) {
					$next.after($item);
					announceDrag($item, 'moved');
					$item.find('.hndle').focus();
				}
			} else if (e.key === ' ' || e.key === 'Enter') {
				// Drop
				e.preventDefault();
				$item.attr('aria-grabbed', 'false').removeClass('pmpro-dragged-by-keyboard');
				announceDrag($item, 'dropped');
				$dragged = null;
				// Optionally: trigger your save order logic here (AJAX)
				var newOrder = [];
				jQuery('.postbox[role="listitem"]', dashboardForm).each(function() {
					var id = jQuery(this).attr('id');
					if (id) {
						newOrder.push(id);
					}
				});
				if (newOrder.length > 0) {
					var nonceValue = jQuery('#pmpro_metabox_nonce').val();
					if (nonceValue) {
						// Save the new sort order
						savePosition(newOrder, nonceValue);
					}
				}
			} else if (e.key === 'Escape') {
				// Cancel
				e.preventDefault();
				$item.attr('aria-grabbed', 'false').removeClass('pmpro-dragged-by-keyboard');
				announceDrag($item, 'cancelled');
				$dragged = null;
			}
		}
	});

	// Visual focus for grabbed item
	jQuery(document).on('focusin focusout', '.hndle', function(event) {
		jQuery(this).closest('.postbox[role="listitem"]').toggleClass('pmpro-keyboard-focus', event.type === 'focusin');
	});

	// Disable WordPress postbox functionality here
	// This prevents conflicts with our custom drag-and-drop functionality.
	if (typeof postboxes !== 'undefined') {
		postboxes.handle_click = function() { return false; };
		postboxes.add_postbox_toggles = function() { return false; };
	}

	// Helper function to save the new order via AJAX
	function savePosition( newOrder, nonceValue ) {
		if (typeof ajaxurl === 'undefined') {
			console.error('AJAX URL is not defined. Ensure that the script is enqueued properly.');
			return;
		}
		// Ensure nonce value is valid
		if (!nonceValue || nonceValue === '') {
			console.error('Nonce value is not defined or empty. Ensure the nonce field exists in the form.');
			return;
		}
		// Make the AJAX request to save the new order
		jQuery.ajax({
			url: ajaxurl,
			type: 'POST',
			data: {
				action: 'pmpro_save_metabox_order',
				pmpro_metabox_nonce: nonceValue,
				order: newOrder.join(',')
			},
			dataType: 'json',
			timeout: 5000,
			error: function(xhr, status, error) {
				console.error('AJAX Error - Status:', status);
				console.error('AJAX Error - Error:', error);
			}
	});
	}
});