1511 lines
48 KiB
JavaScript
1511 lines
48 KiB
JavaScript
|
/**
|
||
|
* ProcessWire Page List Process, JQuery Plugin
|
||
|
*
|
||
|
* Provides the Javascript/jQuery implementation of the PageList process when used with the JSON renderer
|
||
|
*
|
||
|
* ProcessWire 3.x (development), Copyright 2016 by Ryan Cramer
|
||
|
* https://processwire.com
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
function ProcessPageListInit() {
|
||
|
if(ProcessWire.config.ProcessPageList) {
|
||
|
$('#' + ProcessWire.config.ProcessPageList.containerID).ProcessPageList(ProcessWire.config.ProcessPageList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$(document).ready(function() {
|
||
|
ProcessPageListInit();
|
||
|
});
|
||
|
|
||
|
(function($) {
|
||
|
|
||
|
$.fn.ProcessPageList = function(customOptions) {
|
||
|
|
||
|
/**
|
||
|
* List of options that may be passed to the plugin
|
||
|
*
|
||
|
*/
|
||
|
var options = {
|
||
|
|
||
|
// mode: 'select' or 'actions', currently this is automatically determined based on the element the PageList is attached to
|
||
|
mode: '',
|
||
|
|
||
|
// default number of pages to show before pagination
|
||
|
limit: 35,
|
||
|
|
||
|
// the page ID that starts the list
|
||
|
rootPageID: 0,
|
||
|
|
||
|
// show the page identified by 'rootPageID' ?
|
||
|
showRootPage: true,
|
||
|
|
||
|
// the page ID currently selected
|
||
|
selectedPageID: 0,
|
||
|
|
||
|
// id of the admin page
|
||
|
adminPageID: 2,
|
||
|
|
||
|
// id of the trash page
|
||
|
trashPageID: 7,
|
||
|
|
||
|
// language ID, if applicable
|
||
|
langID: 0,
|
||
|
|
||
|
// in 'select' mode, allow no value to be selected (to abort a selected value)
|
||
|
selectAllowUnselect: false,
|
||
|
|
||
|
// show the 'currently selected' page header? (should be false on multi-selection)
|
||
|
selectShowPageHeader: true,
|
||
|
|
||
|
// show the parent path in the selected page label?
|
||
|
selectShowPath: true,
|
||
|
|
||
|
// use multiple mode enhancements when select mode enabled?
|
||
|
selectMultiple: false,
|
||
|
|
||
|
// the label to click on to change the currently selected page
|
||
|
selectStartLabel: 'Change',
|
||
|
|
||
|
// the label to click on to cancel selecting a page
|
||
|
selectCancelLabel: 'Cancel',
|
||
|
|
||
|
// the label to click on to select a given page
|
||
|
selectSelectLabel: 'Select',
|
||
|
|
||
|
// the label to click on to unselect a selected page
|
||
|
selectUnselectLabel: 'Unselect',
|
||
|
|
||
|
// label used for 'more' in paginated lists
|
||
|
moreLabel: 'More',
|
||
|
|
||
|
// label used for 'trash' button in Move mode
|
||
|
trashLabel: 'Trash',
|
||
|
|
||
|
// label used for the move instruction
|
||
|
moveInstructionLabel: "Click and drag to move",
|
||
|
|
||
|
// href attribute of 'select' link
|
||
|
selectSelectHref: '#',
|
||
|
|
||
|
// href attribute of 'unselect' link
|
||
|
selectUnselectHref: '#',
|
||
|
|
||
|
// URL where page lists are loaded from
|
||
|
ajaxURL: ProcessWire.config.urls.admin + 'page/list/',
|
||
|
|
||
|
// URL where page move's should be posted
|
||
|
ajaxMoveURL: ProcessWire.config.urls.admin + 'page/sort/',
|
||
|
|
||
|
// classes for pagination
|
||
|
paginationClass: 'PageListPagination',
|
||
|
paginationCurrentClass: 'PageListPaginationCurrent',
|
||
|
paginationLinkClass: 'ui-state-default',
|
||
|
paginationLinkCurrentClass: 'ui-state-active',
|
||
|
paginationHoverClass: 'ui-state-hover',
|
||
|
paginationDisabledClass: 'ui-priority-secondary',
|
||
|
|
||
|
// pagination number that you want to open (to correspond with openPageIDs)
|
||
|
openPagination: 0,
|
||
|
|
||
|
// IDs of the pages that we want to automatically open (default none)
|
||
|
openPageIDs: [],
|
||
|
|
||
|
// pre-rendered data corresponding to openPageIDs, indexed by 'id-start', i.e. 123-0
|
||
|
openPageData: {},
|
||
|
|
||
|
// speed at which the slideUp/slideDown run (in ms)
|
||
|
speed: 200,
|
||
|
|
||
|
// whether or not hovering an item reveals its actions
|
||
|
useHoverActions: false,
|
||
|
|
||
|
// milliseconds delay between hovering an item and it revealing actions
|
||
|
hoverActionDelay: 250,
|
||
|
|
||
|
// milliseconds in fade time to reveal or hide hover actions
|
||
|
hoverActionFade: 150,
|
||
|
|
||
|
// show only edit and view actions, and make everything else extra actions
|
||
|
useNarrowActions: $('body').hasClass('pw-narrow-width'),
|
||
|
|
||
|
// markup for the spinner used when ajax calls are made
|
||
|
spinnerMarkup: "<span class='PageListLoading'><i class='ui-priority-secondary fa fa-fw fa-spin fa-spinner'></i></span>",
|
||
|
|
||
|
// session field name that holds page label format, when used
|
||
|
labelName: '',
|
||
|
|
||
|
// what to show in the PageListNumChildren quantity: 'children', 'total', 'children/total', 'total/children', or 'id'
|
||
|
// default is blank, which implies 'children'
|
||
|
qtyType: '',
|
||
|
};
|
||
|
|
||
|
// array of "123.0" (page_id.start) that are currently open (used in non-select mode only)
|
||
|
var currentOpenPageIDs = [];
|
||
|
|
||
|
// true when operations are occurring where we want to ignore clicks
|
||
|
var ignoreClicks = false;
|
||
|
|
||
|
var isModal = $("body").hasClass("modal") || $("body").hasClass("pw-iframe");
|
||
|
|
||
|
if(typeof ProcessWire.config.ProcessPageList != "undefined") {
|
||
|
$.extend(options, ProcessWire.config.ProcessPageList);
|
||
|
}
|
||
|
|
||
|
$.extend(options, customOptions);
|
||
|
|
||
|
return this.each(function(index) {
|
||
|
|
||
|
var $container = $(this);
|
||
|
var $outer;
|
||
|
var $root;
|
||
|
var $loading = $(options.spinnerMarkup);
|
||
|
var firstPagination = 0; // used internally by the getPaginationList() function
|
||
|
var curPagination = 0; // current page number used by getPaginationList() function
|
||
|
|
||
|
/**
|
||
|
* Initialize the Page List
|
||
|
*
|
||
|
*/
|
||
|
function init() {
|
||
|
|
||
|
$root = $("<div class='PageListRoot'></div>");
|
||
|
|
||
|
if($container.is(":input")) {
|
||
|
options.selectedPageID = $container.val();
|
||
|
if(!options.selectedPageID.length) options.selectedPageID = 0;
|
||
|
options.mode = 'select';
|
||
|
$container.before($root);
|
||
|
$outer = $container.closest('.InputfieldContent');
|
||
|
setupSelectMode();
|
||
|
} else {
|
||
|
options.mode = 'actions';
|
||
|
$container.append($root);
|
||
|
$outer = $container;
|
||
|
loadChildren(options.rootPageID > 0 ? options.rootPageID : 1, $root, 0, true);
|
||
|
/*
|
||
|
// longclick to initiate sort, still marinating on whether to support this
|
||
|
$(document).on('longclick', 'a.PageListPage', function() {
|
||
|
$(this).parent().find('.PageListActionMove > a').trigger('click');
|
||
|
});
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
$(document).on('pageListRefresh', function(e, pageID) {
|
||
|
// i.e. $(document).trigger('pageListRefresh', pageID);
|
||
|
refreshList(pageID);
|
||
|
});
|
||
|
|
||
|
if(options.useHoverActions) {
|
||
|
$root.addClass('PageListUseHoverActions');
|
||
|
setupHoverActions();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If hover actions enabled, setup events to hide/show hover actions
|
||
|
*
|
||
|
*/
|
||
|
function setupHoverActions() {
|
||
|
|
||
|
var hoverTimeout = null;
|
||
|
var hoverOutTimeout = null;
|
||
|
var $hoveredItem = null;
|
||
|
|
||
|
function showItem($item) {
|
||
|
var $actions = $item.find('.PageListActions');
|
||
|
if(!$actions.is(":visible") || $item.hasClass('PageListItemOpen')) {
|
||
|
// we confirm :visible so that we don't iterfere with admin themes that already
|
||
|
// make the PageList items visible with css hover states
|
||
|
$item.addClass('PageListItemHover');
|
||
|
$actions.css('display', 'inline').css('opacity', 0)
|
||
|
.animate({opacity: 1.0}, options.hoverActionFade);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function hideItem($item) {
|
||
|
var $actions = $item.find('.PageListActions');
|
||
|
$item.removeClass('PageListItemHover');
|
||
|
if($actions.is(":visible")) { // || $hoveredItem.hasClass('PageListItemOpen')) {
|
||
|
$actions.animate({opacity: 0}, options.hoverActionFade, function () {
|
||
|
$actions.hide();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$outer.on('keydown', '.PageListItem', function(e) {
|
||
|
// PR#1 makes page-list keyboard accessible
|
||
|
e = e || window.event;
|
||
|
if(e.keyCode == 0 || e.keyCode == 32) {
|
||
|
// spacebar
|
||
|
var $actions = $(this).find('.PageListActions');
|
||
|
if($actions.is(":visible")) {
|
||
|
$actions.css('display', 'none');
|
||
|
} else {
|
||
|
$actions.css('display', 'inline-block');
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$outer.on('mouseover', '.PageListItem', function(e) {
|
||
|
|
||
|
if($root.is(".PageListSorting") || $root.is(".PageListSortSaving")) return;
|
||
|
|
||
|
var $a = $(this).children('a').first();
|
||
|
if($a.length && !$a.is(':hover')) return;
|
||
|
|
||
|
$hoveredItem = $(this);
|
||
|
//console.log('pageX=' + e.pageX);
|
||
|
//console.log('offsetX=' + $hoveredItem.offset().left);
|
||
|
|
||
|
//var maxPageX = $(this).children('.PageListNumChildren').offset().left + 100;
|
||
|
//if(e.pageX > maxPageX) return;
|
||
|
|
||
|
if($hoveredItem.hasClass('PageListItemHover')) return;
|
||
|
var $item = $(this);
|
||
|
if(hoverTimeout) clearTimeout(hoverTimeout);
|
||
|
var delay = options.hoverActionDelay;
|
||
|
|
||
|
|
||
|
hoverTimeout = setTimeout(function() {
|
||
|
if($hoveredItem.attr('class') == $item.attr('class')) {
|
||
|
var $a = $hoveredItem.children('a').first();
|
||
|
if($a.length && !$a.is(':hover')) return;
|
||
|
var $hideItems = $outer.find(".PageListItemHover");
|
||
|
showItem($hoveredItem);
|
||
|
$hideItems.each(function() { hideItem($(this)); });
|
||
|
}
|
||
|
}, delay);
|
||
|
|
||
|
}).on('mouseout', '.PageListItem', function(e) {
|
||
|
if($root.is(".PageListSorting") || $root.is(".PageListSortSaving")) return;
|
||
|
var $item = $(this);
|
||
|
if($item.hasClass('PageListItemOpen')) return;
|
||
|
if(!$item.hasClass('PageListItemHover')) return;
|
||
|
var delay = options.hoverActionDelay * 0.7;
|
||
|
hoverOutTimeout = setTimeout(function() {
|
||
|
if($item.is(":hover")) return;
|
||
|
if($item.attr('class') == $hoveredItem.attr('class')) return;
|
||
|
hideItem($item);
|
||
|
}, delay);
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets up a mode where the user is given a "select" link for each page, rather than a list of actions
|
||
|
*
|
||
|
* When they hit "select" the list collapses and the selected page ID is populated into an input
|
||
|
*
|
||
|
*/
|
||
|
function setupSelectMode() {
|
||
|
|
||
|
var $actions = $("<ul></ul>").addClass('PageListActions PageListSelectActions actions');
|
||
|
var $pageLabel = $("<p></p>").addClass("PageListSelectName");
|
||
|
if(options.selectShowPageHeader) $pageLabel.append($loading);
|
||
|
|
||
|
var $action = $("<a></a>")
|
||
|
.addClass("PageListSelectActionToggle")
|
||
|
.addClass("PageListSelectActionToggleStart")
|
||
|
.attr('href', '#')
|
||
|
.text(options.selectStartLabel)
|
||
|
.on('click', function() {
|
||
|
|
||
|
if($(this).text() == options.selectStartLabel) {
|
||
|
loadChildren(options.rootPageID > 0 ? options.rootPageID : 1, $root, 0, true);
|
||
|
$(this).text(options.selectCancelLabel)
|
||
|
.removeClass('PageListSelectActionToggleStart').addClass('PageListSelectActionToggleCancel');
|
||
|
} else {
|
||
|
$(this).addClass('PageListSelectActionToggleStart').removeClass('PageListSelectActionToggleCancel');
|
||
|
$root.children(".PageList").slideUp(options.speed, function() {
|
||
|
$(this).remove();
|
||
|
});
|
||
|
$(this).text(options.selectStartLabel);
|
||
|
}
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
$actions.append($("<li></li>").append($action));
|
||
|
|
||
|
$root.append($("<div></div>").addClass('PageListSelectHeader').append($pageLabel).append($actions));
|
||
|
|
||
|
if(options.selectShowPageHeader) {
|
||
|
var ajaxURL = options.ajaxURL +
|
||
|
"?id=" + options.selectedPageID +
|
||
|
"&render=JSON&start=0&limit=0&lang=" + options.langID +
|
||
|
"&mode=" + options.mode;
|
||
|
if(options.labelName.length) ajaxURL += '&labelName=' + options.labelName;
|
||
|
$.getJSON(ajaxURL, function(data) {
|
||
|
var parentPath = '';
|
||
|
if(options.selectShowPath) {
|
||
|
parentPath = data.page.path;
|
||
|
if(parentPath.substring(-1) == '/') parentPath = parentPath.substring(0, parentPath.length-1);
|
||
|
parentPath = parentPath.substring(0, parentPath.lastIndexOf('/')+1);
|
||
|
parentPath = '<span class="detail">' + parentPath + '</span> ';
|
||
|
}
|
||
|
var label = options.selectedPageID > 0 ? parentPath + data.page.label : '';
|
||
|
$root.children(".PageListSelectHeader").find(".PageListSelectName").html(label);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Method that is triggered when the processChildren() method completes
|
||
|
*
|
||
|
*/
|
||
|
function loaded() {
|
||
|
ignoreClicks = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles pagination of PageList items
|
||
|
*
|
||
|
* @param int id ID of the page having children to show
|
||
|
* @param start Index that we are starting with in the current list
|
||
|
* @param int limit The limit being applied to the list (items per page)
|
||
|
* @param int total The total number of items in the list (excluding any limits)
|
||
|
* @return jQuery $list The pagination list ready for insertion
|
||
|
*
|
||
|
*/
|
||
|
function getPaginationList(id, start, limit, total) {
|
||
|
|
||
|
// console.log('getPaginationList(id=' + id + ', start=' + start + ", limit=" + limit + ", total=" + total + ')');
|
||
|
|
||
|
var maxPaginationLinks = 9;
|
||
|
var numPaginations = Math.ceil(total / limit);
|
||
|
curPagination = start >= limit ? Math.floor(start / limit) : 0;
|
||
|
|
||
|
if(curPagination == 0) {
|
||
|
firstPagination = 0;
|
||
|
|
||
|
} else if((curPagination-maxPaginationLinks+1) > firstPagination) {
|
||
|
firstPagination = curPagination - Math.floor(maxPaginationLinks / 2);
|
||
|
|
||
|
} else if(firstPagination > 0 && curPagination == firstPagination) {
|
||
|
firstPagination = curPagination - Math.ceil(maxPaginationLinks / 2);
|
||
|
}
|
||
|
|
||
|
|
||
|
// if we're on the last page of pagination links, then make the firstPagination static at the end
|
||
|
if(firstPagination > numPaginations - maxPaginationLinks) firstPagination = numPaginations - maxPaginationLinks;
|
||
|
|
||
|
if(firstPagination < 0) firstPagination = 0;
|
||
|
|
||
|
var $list = $("<ul></ul>").addClass(options.paginationClass).data('paginationInfo', {
|
||
|
start: start,
|
||
|
limit: limit,
|
||
|
total: total
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* paginationClick is the event function called when an item in the pagination nav is clicked
|
||
|
*
|
||
|
* It loads the new pages (via loadChildren) and then replaces the old pageList with the new one
|
||
|
*
|
||
|
*/
|
||
|
var paginationClick = function(e) {
|
||
|
var $curList = $(this).parents("ul." + options.paginationClass);
|
||
|
var info = $curList.data('paginationInfo');
|
||
|
if(!info) return false;
|
||
|
var start = parseInt($(this).attr('href')) * info.limit;
|
||
|
if(start === NaN) start = 0;
|
||
|
var $newList = getPaginationList(id, start, info.limit, info.total);
|
||
|
var $spinner = $(options.spinnerMarkup);
|
||
|
var $loading = $("<li> </li>").addClass(options.paginationDisabledClass).append($spinner.hide());
|
||
|
$curList.siblings(".PageList").remove(); // remove any open lists below current
|
||
|
$curList.replaceWith($newList);
|
||
|
$newList.append($loading);
|
||
|
$spinner.fadeIn('fast');
|
||
|
var $siblings = $newList.siblings().css('opacity', 0.5);
|
||
|
loadChildren(id, $newList.parent(), start, false, false, true, function() {
|
||
|
$spinner.fadeOut('fast', function() {
|
||
|
$loading.remove();
|
||
|
});
|
||
|
$newList.parent('.PageList').prev('.PageListItem').data('start', start);
|
||
|
updateOpenPageIDs();
|
||
|
});
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
var $separator = null;
|
||
|
var $blankItem = null;
|
||
|
|
||
|
for(var pagination = firstPagination, cnt = 0; pagination < numPaginations; pagination++, cnt++) {
|
||
|
|
||
|
var $a = $("<a></a>").html(pagination+1).attr('href', pagination).addClass(options.paginationLinkClass);
|
||
|
var $item = $("<li></li>").addClass(options.paginationClass + cnt).append($a); // .addClass('ui-state-default');
|
||
|
|
||
|
if(pagination == curPagination) {
|
||
|
//$item.addClass("PageListPaginationCurrent ui-state-focus");
|
||
|
$item.addClass(options.paginationCurrentClass).find("a")
|
||
|
.removeClass(options.paginationLinkClass)
|
||
|
.addClass(options.paginationLinkCurrentClass);
|
||
|
}
|
||
|
|
||
|
$list.append($item);
|
||
|
|
||
|
if(!$blankItem) {
|
||
|
$blankItem = $item.clone().removeClass(options.paginationCurrentClass + ' ' + options.paginationLinkCurrentClass);
|
||
|
$blankItem.find('a').removeClass(options.paginationLinkCurrentClass).addClass(options.paginationLinkClass);
|
||
|
}
|
||
|
// if(!$blankItem) $blankItem = $item.clone().removeClass('PageListPaginationCurrent').find('a').removeClass('ui-state-focus').addClass('ui-state-default');
|
||
|
if(!$separator) $separator = $blankItem.clone().removeClass(options.paginationLinkClass)
|
||
|
.addClass(options.paginationDisabledClass).html("…");
|
||
|
//if(!$separator) $separator = $blankItem.clone().html("…");
|
||
|
|
||
|
if(cnt >= maxPaginationLinks && pagination < numPaginations) {
|
||
|
$lastItem = $blankItem.clone();
|
||
|
$lastItem.find("a").text(numPaginations).attr('href', numPaginations-1);
|
||
|
$list.append($separator.clone()).append($lastItem);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if(firstPagination > 0) {
|
||
|
$firstItem = $blankItem.clone();
|
||
|
$firstItem.find("a").text("1").attr('href', '0').on('click', paginationClick);
|
||
|
$list.prepend($separator.clone()).prepend($firstItem);
|
||
|
}
|
||
|
|
||
|
//if(curPagination+1 < maxPaginationLinks && curPagination+1 < numPaginations) {
|
||
|
if(curPagination+1 < numPaginations) {
|
||
|
$nextBtn = $blankItem.clone();
|
||
|
$nextBtn.find("a").html("<i class='fa fa-angle-right'></i>").attr('href', curPagination+1);
|
||
|
$list.append($nextBtn);
|
||
|
}
|
||
|
|
||
|
if(curPagination > 0) {
|
||
|
$prevBtn = $blankItem.clone();
|
||
|
$prevBtn.find("a").attr('href', curPagination-1).html("<i class='fa fa-angle-left'></i>");
|
||
|
$list.prepend($prevBtn);
|
||
|
}
|
||
|
|
||
|
$list.find("a").on('click', paginationClick)
|
||
|
.on('mouseenter', function() {
|
||
|
$(this).addClass(options.paginationHoverClass);
|
||
|
}).on('mouseleave', function() {
|
||
|
$(this).removeClass(options.paginationHoverClass);
|
||
|
});
|
||
|
|
||
|
return $list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Load children via ajax call, attach them to $target and show.
|
||
|
*
|
||
|
* @param int id ID of the page having children to show
|
||
|
* @param jQuery $target Item to attach children to
|
||
|
* @param int start If not starting from first item, num of item to start with
|
||
|
* @param bool beginList Set to true if this is the first call to create the list
|
||
|
* @param bool pagination Set to false if you don't want pagination, otherwise leave it out
|
||
|
* @param bool replace Should any existing list be replaced (true) or appended (false)
|
||
|
*
|
||
|
*/
|
||
|
function loadChildren(id, $target, start, beginList, pagination, replace, callback) {
|
||
|
|
||
|
if(pagination == undefined) pagination = true;
|
||
|
if(replace == undefined) replace = false;
|
||
|
|
||
|
var processChildren = function(data) {
|
||
|
|
||
|
if(data && data.error) {
|
||
|
ProcessWire.alert(data.message);
|
||
|
$loading.hide();
|
||
|
ignoreClicks = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var $children = listChildren($(data.children));
|
||
|
var nextStart = data.start + data.limit;
|
||
|
//var openPageKey = id + '-' + start;
|
||
|
|
||
|
if($target.hasClass('PageListItem')) {
|
||
|
setNumChildren($target, data.page.numChildren, data.page.numTotal);
|
||
|
}
|
||
|
|
||
|
if(data.page.numChildren > nextStart) {
|
||
|
var $a = $("<a></a>").attr('href', nextStart).data('pageId', id).text(options.moreLabel).on('click', clickMore);
|
||
|
$children.append($("<ul></ul>").addClass('PageListActions actions').append($("<li></li>").addClass('PageListActionMore').append($a)));
|
||
|
}
|
||
|
if(pagination && (data.page.numChildren > nextStart || data.start > 0)) {
|
||
|
$children.prepend(getPaginationList(id, data.start, data.limit, data.page.numChildren));
|
||
|
}
|
||
|
|
||
|
$children.hide();
|
||
|
|
||
|
if(beginList) {
|
||
|
var $listRoot;
|
||
|
$listRoot = listChildren($(data.page));
|
||
|
if(options.showRootPage) $listRoot.children(".PageListItem").addClass("PageListItemOpen");
|
||
|
else $listRoot.children('.PageListItem').hide().parent('.PageList').addClass('PageListRootHidden');
|
||
|
$listRoot.append($children);
|
||
|
$target.append($listRoot);
|
||
|
|
||
|
} else if($target.hasClass('PageList')) {
|
||
|
|
||
|
var $newChildren = $children.children(".PageListItem, .PageListActions");
|
||
|
if(replace) {
|
||
|
// $target.children(".PageListItem, .PageListActions").replaceWith($newChildren); // doesn't work w/jQuery 1.9+
|
||
|
$target.children('.PageListItem, .PageListActions').remove();
|
||
|
}
|
||
|
$target.append($newChildren);
|
||
|
|
||
|
} else {
|
||
|
$target.after($children);
|
||
|
}
|
||
|
|
||
|
if($loading.parent().is('.PageListRoot')) {
|
||
|
$loading.hide();
|
||
|
} else {
|
||
|
$loading.fadeOut('fast');
|
||
|
}
|
||
|
|
||
|
if(replace) {
|
||
|
$children.show();
|
||
|
loaded();
|
||
|
if(callback != undefined) callback();
|
||
|
$container.trigger('pageListChildrenDone', data);
|
||
|
} else {
|
||
|
$children.slideDown(options.speed, function() {
|
||
|
loaded();
|
||
|
if(callback != undefined) callback();
|
||
|
$container.trigger('pageListChildrenDone', data);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
$children.prev('.PageListItem').data('start', data.start);
|
||
|
|
||
|
// if a pagination is requested to be opened, and it exists, then open it
|
||
|
/*
|
||
|
if(options.openPagination > 1) {
|
||
|
//var $a = $(".PageListPagination" + (options.openPagination-1) + ">a");
|
||
|
var $a = $(".PageListPagination a[href=" + (options.openPagination-1) + "]");
|
||
|
if($a.length > 0) {
|
||
|
$a.trigger('click');
|
||
|
options.openPagination = 0;
|
||
|
} else {
|
||
|
// last pagination link
|
||
|
$(".PageListPagination9 a").trigger('click');
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
};
|
||
|
|
||
|
if(!replace) $target.append($loading.fadeIn('fast'));
|
||
|
|
||
|
|
||
|
var key = id + '-' + start;
|
||
|
if(typeof options.openPageData[key] != "undefined"
|
||
|
&& !$target.hasClass('PageListID7') // trash
|
||
|
&& !$target.hasClass('PageListForceReload')) {
|
||
|
processChildren(options.openPageData[key]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// @teppokoivula PR #1052
|
||
|
var ajaxURL = options.ajaxURL +
|
||
|
"?id=" + id +
|
||
|
"&render=JSON&start=" + start +
|
||
|
"&lang=" + options.langID +
|
||
|
"&open=" + options.openPageIDs[0] +
|
||
|
"&mode=" + options.mode;
|
||
|
if(options.labelName.length) ajaxURL += '&labelName=' + options.labelName;
|
||
|
$.getJSON(ajaxURL)
|
||
|
.done(function(data, textStatus, jqXHR) {
|
||
|
processChildren(data);
|
||
|
})
|
||
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
||
|
processChildren({
|
||
|
error: 1,
|
||
|
message: !jqXHR.status ? options.ajaxNetworkError : options.ajaxUnknownError
|
||
|
});
|
||
|
});
|
||
|
// end #1052
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Given a list of pages, generates a list of them
|
||
|
*
|
||
|
* @param jQuery $children
|
||
|
*
|
||
|
*/
|
||
|
function listChildren($children) {
|
||
|
|
||
|
var $list = $("<div></div>").addClass("PageList");
|
||
|
var $ul = $list;
|
||
|
|
||
|
$children.each(function(n, child) {
|
||
|
$ul.append(listChild(child));
|
||
|
});
|
||
|
|
||
|
addClickEvents($ul);
|
||
|
|
||
|
return $list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a parent to reload its children on next access rather than using cached data
|
||
|
*
|
||
|
* @param $pageListItem .PageList or .PageListItem element
|
||
|
* @param reloadNow Reload the list now? (default=false)
|
||
|
*
|
||
|
*/
|
||
|
function setForceReload($pageListItem, reloadNow) {
|
||
|
|
||
|
if($pageListItem.hasClass('PageList')) $pageListItem = $pageListItem.prev('.PageListItem');
|
||
|
|
||
|
$pageListItem.addClass('PageListForceReload');
|
||
|
|
||
|
var id = $pageListItem.data('pageId');
|
||
|
var prefix = id + '-';
|
||
|
|
||
|
if(typeof options.openPageData != "undefined") {
|
||
|
// remove cached items from openPageData
|
||
|
var openPageData = {};
|
||
|
for(var key in options.openPageData) {
|
||
|
if(key.indexOf(prefix) === 0) {
|
||
|
// skip it
|
||
|
} else {
|
||
|
openPageData[key] = options.openPageData[key];
|
||
|
}
|
||
|
}
|
||
|
options.openPageData = openPageData;
|
||
|
}
|
||
|
|
||
|
if(typeof reloadNow != "undefined" && reloadNow) {
|
||
|
var $a = $pageListItem.children('a.PageListPage');
|
||
|
if($pageListItem.hasClass('PageListItemOpen')) {
|
||
|
$a.trigger('click');
|
||
|
setTimeout(function() { $a.trigger('click'); }, 250);
|
||
|
} else {
|
||
|
$a.trigger('click');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param $ul Any element that contains items needing click events attached
|
||
|
*
|
||
|
*/
|
||
|
function addClickEvents($ul) {
|
||
|
|
||
|
$("a.PageListPage", $ul).on('click', clickChild);
|
||
|
$ul.on('click', '.PageListActionMove a', clickMove);
|
||
|
// $(".PageListActionMove a", $ul).on('click', clickMove);
|
||
|
$(".PageListActionSelect a", $ul).on('click', clickSelect);
|
||
|
$(".PageListTriggerOpen:not(.PageListID1) > a.PageListPage", $ul).trigger('click');
|
||
|
$(".PageListActionExtras > a:not(.clickExtras)", $ul).addClass('clickExtras').on('click', clickExtras);
|
||
|
|
||
|
// if(options.useHoverActions) $(".PageListActionExtras > a", $ul).on('mouseover', clickExtras);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Given a single page, generates the list item for it
|
||
|
*
|
||
|
* @param map child
|
||
|
*
|
||
|
*/
|
||
|
function listChild(child) {
|
||
|
|
||
|
var $li = $("<div></div>").data('pageId', child.id).addClass('PageListItem').addClass('PageListTemplate_' + child.template);
|
||
|
var $a = $("<a></a>")
|
||
|
.attr('href', '#')
|
||
|
.attr('title', child.path)
|
||
|
.html(child.label)
|
||
|
.addClass('PageListPage label');
|
||
|
|
||
|
$li.addClass(child.numChildren > 0 ? 'PageListHasChildren' : 'PageListNoChildren').addClass('PageListID' + child.id);
|
||
|
if(child.status == 0) $li.addClass('PageListStatusOff disabled');
|
||
|
if(child.status & 2048) $li.addClass('PageListStatusUnpublished secondary');
|
||
|
if(child.status & 1024) $li.addClass('PageListStatusHidden secondary');
|
||
|
if(child.status & 512) $li.addClass('PageListStatusTemp secondary'); // typically combined with PageListStatusUnpublished
|
||
|
if(child.status & 16) $li.addClass('PageListStatusSystem');
|
||
|
if(child.status & 8) $li.addClass('PageListStatusSystem');
|
||
|
if(child.status & 4) $li.addClass('PageListStatusLocked');
|
||
|
if(child.addClass && child.addClass.length) $li.addClass(child.addClass);
|
||
|
if(child.type && child.type.length > 0) if(child.type == 'System') $li.addClass('PageListStatusSystem');
|
||
|
|
||
|
$(options.openPageIDs).each(function(n, id) {
|
||
|
id = parseInt(id);
|
||
|
if(child.id == id) $li.addClass('PageListTriggerOpen');
|
||
|
});
|
||
|
|
||
|
$li.append($a);
|
||
|
setNumChildren($li, child.numChildren, child.numTotal, true);
|
||
|
|
||
|
if(child.note && child.note.length) $li.append($("<span>" + child.note + "</span>").addClass('PageListNote detail'));
|
||
|
|
||
|
var $actions = $("<ul></ul>").addClass('PageListActions actions');
|
||
|
var links = options.rootPageID == child.id ? [] : [{ name: options.selectSelectLabel, url: options.selectSelectHref }];
|
||
|
if(options.mode == 'actions') {
|
||
|
links = child.actions;
|
||
|
} else if(options.selectAllowUnselect) {
|
||
|
if(child.id == $container.val()) links = [{ name: options.selectUnselectLabel, url: options.selectUnselectHref }];
|
||
|
}
|
||
|
|
||
|
var $lastAction = null;
|
||
|
var $extrasLink = null; // link that toggles extra actions
|
||
|
var extras = {}; // extra actions
|
||
|
var hasExtrasLink = false; // only referenced if options.useNarrowActions
|
||
|
|
||
|
if(options.useNarrowActions) {
|
||
|
for(var n = 0; n < links.length; n++) {
|
||
|
if(typeof links[n].extras != "undefined") hasExtrasLink = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$(links).each(function(n, action) {
|
||
|
var actionName;
|
||
|
if(action.name == options.selectSelectLabel) {
|
||
|
actionName = 'Select';
|
||
|
} else if(action.name == options.selectUnselectLabel) {
|
||
|
actionName = 'Select';
|
||
|
} else {
|
||
|
actionName = action.cn; // cn = className
|
||
|
if(options.useNarrowActions && hasExtrasLink
|
||
|
&& (actionName != 'Edit' && actionName != 'View' && actionName != 'Extras')) {
|
||
|
// move non-edit/view actions to extras when in narrow mode
|
||
|
extras[actionName] = action;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var $a = $("<a></a>").html(action.name).attr('href', action.url);
|
||
|
|
||
|
if(!isModal) {
|
||
|
if(action.cn == 'Edit') {
|
||
|
$a.addClass('pw-modal pw-modal-large pw-modal-longclick');
|
||
|
$a.attr('data-buttons', '#ProcessPageEdit > .Inputfields > .InputfieldSubmit .ui-button');
|
||
|
} else if(action.cn == 'View') {
|
||
|
$a.addClass('pw-modal pw-modal-large pw-modal-longclick');
|
||
|
}
|
||
|
}
|
||
|
if(typeof action.extras != "undefined") {
|
||
|
for(var key in action.extras) {
|
||
|
extras[key] = action.extras[key];
|
||
|
}
|
||
|
$extrasLink = $a;
|
||
|
}
|
||
|
|
||
|
var $action = $("<li></li>").addClass('PageListAction' + actionName).append($a);
|
||
|
if(actionName == 'Extras') $lastAction = $action;
|
||
|
else $actions.append($action);
|
||
|
});
|
||
|
|
||
|
if($extrasLink) $extrasLink.data('extras', extras);
|
||
|
|
||
|
if($lastAction) {
|
||
|
$actions.append($lastAction);
|
||
|
$lastAction.addClass('ui-priority-secondary');
|
||
|
}
|
||
|
|
||
|
$li.append($actions);
|
||
|
return $li;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get number of children for given .PageListItem
|
||
|
*
|
||
|
*/
|
||
|
function getNumChildren($item, getTotal) {
|
||
|
if(typeof getTotal == "undefined") getTotal = false;
|
||
|
var n;
|
||
|
if(getTotal) {
|
||
|
n = $item.attr('data-numTotal');
|
||
|
} else {
|
||
|
n = $item.attr('data-numChild');
|
||
|
}
|
||
|
return n && n.length > 0 ? parseInt(n) : 0;
|
||
|
}
|
||
|
|
||
|
function getNumTotal($item) {
|
||
|
return getNumChildren($item, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set number of children for given PageListItem
|
||
|
*
|
||
|
*/
|
||
|
function setNumChildren($item, numChildren, numTotal, addNew) {
|
||
|
|
||
|
if(typeof numTotal == "undefined") numTotal = numChildren;
|
||
|
if(typeof addNew == "undefined") addNew = false;
|
||
|
|
||
|
var $numChildren = addNew ? '' : $item.children('.PageListNumChildren');
|
||
|
var n = numChildren === false ? numTotal : numChildren;
|
||
|
|
||
|
if(addNew || !$numChildren.length) {
|
||
|
$numChildren = $('<span></span>').addClass('PageListNumChildren detail');
|
||
|
addNew = true;
|
||
|
}
|
||
|
|
||
|
if(n < 1) {
|
||
|
$item.removeClass('PageListHasChildren').addClass('PageListNoChildren');
|
||
|
if(numTotal !== false) numTotal = 0;
|
||
|
} else {
|
||
|
$item.removeClass('PageListNoChildren').addClass('PageListHasChildren');
|
||
|
}
|
||
|
|
||
|
if(numTotal === false) {
|
||
|
numTotal = getNumTotal($item);
|
||
|
} else {
|
||
|
$item.attr('data-numTotal', numTotal);
|
||
|
}
|
||
|
|
||
|
if(numChildren === false) {
|
||
|
numChildren = getNumChildren($item);
|
||
|
} else {
|
||
|
if(numChildren < 0) numChildren = 0;
|
||
|
$item.attr('data-numChild', numChildren);
|
||
|
}
|
||
|
|
||
|
var numLabel = '';
|
||
|
var slash;
|
||
|
switch(options.qtyType) {
|
||
|
case 'total':
|
||
|
numLabel = numTotal;
|
||
|
break;
|
||
|
case 'total/children':
|
||
|
slash = "<span class='ui-priority-secondary'>/</span>";
|
||
|
numLabel = numTotal > 0 && numTotal != numChildren ? numTotal + slash + numChildren : numTotal;
|
||
|
break;
|
||
|
case 'children/total':
|
||
|
slash = "<span class='ui-priority-secondary'>/</span>";
|
||
|
numLabel = numTotal > 0 && numTotal != numChildren ? numChildren + slash + numTotal : numTotal;
|
||
|
break;
|
||
|
case 'id':
|
||
|
numLabel = $item.data('pageId');
|
||
|
break;
|
||
|
default:
|
||
|
numLabel = numChildren;
|
||
|
}
|
||
|
if(!numLabel) numLabel = '';
|
||
|
$numChildren.html(numLabel);
|
||
|
|
||
|
if(addNew) $item.append($numChildren);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set total/descendants
|
||
|
*
|
||
|
*/
|
||
|
function setNumTotal($item, numTotal) {
|
||
|
setNumChildren($item, false, numTotal);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Recursively adjust total/descendants number up the tree by given amount (n)
|
||
|
*
|
||
|
*/
|
||
|
function adjustNumTotal($item, n) {
|
||
|
var numTotal = getNumTotal($item);
|
||
|
numTotal += n;
|
||
|
if(numTotal < 0) numTotal = 0;
|
||
|
setNumTotal($item, numTotal);
|
||
|
var $parentItem = $item.closest('.PageList').prev('.PageListItem');
|
||
|
if($parentItem.length) adjustNumTotal($parentItem, n);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extra actions button click handler
|
||
|
*
|
||
|
*/
|
||
|
function clickExtras(e) {
|
||
|
|
||
|
var $a = $(this);
|
||
|
var extras = $a.data('extras');
|
||
|
if(typeof extras == "undefined") return false;
|
||
|
|
||
|
var $li = $a.closest('.PageListItem');
|
||
|
var $actions = $a.closest('.PageListActions');
|
||
|
var $lastItem = null;
|
||
|
var $icon = $a.children('i.fa');
|
||
|
var $extraActions = $actions.find("li.PageListActionExtra");
|
||
|
|
||
|
/*
|
||
|
if($extraActions.length && e.type != 'click') {
|
||
|
// mouseover only opens, but a click is required to close
|
||
|
return;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
$icon.toggleClass('fa-flip-horizontal');
|
||
|
|
||
|
if($extraActions.length) {
|
||
|
$extraActions.fadeOut(100, function() {
|
||
|
$extraActions.remove();
|
||
|
});
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for(var extraKey in extras) {
|
||
|
|
||
|
var extra = extras[extraKey];
|
||
|
var $extraLink = $("<a />")
|
||
|
.addClass('PageListActionExtra PageListAction' + extra.cn)
|
||
|
.attr('href', extra.url)
|
||
|
.html(extra.name);
|
||
|
|
||
|
/*
|
||
|
if(extra.cn == 'Trash') {
|
||
|
// handler for trash action
|
||
|
$extraLink.on('click', function() {
|
||
|
trashPage($li);
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
} else
|
||
|
*/
|
||
|
if(typeof extra.ajax != "undefined" && extra.ajax == true) {
|
||
|
// ajax action
|
||
|
$extraLink.on('click', function () {
|
||
|
|
||
|
$li.find('.PageListActions').hide();
|
||
|
var $spinner = $(options.spinnerMarkup);
|
||
|
var href = $(this).attr('href');
|
||
|
var actionName = href.match(/[\?&]action=([-_a-zA-Z0-9]+)/)[1];
|
||
|
var pageID = parseInt(href.match(/[\?&]id=([0-9]+)/)[1]);
|
||
|
var tokenName = $("#PageListContainer").attr('data-token-name');
|
||
|
var tokenValue = $("#PageListContainer").attr('data-token-value');
|
||
|
var postData = {
|
||
|
action: actionName,
|
||
|
id: pageID,
|
||
|
};
|
||
|
postData[tokenName] = tokenValue;
|
||
|
$li.append($spinner);
|
||
|
|
||
|
$.post(href + '&render=json', postData, function (data) {
|
||
|
|
||
|
if (data.success) {
|
||
|
|
||
|
$li.fadeOut('fast', function() {
|
||
|
|
||
|
var addNew = false;
|
||
|
var removeItem = data.remove;
|
||
|
var refreshChildren = data.refreshChildren;
|
||
|
var $liNew = false;
|
||
|
|
||
|
if(typeof data.child != "undefined") {
|
||
|
// prepare update existing item
|
||
|
$liNew = listChild(data.child);
|
||
|
} else if(typeof data.newChild != "undefined") {
|
||
|
// prepare append new item
|
||
|
$liNew = listChild(data.newChild);
|
||
|
addNew = true;
|
||
|
}
|
||
|
|
||
|
// display a message for a second to let them know what was done
|
||
|
if($liNew) {
|
||
|
var $msg = $("<span />").addClass('notes').html(data.message);
|
||
|
$msg.prepend(" <i class='fa fa-check-square ui-priority-secondary'></i> ");
|
||
|
$liNew.append($msg);
|
||
|
addClickEvents($liNew);
|
||
|
}
|
||
|
|
||
|
if(addNew) {
|
||
|
// append new item
|
||
|
$spinner.fadeOut('normal', function() { $spinner.remove() });
|
||
|
$liNew.hide();
|
||
|
$li.after($liNew);
|
||
|
$liNew.slideDown();
|
||
|
setForceReload($liNew.closest('.PageList'));
|
||
|
} else if($liNew) {
|
||
|
// update existing item
|
||
|
if($li.hasClass('PageListItemOpen')) $liNew.addClass('PageListItemOpen');
|
||
|
$li.replaceWith($liNew);
|
||
|
}
|
||
|
|
||
|
$li.fadeIn('fast', function () {
|
||
|
// display message for 1 second, then remove
|
||
|
setTimeout(function () {
|
||
|
$msg.fadeOut('normal', function () {
|
||
|
var $parentItem = $liNew.closest('.PageList').prev('.PageListItem');
|
||
|
var numChildren = getNumChildren($parentItem);
|
||
|
var numTotal = getNumTotal($parentItem);
|
||
|
if(removeItem) {
|
||
|
numChildren--;
|
||
|
adjustNumTotal($parentItem, -1);
|
||
|
} else if(addNew) {
|
||
|
numChildren++;
|
||
|
adjustNumTotal($parentItem, 1);
|
||
|
}
|
||
|
setNumChildren($parentItem, numChildren, false);
|
||
|
setForceReload($parentItem);
|
||
|
if(removeItem) {
|
||
|
$liNew.next('.PageList').fadeOut('fast');
|
||
|
$liNew.fadeOut('fast', function() {
|
||
|
$liNew.remove();
|
||
|
});
|
||
|
} else {
|
||
|
$msg.remove();
|
||
|
}
|
||
|
});
|
||
|
}, 1000);
|
||
|
});
|
||
|
|
||
|
// refresh the children of the page represented by refreshChildren
|
||
|
if(refreshChildren) {
|
||
|
var $refreshParent = $(".PageListID" + refreshChildren);
|
||
|
if($refreshParent.length) setForceReload($refreshParent, true);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
} else {
|
||
|
// data.success === false, so display error
|
||
|
$spinner.remove();
|
||
|
ProcessWire.alert(data.message);
|
||
|
}
|
||
|
});
|
||
|
return false;
|
||
|
});
|
||
|
} else {
|
||
|
// some other action where the direct URL can be used, so we don't need to do anything
|
||
|
}
|
||
|
|
||
|
var $extraLinkItem = $("<li />").addClass('PageListActionExtra PageListAction' + extra.cn).append($extraLink);
|
||
|
$extraLink.hide();
|
||
|
|
||
|
if(extra.cn == 'Trash') {
|
||
|
$li.addClass('trashable');
|
||
|
// ensure the Trash item is always the last one
|
||
|
$lastItem = $extraLinkItem;
|
||
|
} else {
|
||
|
$actions.append($extraLinkItem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if($lastItem) $actions.append($lastItem);
|
||
|
|
||
|
$actions.find(".PageListActionExtra a").fadeIn(50, function() {
|
||
|
$(this).css('display', 'inline-block');
|
||
|
});
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Event called when a page label is clicked on
|
||
|
*
|
||
|
* @param event e
|
||
|
*
|
||
|
*/
|
||
|
function clickChild(e) {
|
||
|
|
||
|
var $t = $(this);
|
||
|
var $li = $t.parent('.PageListItem');
|
||
|
var id = $li.data('pageId');
|
||
|
|
||
|
if(ignoreClicks && !$li.hasClass("PageListTriggerOpen")) return false;
|
||
|
|
||
|
if($root.is(".PageListSorting") || $root.is(".PageListSortSaving")) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if($li.hasClass("PageListItemOpen")) {
|
||
|
var collapseThis = true;
|
||
|
if($li.hasClass('PageListID1') && !$li.hasClass('PageListForceReload') && options.mode != 'select') {
|
||
|
var $collapseItems = $(this).closest('.PageListRoot').find('.PageListItemOpen:not(.PageListID1)');
|
||
|
if($collapseItems.length) {
|
||
|
// collapse all open items, except homepage, when homepage link is collapsed
|
||
|
$root.find('.PageListItemOpen:not(.PageListID1)').each(function() {
|
||
|
$(this).children('a.PageListPage').trigger('click');
|
||
|
});
|
||
|
collapseThis = false;
|
||
|
}
|
||
|
}
|
||
|
if(collapseThis) {
|
||
|
$li.removeClass('PageListItemOpen').next(".PageList").slideUp(options.speed, function() {
|
||
|
$(this).remove();
|
||
|
});
|
||
|
}
|
||
|
} else {
|
||
|
$li.addClass('PageListItemOpen');
|
||
|
var numChildren = getNumChildren($li);
|
||
|
if(numChildren > 0 || $li.hasClass('PageListForceReload')) {
|
||
|
ignoreClicks = true;
|
||
|
var start = getOpenPageStart(id);
|
||
|
loadChildren(id, $li, start, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(options.mode != 'select') {
|
||
|
setTimeout(function() { updateOpenPageIDs() }, 250);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the pagination "start" index for the given open page ID
|
||
|
*
|
||
|
* @param id
|
||
|
* @returns {number}
|
||
|
*
|
||
|
*/
|
||
|
function getOpenPageStart(id) {
|
||
|
var start = 0;
|
||
|
for(n = 0; n < options.openPageIDs.length; n++) {
|
||
|
var key = options.openPageIDs[n];
|
||
|
if(key.indexOf('-') == -1) continue;
|
||
|
var parts = options.openPageIDs[n].split('-');
|
||
|
var _id = parseInt(parts[0]);
|
||
|
if(_id == id) {
|
||
|
start = parseInt(parts[1]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return start;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the currentOpenPageIDs list and cookie to reflect the current open pages
|
||
|
*
|
||
|
*/
|
||
|
function updateOpenPageIDs() {
|
||
|
currentOpenPageIDs = [];
|
||
|
$('.PageListItemOpen').each(function() {
|
||
|
var id = $(this).data('pageId');
|
||
|
var start = $(this).data('start');
|
||
|
if(typeof start == "undefined" || start === null) {
|
||
|
start = 0;
|
||
|
} else {
|
||
|
start = parseInt(start);
|
||
|
}
|
||
|
if(jQuery.inArray(id, currentOpenPageIDs) == -1) {
|
||
|
currentOpenPageIDs.push(id + '-' + start); // id.start
|
||
|
}
|
||
|
});
|
||
|
// console.log(currentOpenPageIDs);
|
||
|
$.cookie('pagelist_open', currentOpenPageIDs);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Force refresh the list that pageID appears in
|
||
|
*
|
||
|
* @param pageID
|
||
|
* @param animate
|
||
|
*
|
||
|
*/
|
||
|
function refreshList(pageID, animate) {
|
||
|
|
||
|
var $parentList = $('.PageListID' + pageID).parent('.PageList');
|
||
|
var $parentItem = $parentList.prev('.PageListItem');
|
||
|
if(!$parentItem.length) return;
|
||
|
|
||
|
var parentID = $parentItem.attr('class').match(/PageListID(\d+)/)[1];
|
||
|
var start = 0;
|
||
|
var $pagination = $parentList.children("ul." + options.paginationClass);
|
||
|
var paginationInfo = $pagination.data('paginationInfo');
|
||
|
var $removeItems = null;
|
||
|
|
||
|
if(paginationInfo) {
|
||
|
var $a = $pagination.find('.pw-link-active');
|
||
|
if($a.length) start = parseInt($a.attr('href')) * paginationInfo.limit;
|
||
|
if(start === NaN) start = 0;
|
||
|
}
|
||
|
if(parentID == 1) {
|
||
|
$removeItems = $parentList.children();
|
||
|
$parentList = $parentItem;
|
||
|
}
|
||
|
setForceReload($parentList);
|
||
|
loadChildren(parentID, $parentList, start, false, true, true, function() {
|
||
|
if($removeItems && $removeItems.length) $removeItems.remove();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Event called when the 'more' action/link is clicked on
|
||
|
*
|
||
|
* @param event e
|
||
|
*
|
||
|
*/
|
||
|
function clickMore(e) {
|
||
|
|
||
|
var $t = $(this);
|
||
|
var $actions = $t.parent('li').parent('ul.PageListActions');
|
||
|
var $pageList = $actions.parent('.PageList');
|
||
|
var id = $t.data('pageId');
|
||
|
var nextStart = parseInt($t.attr('href'));
|
||
|
|
||
|
loadChildren(id, $pageList, nextStart, false);
|
||
|
$actions.remove();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Event called when the 'move' action/link is clicked on
|
||
|
*
|
||
|
* @param event e
|
||
|
*
|
||
|
*/
|
||
|
function clickMove() {
|
||
|
|
||
|
if(ignoreClicks) return false;
|
||
|
|
||
|
|
||
|
var $t = $(this);
|
||
|
|
||
|
if($(".PageListItem:visible").length == 1) {
|
||
|
// no other items to sort/move to
|
||
|
$t.css('text-decoration', 'line-through').addClass('ui-state-disabled');
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
var $li = $t.parent('li').parent('ul.PageListActions').parent('.PageListItem');
|
||
|
|
||
|
if($li.hasClass("PageListItemOpen")) $li.children(".PageListPage").trigger('click'); // @somatonic PR163
|
||
|
|
||
|
// make an invisible PageList placeholder that allows 'move' action to create a child below this
|
||
|
$root.find('.PageListItemOpen').each(function() {
|
||
|
var numChildren = getNumChildren($(this));
|
||
|
// if there are children and the next sibling doesn't contain a visible .PageList, then don't add a placeholder
|
||
|
if(parseInt(numChildren) > 1 && $(this).next().find(".PageList:visible").length == 0) {
|
||
|
return;
|
||
|
}
|
||
|
var $ul = $("<div></div>").addClass('PageListPlaceholder').addClass('PageList');
|
||
|
$ul.append($("<div></div>").addClass('PageListItem PageListPlaceholderItem').html(' '));
|
||
|
$(this).after($ul);
|
||
|
//$(this).prepend($ul.clone());
|
||
|
//$(this).addClass('PageListItemNoSort');
|
||
|
});
|
||
|
|
||
|
var sortOptions = {
|
||
|
stop: stopMove,
|
||
|
helper: 'PageListItemHelper',
|
||
|
items: '.PageListItem:not(.PageListItemOpen)',
|
||
|
placeholder: 'PageListSortPlaceholder',
|
||
|
start: function(e, ui) {
|
||
|
$(".PageListSortPlaceholder").css('width', ui.item.children(".PageListPage").outerWidth() + 'px');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var $sortRoot = $root.children('.PageList').children('.PageList');
|
||
|
|
||
|
var $cancelLink = $("<a href='#'>" + options.selectCancelLabel + "</a>").on('click', function() {
|
||
|
return cancelMove($li);
|
||
|
});
|
||
|
|
||
|
var $actions = $li.children("ul.PageListActions");
|
||
|
var $moveAction = $("<span class='PageListMoveNote detail'><i class='fa fa-fw fa-sort'></i> " + options.moveInstructionLabel + "<i class='fa fa-fw fa-angle-left'></i></span>");
|
||
|
$moveAction.append($cancelLink);
|
||
|
|
||
|
$actions.before($moveAction);
|
||
|
|
||
|
$li.addClass('PageListSortItem');
|
||
|
$li.parent('.PageList').attr('id', 'PageListMoveFrom');
|
||
|
|
||
|
$root.addClass('PageListSorting');
|
||
|
$sortRoot.addClass('PageListSortingList').sortable(sortOptions);
|
||
|
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove everything setup from an active 'move'
|
||
|
*
|
||
|
* @param jQuery $li List item that initiated the 'move'
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
function cancelMove($li) {
|
||
|
var $sortRoot = $root.find('.PageListSortingList');
|
||
|
$sortRoot.sortable('destroy').removeClass('PageListSortingList');
|
||
|
$li.removeClass('PageListSortItem').parent('.PageList').removeAttr('id');
|
||
|
$li.find('.PageListMoveNote').remove();
|
||
|
$root.find(".PageListPlaceholder").remove();
|
||
|
$root.removeClass('PageListSorting');
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove everything setup from an active 'move'
|
||
|
*
|
||
|
* @param jQuery $li List item that initiated the 'move'
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
function trashPage($li) {
|
||
|
var $trash = $root.find('.PageListID' + options.trashPageID);
|
||
|
if(!$trash.hasClass('PageListItemOpen')) {
|
||
|
$root.removeClass('PageListSorting');
|
||
|
$trash.children('a').trigger('click');
|
||
|
$root.addClass('PageListSorting');
|
||
|
}
|
||
|
var $trashList = $trash.next('.PageList');
|
||
|
if($trashList.length == 0) {
|
||
|
$trashList = $("<div class='PageList'></div>");
|
||
|
$trash.after($trashList);
|
||
|
}
|
||
|
$trashList.prepend($li);
|
||
|
var ui = { item: $li };
|
||
|
stopMove(null, ui);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Event called when the mouse stops after performing a 'move'
|
||
|
*
|
||
|
* @param event e
|
||
|
* @param jQueryUI ui
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
function stopMove(e, ui) {
|
||
|
|
||
|
var $li = ui.item;
|
||
|
var $a = $li.children('.PageListPage');
|
||
|
var id = parseInt($li.data('pageId'));
|
||
|
var $ul = $li.parent('.PageList');
|
||
|
var $from = $("#PageListMoveFrom")
|
||
|
|
||
|
// get the previous sibling .PageListItem, and skip over the pagination list if it's there
|
||
|
var $ulPrev = $ul.prev().is('.PageListItem') ? $ul.prev() : $ul.prev().prev();
|
||
|
var parent_id = parseInt($ulPrev.data('pageId'));
|
||
|
|
||
|
// check if item was moved to an invalid spot
|
||
|
// in this case, a spot between another open PageListItem and its PageList
|
||
|
var $liPrev = $li.prev(".PageListItem");
|
||
|
if($liPrev.is(".PageListItemOpen")) return false;
|
||
|
|
||
|
// check if item was moved into an invisible parent placeholder PageList
|
||
|
if($ul.is('.PageListPlaceholder')) {
|
||
|
var $ulNext = $ul.next();
|
||
|
if($ulNext.is('.PageList:visible')) {
|
||
|
// first item at top of list ended up in placeholder, but belongs in next sibling PageList
|
||
|
$ulNext.prepend($li);
|
||
|
$ul = $ulNext;
|
||
|
} else {
|
||
|
// if so, it's no longer a placeholder, but a real PageList
|
||
|
$ul.removeClass('PageListPlaceholder').children('.PageListPlaceholderItem').remove();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$root.addClass('PageListSortSaving');
|
||
|
cancelMove($li);
|
||
|
|
||
|
// setup to save the change
|
||
|
$li.append($loading.fadeIn('fast'));
|
||
|
var sortCSV = '';
|
||
|
|
||
|
// create a CSV string containing the order of Page IDs
|
||
|
$ul.children(".PageListItem").each(function() {
|
||
|
sortCSV += $(this).data('pageId') + ',';
|
||
|
});
|
||
|
|
||
|
var postData = {
|
||
|
id: id,
|
||
|
parent_id: parent_id,
|
||
|
sort: sortCSV
|
||
|
};
|
||
|
|
||
|
postData[$('#PageListContainer').attr('data-token-name')] = $('#PageListContainer').attr('data-token-value'); // CSRF Token
|
||
|
|
||
|
var success = 'unknown';
|
||
|
|
||
|
// save the change
|
||
|
$.post(options.ajaxMoveURL, postData, function(data) {
|
||
|
|
||
|
$loading.fadeOut('fast');
|
||
|
|
||
|
$a.fadeOut('fast', function() {
|
||
|
$(this).fadeIn("fast")
|
||
|
$li.removeClass('PageListSortItem');
|
||
|
$root.removeClass('PageListSorting');
|
||
|
});
|
||
|
|
||
|
if(data && data.error) {
|
||
|
ProcessWire.alert(data.message);
|
||
|
}
|
||
|
|
||
|
// if item moved from one list to another, then update the numChildren counts
|
||
|
if(!$ul.is("#PageListMoveFrom")) {
|
||
|
// update count where item came from
|
||
|
var $fromItem = $from.prev(".PageListItem");
|
||
|
var numChildren = getNumChildren($fromItem);
|
||
|
var numTotal = getNumTotal($fromItem);
|
||
|
if(numChildren > 0) {
|
||
|
numChildren--;
|
||
|
adjustNumTotal($fromItem, -1);
|
||
|
} else {
|
||
|
$from.remove(); // empty list, no longer needed
|
||
|
}
|
||
|
setNumChildren($fromItem, numChildren, false);
|
||
|
setForceReload($fromItem);
|
||
|
|
||
|
// update count where item went to
|
||
|
var $toItem = $ul.prev(".PageListItem");
|
||
|
numChildren = getNumChildren($toItem) + 1;
|
||
|
adjustNumTotal($toItem, 1);
|
||
|
setNumChildren($toItem, numChildren, false);
|
||
|
setForceReload($toItem);
|
||
|
}
|
||
|
$from.attr('id', ''); // remove tempoary #PageListMoveFrom
|
||
|
$root.removeClass('PageListSortSaving');
|
||
|
|
||
|
}, 'json');
|
||
|
|
||
|
// trigger pageMoved event: @teppokoivula
|
||
|
$li.trigger('pageMoved');
|
||
|
|
||
|
return true; // whether or not to allow the sort
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Event called when the "select" link is clicked on in select mode
|
||
|
*
|
||
|
* This also triggers a 'pageSelected' event on the attached text input.
|
||
|
*
|
||
|
* @see setupSelectMode()
|
||
|
*
|
||
|
*/
|
||
|
function clickSelect() {
|
||
|
|
||
|
var $t = $(this);
|
||
|
var $li = $t.parent('li').parent('ul.PageListActions').parent('.PageListItem');
|
||
|
var id = $li.data('pageId');
|
||
|
var $a = $li.children(".PageListPage");
|
||
|
var title = $a.text();
|
||
|
var url = $a.attr('title');
|
||
|
var $header = $root.children(".PageListSelectHeader");
|
||
|
|
||
|
if($t.text() == options.selectUnselectLabel) {
|
||
|
// if unselect link clicked, then blank out the values
|
||
|
id = 0;
|
||
|
title = '';
|
||
|
}
|
||
|
|
||
|
if(id != $container.val()) $container.val(id).trigger('change');
|
||
|
|
||
|
if(options.selectShowPageHeader) {
|
||
|
$header.children(".PageListSelectName").text(title);
|
||
|
}
|
||
|
|
||
|
// trigger pageSelected event
|
||
|
$container.trigger('pageSelected', {
|
||
|
id: id,
|
||
|
url: url,
|
||
|
title: title,
|
||
|
a: $a,
|
||
|
actionLink: $t
|
||
|
});
|
||
|
|
||
|
if(!options.selectMultiple) {
|
||
|
$header.find(".PageListSelectActionToggle").trigger('click'); // close the list
|
||
|
}
|
||
|
|
||
|
// jump to specified anchor, if provided
|
||
|
if(options.selectMultiple || options.selectSelectHref == '#') return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// initialize the plugin
|
||
|
init();
|
||
|
|
||
|
});
|
||
|
};
|
||
|
})(jQuery);
|