(function( $ ) {
'use strict';
$(document).ready( function() {
var itemList = $('#item-list'), // Container of item list
maxLevel = 6,
dragAxis = false,
dragTabSize = 20,
hierarchicalValue = ( typeof contentOrderSort.hierarchical !== 'undefined' ) ? contentOrderSort.hierarchical : contentOrderSort.hirarchical,
isHierarchical = ( String( hierarchicalValue ) === 'true' ),
disableParentChange = ! isHierarchical,
isSavingOrder = false,
sort_started = {}, // For data related to the dragged element when dragging started
sort_finished = {}; // For data related to the dragged element when dragging has finished
// console.log(contentOrderSort); // Data passed from PHP via wp_localize_script
if ( ! isHierarchical ) {
// Non-hierarchical lists should only support top-level reordering.
maxLevel = 1;
// Locking drag to vertical axis prevents right-drift from triggering invalid nest attempts.
dragAxis = 'y';
// Keep placeholder-based drop valid even when pointer drifts right.
dragTabSize = 9999;
}
// Make item list into nested sortable
// Ref: https://api.jqueryui.com/sortable/
// Ref: https://github.com/ilikenwf/nestedSortable
// See options at https://github.com/ilikenwf/nestedSortable/blob/master/jquery.mjs.nestedSortable.js#L37
// Ref: https://ilikenwf.github.io/example.html (demo)
itemList.nestedSortable({
// Disable nesting if set to true
protectRoot: false,
// Disable moving sub-item to a different parent or up the nested structure
disableParentChange: disableParentChange,
isTree: true,
// Forces the placeholder to have a size.
forcePlaceholderSize: true,
// Restricts sort start click to the specified element.
// Allows for a helper element to be used for dragging display.
// If set to "clone", then the element will be cloned and the clone will be dragged.
helper: 'clone',
// Use vertical-only dragging for non-hierarchical post types.
axis: dragAxis,
listType: 'ul',
items: 'li',
toleranceElement: '> div', // Direct children of the li element
handle: 'div', // The same
for toleranceElement is set as the drag handle
// Specifies which mode to use for testing whether the item being moved is hovering over another item.
// If set to 'pointr', is when the mouse pointer overlaps the other item.
tolerance: 'pointer',
// The maximum depth of nested items the list can accept.
maxLevels: maxLevel,
// Defines the opacity of the helper while sorting.
opacity: 0.6,
placeholder: 'ui-sortable-placeholder',
// Whether the sortable items should revert to their new positions using a smooth animation.
// If set as a number, it's in miliseconds
revert: 250,
// How far right or left (in pixels) the item has to travel
// in order to be nested or to be sent outside its current list. Default: 20
tabSize: dragTabSize,
// This event is triggered when sorting starts.
start: function (event, ui) {
if ( isSavingOrder ) {
return false;
}
// console.log('ui.item -- start');
// console.log(ui.item);
sort_started.item = ui.item; // The jQuery object representing the current dragged element.
sort_started.prev = ui.item.prev(':not(".ui-sortable-placeholder")');
sort_started.next = ui.item.next(':not(".ui-sortable-placeholder")');
},
// This event is triggered when the user stopped sorting and the DOM position has changed.
update: function (event, ui) {
// console.log('ui.item -- update');
// console.log(ui.item);
// Elements of the "Updating order..." notice
var updateNotice = $('#updating-order-notice'), // Wrapper
spinner = $('#spinner-img'), // Spinner
updateSuccess = $('.updating-order-notice .dashicons.dashicons-saved'); // Check mark
ui.item.find('div.row-content:first').append(updateNotice);
// Reset the state of the "Updating order..." indicator
$(spinner).show();
$(updateSuccess).hide();
$(updateNotice).css('background-color','#eee').fadeIn();
// Get the end items where the item was placed
// console.log('sort_finished');
// console.log(sort_finished);
sort_finished.item = ui.item; // The jQuery object representing the current dragged element.
// sort_finished.prev = ui.item.prev(':not(".ui-sortable-placeholder")');
// sort_finished.next = ui.item.next(':not(".ui-sortable-placeholder")');
// If an item is moved as a child of another item
// it will be inserted inside the ul.child-list with a data-parent
if ( ui.item.parent('.child-list').length ) {
// if ( ui.item.siblings('.list-item').length ) {
// Do something
// } else {
// Let's set the item's parent ID here, taking from ul.child-list's data-parent info
sort_finished.item.attr('data-parent', ui.item.parent('.child-list').attr('data-parent'));
// }
} else {
sort_finished.item.attr('data-parent', '0');
}
// console.log('sort_finished.prev');
// console.log(sort_finished.prev);
var list_offset = parseInt(sort_finished.item.index());
sort_finished.item.attr('data-menu-order', list_offset);
// Get attributes
var attributes = {};
$.each(sort_finished.item[0].attributes, function () {
attributes[this.name] = this.value;
});
// console.log('attributes: ' + cleanStringify(attributes));
// Data for ajax call
var dataArgs = {
action: contentOrderSort.action, // from wp_localize_script
item_parent: 0, // We only deal with top-level items, not child items
start: 0, // Start processing menu_order update in DB from item with menu_order defined here
nonce: contentOrderSort.nonce,
post_id: sort_finished.item.attr('data-id'),
menu_order: sort_finished.item.attr('data-menu-order'),
excluded_items: {},
post_type: sort_started.item.attr('data-post-type'),
attributes: attributes,
};
// console.log('dataArgs: ' + cleanStringify(dataArgs));
isSavingOrder = true;
// AJAX call to update menu_order for items in the list
$.ajax({
type: "POST",
url: ajaxurl,
data: dataArgs,
success: function(response) {
// console.log(response);
// Update the state of the "Updating order..." indicator
$(spinner).hide();
$(updateSuccess).show();
$(updateNotice).css('background-color','#cce5cc').delay(1000).fadeOut();
},
error: function(errorThrown) {
console.log(errorThrown);
},
complete: function() {
isSavingOrder = false;
}
});
}
});
});
// Convert object to simpler string for console.log. Ref: https://stackoverflow.com/a/48845206
// function cleanStringify(object) {
// if (object && typeof object === 'object') {
// object = copyWithoutCircularReferences([object], object);
// }
// return JSON.stringify(object);
// function copyWithoutCircularReferences(references, object) {
// var cleanObject = {};
// Object.keys(object).forEach(function(key) {
// var value = object[key];
// if (value && typeof value === 'object') {
// if (references.indexOf(value) < 0) {
// references.push(value);
// cleanObject[key] = copyWithoutCircularReferences(references, value);
// references.pop();
// } else {
// cleanObject[key] = '###_Circular_###';
// }
// } else if (typeof value !== 'function') {
// cleanObject[key] = value;
// }
// });
// return cleanObject;
// }
// }
})( jQuery );