first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-06-08 17:09:23 -04:00
commit df3a033196
17887 changed files with 8637778 additions and 0 deletions
@@ -0,0 +1,91 @@
/**
* @file js/classes/features/CollapsibleGridFeature.js
*
* Copyright (c) 2016-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class CollapsibleGridFeature
* @ingroup js_classes_features
*
* @brief Adds collapse/expand functionality to grids.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.Feature
*/
$.pkp.classes.features.CollapsibleGridFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.CollapsibleGridFeature,
$.pkp.classes.features.Feature);
//
// Getter and setters.
//
/**
* Get the collapse/expand control link selector.
* @return {string}
*/
$.pkp.classes.features.CollapsibleGridFeature.prototype.getControlSelector =
function() {
return "a[id^='collapsibleGridControl-expandGridControlLink-button-']";
};
/**
* @inheritDoc
*/
$.pkp.classes.features.CollapsibleGridFeature.prototype.init =
function() {
$(this.getControlSelector(), this.getGridHtmlElement()).
click(this.callbackWrapper(this.toggleGridClickHandler_, this));
};
/**
* @inheritDoc
*/
$.pkp.classes.features.CollapsibleGridFeature.prototype.
addFeatureHtml = function($gridElement, options) {
var castOptions = /** @type {{collapsibleLink: string?}} */ (options);
$gridElement.find('div.grid_header_bar').prepend(castOptions.collapsibleLink);
};
//
// Private helper methods.
//
/**
* Collapse/expand grid.
* @private
* @param {Object} callingContext The calling element or object.
* @param {Event=} opt_event The triggering event.
* @return {boolean} Should return false to stop event processing.
*/
$.pkp.classes.features.CollapsibleGridFeature.prototype.
toggleGridClickHandler_ = function(callingContext, opt_event) {
var $control = this.getGridHtmlElement().find(this.getControlSelector());
this.getGridHtmlElement().find('div.grid_header').siblings().toggle();
$control.toggleClass('expand_all').toggleClass('collapse_all');
// Hide the search controls, if they are visible.
this.getGridHtmlElement().
find('div.grid_header_bar .search_extras_collapse').click();
// Toggle all grid actions.
this.getGridHtmlElement().find('div.grid_header span.options').toggle();
return false;
};
}(jQuery));
+200
View File
@@ -0,0 +1,200 @@
/**
* @defgroup js_classes_features
*/
/**
* @file js/classes/features/Feature.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Feature
* @ingroup js_classes_features
*
* @brief Base grid feature class.
* @see lib/pkp/classes/controllers/grid/feature/GridFeature.php
*
* We use the features concept of the ext js framework:
* http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.feature.Feature
*/
(function($) {
/** @type {Object} */
$.pkp.classes.features = $.pkp.classes.features || {};
/**
* @constructor
* @extends $.pkp.classes.ObjectProxy
* @param {$.pkp.controllers.grid.GridHandler} gridHandler The grid
* handler object.
* @param {Array} options Associated options.
*/
$.pkp.classes.features.Feature =
function(gridHandler, options) {
this.gridHandler = gridHandler;
this.options_ = options;
this.addFeatureHtml(this.getGridHtmlElement(), options);
};
//
// Protected properties.
//
/**
* The grid that this feature is attached to.
* @protected
* @type {$.pkp.controllers.grid.GridHandler}
*/
$.pkp.classes.features.Feature.prototype.gridHandler = null;
//
// Private properties.
//
/**
* This feature configuration options.
* @private
* @type {Object}
*/
$.pkp.classes.features.Feature.prototype.options_ = null;
//
// Setters and getters.
//
/**
* @param {Object} options The feature options.
*/
$.pkp.classes.features.Feature.prototype.setOptions =
function(options) {
this.options_ = options;
};
/**
* @return {Object} The feature options.
*/
$.pkp.classes.features.Feature.prototype.getOptions =
function() {
return this.options_;
};
//
// Public template methods.
//
/**
* Initialize this feature. Needs to be extended to implement
* specific initialization. This method will always be called
* by the components that this feature is attached to, in the
* moment of the attachment.
*/
$.pkp.classes.features.Feature.prototype.init =
function() {
throw new Error('Abstract method!');
};
//
// Template methods (hooks into grid widgets).
//
/**
* Hook into the add new element grid functionality.
* @param {jQueryObject} $newElement The new element to be added.
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.Feature.prototype.addElement =
function($newElement) {
return false;
};
/**
* Hook into the replace element content grid functionality.
* @param {jQueryObject} $newContent The element new content to be shown.
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.Feature.prototype.replaceElement =
function($newContent) {
return false;
};
/**
* Hook into the resequence rows grid functionality.
* @param {Object} sequenceMap The grid rows sequence.
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.Feature.prototype.resequenceRows =
function(sequenceMap) {
return false;
};
/**
* Hook into the refresh grid functionality. Called just before
* the fetch (grid or row) call is done.
* @param {number|Object=} opt_elementId
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.Feature.prototype.refreshGrid =
function(opt_elementId) {
return false;
};
/**
* Hook into the replace element response handler. Called after the
* response is handled.
* @param {Object} handledJsonData Object with the response content handled
* by the grid.
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.Feature.prototype.replaceElementResponseHandler =
function(handledJsonData) {
return false;
};
//
// Protected methods.
//
/**
* Use the grid handler object and call the
* callback wrapper method there.
* @see $.pkp.classes.Handler.callbackWrapper()
* @return {Function} Callback function.
*/
$.pkp.classes.features.Feature.prototype.callbackWrapper =
function(callback, opt_context) {
return this.gridHandler.callbackWrapper(callback, opt_context);
};
/**
* Extend to add extra html elements in the component
* that this feature is attached to.
* @param {jQueryObject} $gridElement Grid element to add elements to.
* @param {Object} options Feature options.
*/
$.pkp.classes.features.Feature.prototype.addFeatureHtml =
function($gridElement, options) {
// Default implementation does nothing.
};
/**
* Get the html element of the grid that this feature
* is attached to.
*
* @return {jQueryObject} Return the grid's HTML element.
*/
$.pkp.classes.features.Feature.prototype.getGridHtmlElement =
function() {
return this.gridHandler.getHtmlElement();
};
}(jQuery));
@@ -0,0 +1,90 @@
/**
* @file js/classes/features/GeneralPagingFeature.js
*
* Copyright (c) 2016-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class GeneralPagingFeature
* @ingroup js_classes_features
*
* @brief Base class that implements general functionalities for features
* that handles paging on grids.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.Feature
*/
$.pkp.classes.features.GeneralPagingFeature =
function(gridHandler, options) {
options.defaultItemsPerPage = parseInt(options.defaultItemsPerPage, 10);
options.currentItemsPerPage = parseInt(options.currentItemsPerPage, 10);
if (!options.itemsTotal) {
options.itemsTotal = 0;
} else {
options.itemsTotal = parseInt(options.itemsTotal, 10);
}
options.currentPage = parseInt(options.currentPage, 10);
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.GeneralPagingFeature,
$.pkp.classes.features.Feature);
//
// Getters and setters.
//
/**
* @return {{itemsPerPageParamName: string,
* defaultItemsPerPage: number,
* currentItemsPerPage: number,
* itemsTotal: number,
* pageParamName: string,
* currentPage: number,
filter: string,
* pagingMarkup: string }}
* @override
*/
$.pkp.classes.features.GeneralPagingFeature.prototype.getOptions =
function() {
var castOptions = /** @type {{itemsPerPageParamName: string,
defaultItemsPerPage: number,
currentItemsPerPage: number,
itemsTotal: number,
pageParamName: string,
currentPage: number,
filter: string,
pagingMarkup: string }} */
(this.parent('getOptions'));
return castOptions;
};
//
// Protected methods.
//
/**
* Set grid requests extra parameters.
* @param {Object} params
*/
$.pkp.classes.features.GeneralPagingFeature.prototype.setGridParams =
function(params) {
var options = this.getOptions(), filter;
// Add the filter data, if any.
if (options.hasOwnProperty('filter')) {
filter = $.parseJSON(options.filter);
$.extend(true, params, filter);
}
this.gridHandler.setFetchExtraParams(params);
};
}(jQuery));
@@ -0,0 +1,339 @@
/**
* @file js/classes/features/InfiniteScrollingFeature.js
*
* Copyright (c) 2016-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class InfiniteScrollingFeature
* @ingroup js_classes_features
*
* @brief Feature that implements infinite scrolling on grids.
* It doesn't support category grids.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.GeneralPagingFeature
*/
$.pkp.classes.features.InfiniteScrollingFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.InfiniteScrollingFeature,
$.pkp.classes.features.GeneralPagingFeature);
//
// Private properties
//
/**
* The scrollable element.
* @private
* @type {jQueryObject}
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.
$scrollableElement_ = $();
/**
* The scrolling observer callback function.
* @private
* @type {Function}
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.
observeScrollCallback_ = function() {};
//
// Extended methods from GeneralPagingFeature
//
/**
* @inheritDoc
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.init =
function() {
var $scrollableElement = $('div.scrollable', this.getGridHtmlElement());
if (!$scrollableElement.length) {
this.gridHandler.publishEvent('pkpObserveScrolling');
this.gridHandler.publishEvent('pkpRemoveScrollingObserver');
}
this.$scrollableElement_ = $scrollableElement;
this.observeScrollCallback_ = this.gridHandler.callbackWrapper(
this.observeScroll_, this);
this.addScrollHandler_();
this.fixGridHeight_();
this.addPagingDataToRows_();
};
/**
* @inheritDoc
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.addFeatureHtml =
function($gridElement, options) {
var castOptions = /** @type {{pagingMarkup: string?,
loadingContainer: string?}} */ (options);
$gridElement.append(castOptions.pagingMarkup);
$gridElement.find('.pkp_linkaction_moreItems')
.click(this.gridHandler.callbackWrapper(this.loadMoreItems_, this));
};
//
// Hooks implementation.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.refreshGrid =
function(opt_elementId) {
var options = this.getOptions(), params, $firstRow, $lastRow, page, $gridRow,
elementId;
params = this.gridHandler.getFetchExtraParams();
params[options.pageParamName] = options.currentPage;
if (opt_elementId && opt_elementId !==
$.pkp.controllers.grid.GridHandler.FETCH_ALL_ROWS_ID) {
// We need to make sure we pass the right page for the element.
elementId = (/** @type {number} */ (opt_elementId));
$gridRow = this.gridHandler.getRowByDataId(elementId);
if ($gridRow.length == 1) {
params[options.pageParamName] = Number($gridRow.attr('data-paging'));
}
}
params[options.itemsPerPageParamName] = options.currentItemsPerPage;
this.setGridParams(params);
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.
replaceElementResponseHandler = function(handledJsonData) {
var pagingInfo, options, castJsonData, rowMarkup;
options = this.getOptions();
castJsonData = /** @type {{pagingInfo: Object,
deletedRowReplacement: string}} */
(handledJsonData);
if (castJsonData.deletedRowReplacement != undefined) {
rowMarkup = handledJsonData.deletedRowReplacement;
this.gridHandler.insertOrReplaceElement(rowMarkup);
this.updatePagingDataInAllRows_();
}
this.addScrollHandler_();
if (castJsonData.pagingInfo != undefined) {
pagingInfo = handledJsonData.pagingInfo;
this.setOptions(pagingInfo);
if (pagingInfo.pagingMarkup != undefined) {
$('div.gridPagingScrolling', this.getGridHtmlElement()).
replaceWith(pagingInfo.pagingMarkup);
}
}
this.addPagingDataToRows_();
this.toggleLoadingContainer_();
this.getGridHtmlElement().find('.pkp_linkaction_moreItems').
click(this.gridHandler.callbackWrapper(this.loadMoreItems_, this));
return false;
};
//
// Private helper methods.
//
/**
* Scroll handler to detect when it's time to request more rows.
*
* @private
*
* @param {HTMLElement} sourceElement
* @param {Event} event
* @return {boolean}
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.observeScroll_ =
function(sourceElement, event) {
var options = this.getOptions(), sourceElementHeight,
bottomLimit, windowDimensions;
if (options.itemsTotal == this.gridHandler.getRows().length) {
return false;
}
if (!this.getGridHtmlElement().is(':visible')) {
return false;
}
if ($(sourceElement).hasClass('scrollable')) {
sourceElementHeight = $(sourceElement).height();
bottomLimit = sourceElement.scrollHeight;
} else {
windowDimensions = $.pkp.controllers.SiteHandler.
prototype.getWindowDimensions();
sourceElementHeight = windowDimensions.height;
bottomLimit = this.getGridHtmlElement().offset().top +
this.getGridHtmlElement().height();
}
if (sourceElementHeight + $(sourceElement).scrollTop() >= bottomLimit) {
// Avoid multiple rows requests.
if (this.$scrollableElement_.length) {
this.$scrollableElement_.unbind('scroll');
} else {
this.getGridHtmlElement().trigger('pkpRemoveScrollingObserver',
[this.observeScrollCallback_]);
}
this.loadMoreItems_();
}
return false;
};
/**
* Fix the grid height to acomodate the number of initial visible rows.
*
* @private
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.fixGridHeight_ =
function() {
var $scrollableDivs = $('div.scrollable', this.getGridHtmlElement()),
index, limit, $div, timer, length;
if ($scrollableDivs.length > 0) {
timer = setInterval(function() {
if ($scrollableDivs.is(':visible')) {
clearInterval(timer);
length = $scrollableDivs.length;
for (index = 0, limit = length; index < limit; index++) {
$div = $($scrollableDivs[index]);
if ($div.get(0).scrollHeight > $div.height()) {
$div.css('max-height', $div.get(0).scrollHeight - 10);
}
}
}
},300);
}
};
/**
* Add paging data to the respective rows.
*
* @private
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.addPagingDataToRows_ =
function() {
var $rows, options = this.getOptions();
$rows = this.gridHandler.getRows().filter('tr:not([data-paging])');
$rows.attr('data-paging', options.currentPage);
};
/**
* Update paging data in all grid rows.
*
* @private
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.
updatePagingDataInAllRows_ = function() {
var $rows, options = this.getOptions(), index, limit, page = 1,
itemsCount = 1;
$rows = this.gridHandler.getRows();
$rows.removeAttr('data-paging');
for (index = 0, limit = $rows.length; index < limit; index++) {
$($rows[index]).attr('data-paging', page);
itemsCount++;
if (itemsCount > options.currentItemsPerPage) {
itemsCount = 1;
page++;
}
}
};
/**
* Add scroll handler to the grid element.
*
* @private
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.addScrollHandler_ =
function() {
var $scrollableElement = this.$scrollableElement_;
if ($scrollableElement.length) {
$scrollableElement.
scroll(this.observeScrollCallback_);
} else {
this.getGridHtmlElement().trigger('pkpObserveScrolling',
[this.observeScrollCallback_]);
}
};
/**
* Toggle the scrolling loading element.
*
* @private
*
* @param {boolean=} opt_show
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.
toggleLoadingContainer_ = function(opt_show) {
var $loadingElement =
this.getGridHtmlElement().find('div.gridPagingScrolling div.pkp_loading'),
$scrollableElement = this.$scrollableElement_,
scrollTop,
loadingHeight = $loadingElement.height(),
scrollTarget;
if (opt_show) {
this.getGridHtmlElement().addClass('loading');
scrollTop = $scrollableElement.scrollTop();
scrollTarget = /** @type {number} */ (scrollTop + loadingHeight);
$scrollableElement.scrollTop(scrollTarget);
} else {
this.getGridHtmlElement().removeClass('loading');
}
};
/**
* Trigger necessary actions for the grid to
* load next page items.
*
* @private
*
*/
$.pkp.classes.features.InfiniteScrollingFeature.prototype.
loadMoreItems_ = function() {
var options = this.getOptions();
// Show the loading icon.
this.toggleLoadingContainer_(true);
options.currentPage = Number($('tr.gridRow',
this.getGridHtmlElement()).last().attr('data-paging')) + 1;
this.getGridHtmlElement().trigger('dataChanged',
[$.pkp.controllers.grid.GridHandler.FETCH_ALL_ROWS_ID]);
};
}(jQuery));
@@ -0,0 +1,201 @@
/**
* @file js/classes/features/OrderCategoryGridItemsFeature.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class OrderCategoryGridItemsFeature
* @ingroup js_classes_features
*
* @brief Feature for ordering category grid items.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.OrderGridItemsFeature
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.OrderCategoryGridItemsFeature,
$.pkp.classes.features.OrderGridItemsFeature);
//
// Extended methods from OrderItemsFeature.
//
/**
* Setup the sortable plugin.
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
setupSortablePlugin = function() {
var $categories, index, limit, $category, userAgent;
this.applySortPlgOnElements(
this.getGridHtmlElement(), 'tbody.orderable', null);
// FIXME *7610*: IE8 can't handle well ordering in both categories and
// category rows.
userAgent = navigator.userAgent.toLowerCase();
if (/msie/.test(userAgent) &&
parseInt(userAgent.substr(userAgent.indexOf('msie') + 5, 1), 10) <= 8) {
return;
}
$categories = this.gridHandler.getCategories();
for (index = 0, limit = $categories.length; index < limit; index++) {
$category = $($categories[index]);
this.applySortPlgOnElements($category, 'tr.orderable', null);
}
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
saveOrderHandler = function() {
this.gridHandler.updateEmptyPlaceholderPosition();
this.parent('saveOrderHandler');
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
cancelOrderHandler = function() {
var categorySequence = this.getCategorySequence_(this.itemsOrder);
this.parent('cancelOrderHandler');
this.gridHandler.resequenceCategories(categorySequence);
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
toggleItemsDragMode = function() {
this.parent('toggleItemsDragMode');
var isOrdering = this.isOrdering,
$categories = this.gridHandler.getCategories(),
index, limit, $category;
for (index = 0, limit = $categories.length; index < limit; index++) {
$category = $($categories[index]);
this.toggleCategoryDragMode_($category);
}
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
addOrderingClassToRows = function() {
var options = this.getOptions(),
type = parseInt(options.type, 10), $categories;
if (type == $.pkp.cons.ORDER_CATEGORY_GRID_CATEGORIES_ONLY ||
type == $.pkp.cons.ORDER_CATEGORY_GRID_CATEGORIES_AND_ROWS) {
$categories = this.gridHandler.getCategories();
$categories.addClass('orderable');
}
if (type == $.pkp.cons.ORDER_CATEGORY_GRID_CATEGORIES_ROWS_ONLY ||
type == $.pkp.cons.ORDER_CATEGORY_GRID_CATEGORIES_AND_ROWS) {
this.parent('addOrderingClassToRows');
}
// We don't want to order category rows tr elements, so
// remove any style that might be added by calling parent.
this.gridHandler.getCategoryRow().removeClass('orderable');
};
//
// Overriden method from OrderGridItemsFeature
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.getItemsDataId =
function() {
var categoriesSeq = this.getCategorySequence_(this.itemsOrder),
itemsDataId = [],
index, limit,
$category, categoryRowsDataId, categoryDataId;
for (index = 0, limit = categoriesSeq.length; index < limit; index++) {
$category = $('#' + categoriesSeq[index]);
categoryRowsDataId = this.getRowsDataId($category);
categoryDataId = this.gridHandler.getCategoryDataId($category);
itemsDataId.push(
{'categoryId': categoryDataId, 'rowsId': categoryRowsDataId });
}
return itemsDataId;
};
//
// Private helper methods.
//
/**
* Enable/disable category drag mode.
* @param {jQueryObject} $category Category to set mode on.
* @private
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
toggleCategoryDragMode_ = function($category) {
var isOrdering = this.isOrdering,
$categoryRow = this.gridHandler.getCategoryRow($category),
$categoryRowColumn = $('td:first', $categoryRow),
moveClasses = this.getMoveItemClasses();
if (isOrdering) {
$categoryRowColumn.addClass(moveClasses);
} else {
$categoryRowColumn.removeClass(moveClasses);
}
};
/**
* Get the categories sequence, based on the passed items order.
* @param {Array} itemsOrder Items order.
* @return {Array} A sequence array with the category data id as values.
* @private
*/
$.pkp.classes.features.OrderCategoryGridItemsFeature.prototype.
getCategorySequence_ = function(itemsOrder) {
var index, limit, categorySequence = [], categoryDataId, categoryId;
for (index = 0, limit = itemsOrder.length; index < limit; index++) {
categoryDataId = this.gridHandler
.getCategoryDataIdByRowId(itemsOrder[index]);
categoryId = this.gridHandler.getCategoryIdPrefix() + categoryDataId;
if ($.inArray(categoryId, categorySequence) > -1) {
continue;
}
categorySequence.push(categoryId);
}
return categorySequence;
};
}(jQuery));
@@ -0,0 +1,97 @@
/**
* @file js/classes/features/OrderGridItemsFeature.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class OrderGridItemsFeature
* @ingroup js_classes_features
*
* @brief Feature for ordering grid items.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.OrderItemsFeature
*/
$.pkp.classes.features.OrderGridItemsFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.OrderGridItemsFeature,
$.pkp.classes.features.OrderItemsFeature);
//
// Extended methods from OrderItemsFeature.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderGridItemsFeature.prototype.setupSortablePlugin =
function() {
this.applySortPlgOnElements(
this.getGridHtmlElement(), 'tr.orderable', null);
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderGridItemsFeature.prototype.saveOrderHandler =
function() {
var stringifiedData, saveOrderCallback,
options = /** @type {{saveItemsSequenceUrl: string}} */
(this.getOptions()),
returner;
this.parent('saveOrderHandler');
stringifiedData = JSON.stringify(this.getItemsDataId());
saveOrderCallback = this.callbackWrapper(
this.saveOrderResponseHandler_, this);
$.post(options.saveItemsSequenceUrl,
{data: stringifiedData, csrfToken: options.csrfToken},
saveOrderCallback, 'json');
return false;
};
//
// Protected methods to be overriden by subclasses
//
/**
* Get all items data id in a sequence array.
* @return {Array} List of all items data.
*/
$.pkp.classes.features.OrderGridItemsFeature.prototype.getItemsDataId =
function() {
return this.getRowsDataId(this.getGridHtmlElement());
};
//
// Private helper methods.
//
/**
* Save order response handler.
* @private
*
* @param {Object} ajaxContext The AJAX request context.
* @param {Object} jsonData A parsed JSON response object.
*/
$.pkp.classes.features.OrderGridItemsFeature.prototype.
saveOrderResponseHandler_ = function(ajaxContext, jsonData) {
var processedJsonData = this.gridHandler.handleJson(jsonData);
this.toggleState(false);
};
}(jQuery));
@@ -0,0 +1,639 @@
/**
* @file js/classes/features/OrderItemsFeature.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class OrderItemsFeature
* @ingroup js_classes_features
*
* @brief Base feature class for ordering grid items.
*/
(function($) {
/**
* @constructor
*
* @param {jQueryObject} gridHandler The handler of
* the grid element that this feature is attached to.
* @param {Object} options Configuration of this feature.
* @extends $.pkp.classes.features.Feature
*/
$.pkp.classes.features.OrderItemsFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
this.$orderButton_ = $('.pkp_linkaction_orderItems',
this.getGridHtmlElement());
this.$finishControl_ = $('.order_finish_controls', this.getGridHtmlElement());
if (this.$orderButton_.length === 0) {
// No order button, it will always stay in ordering mode.
this.isOrdering = true;
}
this.itemsOrder = [];
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.OrderItemsFeature,
$.pkp.classes.features.Feature);
//
// Protected properties
//
/**
* Item sequence.
* @protected
* @type {Array}
*/
$.pkp.classes.features.OrderItemsFeature.prototype.itemsOrder = null;
/**
* Flag to control if user is ordering items.
* @protected
* @type {boolean}
*/
$.pkp.classes.features.OrderItemsFeature.prototype.isOrdering = false;
//
// Private properties.
//
/**
* Initiate ordering state button.
* @private
* @type {jQueryObject}
*/
$.pkp.classes.features.OrderItemsFeature.prototype.$orderButton_ = null;
/**
* Cancel ordering state button.
* @private
* @type {jQueryObject}
*/
$.pkp.classes.features.OrderItemsFeature.prototype.$cancelButton_ = null;
/**
* Save ordering state button.
* @private
* @type {jQueryObject}
*/
$.pkp.classes.features.OrderItemsFeature.prototype.$saveButton_ = null;
/**
* Ordering finish control.
* @private
* @type {jQueryObject}
*/
$.pkp.classes.features.OrderItemsFeature.prototype.$finishControl_ = null;
//
// Getters and setters.
//
/**
* Get the order button.
* @return {jQueryObject} The order button JQuery object.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.getOrderButton =
function() {
return this.$orderButton_;
};
/**
* Get the finish control.
* @return {jQueryObject} The JQuery "finish" control.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.getFinishControl =
function() {
return this.$finishControl_;
};
/**
* Get save order button.
*
* @return {jQueryObject} The "save order" JQuery object.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.getSaveOrderButton =
function() {
return this.getFinishControl().find('.saveButton');
};
/**
* Get cancel order link.
*
* @return {jQueryObject} The "cancel order" JQuery control.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.getCancelOrderButton =
function() {
return this.getFinishControl().find('.cancelFormButton');
};
/**
* Get the move item row action element selector.
* @return {string} Return the element selector.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.
getMoveItemRowActionSelector = function() {
return '.orderable .pkp_linkaction_moveItem';
};
/**
* Get the css classes used to stylize the ordering items.
* @return {string} CSS classes.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.getMoveItemClasses =
function() {
return 'pkp_helpers_moveicon ordering';
};
//
// Public template methods.
//
/**
* Called every time user start dragging an item.
* @param {jQueryObject} contextElement The element this event occurred for.
* @param {Event} event The drag/drop event.
* @param {Object} ui Object with data related to the event elements.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.dragStartCallback =
function(contextElement, event, ui) {
// The default implementation does nothing.
};
/**
* Called every time user stop dragging an item.
* @param {jQueryObject} contextElement The element this event occurred for.
* @param {Event} event The drag/drop event.
* @param {Object} ui Object with data related to the event elements.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.dragStopCallback =
function(contextElement, event, ui) {
// The default implementation does nothing.
};
/**
* Called every time sequence is changed.
* @param {jQueryObject} contextElement The element this event occurred for.
* @param {Event} event The drag/drop event.
* @param {Object} ui Object with data related to the event elements.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.updateOrderCallback =
function(contextElement, event, ui) {
// The default implementation does nothing.
};
//
// Extended methods from Feature
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderItemsFeature.prototype.init =
function() {
this.addOrderingClassToRows();
this.toggleMoveItemRowAction(this.isOrdering);
this.getGridHtmlElement().find('div.order_message').hide();
this.toggleOrderLink_();
if (this.isOrdering) {
this.setupSortablePlugin();
}
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderItemsFeature.prototype.addFeatureHtml =
function($gridElement, options) {
var castOptions = /** @type {{orderFinishControls: string?,
orderMessage: string?}} */ (options),
$orderFinishControls, orderMessageHtml, $gridRows;
if (castOptions.orderFinishControls !== undefined) {
$orderFinishControls = $(castOptions.orderFinishControls);
$gridElement.find('table').last().after($orderFinishControls);
$orderFinishControls.hide();
}
if (castOptions.orderMessage !== undefined) {
orderMessageHtml = castOptions.orderMessage;
$gridRows = $gridElement.find('.gridRow').filter(function(index, element) {
return !Boolean($(this).find('a.pkp_linkaction_moveItem').length);
});
$gridRows.find('td:first-child').prepend(orderMessageHtml);
}
this.updateOrderLinkVisibility_();
};
//
// Protected template methods.
//
/**
* Add orderable class to grid rows.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.addOrderingClassToRows =
function() {
// Add ordering class to grid rows.
var $gridRows = this.gridHandler.getRows().filter(function(index, element) {
return $(this).find('a.pkp_linkaction_moveItem').length;
});
$gridRows.addClass('orderable');
};
/**
* Setup the sortable plugin. Must be implemented in subclasses.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.setupSortablePlugin =
function() {
// Default implementation does nothing.
};
/**
* Called every time storeOrder is called. This is a chance to subclasses
* execute operations with each row that has their sequence being saved.
* @param {number} index The current row index position inside the rows
* jQuery object.
* @param {jQueryObject} $row Row for which to store the sequence.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.storeRowOrder =
function(index, $row) {
// The default implementation does nothing.
};
//
// Protected methods.
//
/**
* Initiate ordering button click event handler.
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.clickOrderHandler =
function() {
this.gridHandler.hideAllVisibleRowActions();
this.storeOrder(this.gridHandler.getRows());
this.toggleState(true);
return false;
};
/**
* Save order handler.
* @return {boolean} Return false to stop click event processing.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.saveOrderHandler =
function() {
var $rows;
this.gridHandler.updateControlRowsPosition();
this.unbindOrderFinishControlsHandlers_();
$rows = this.gridHandler.getRows();
this.storeOrder($rows);
return false;
};
/**
* Cancel ordering action click event handler.
* @return {boolean} Always returns false.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.cancelOrderHandler =
function() {
this.gridHandler.resequenceRows(this.itemsOrder);
this.toggleState(false);
return false;
};
/**
* Execute all operations necessary to change the state of the
* ordering process (enabled or disabled).
* @param {boolean} isOrdering Is ordering process active?
*/
$.pkp.classes.features.OrderItemsFeature.prototype.toggleState =
function(isOrdering) {
this.isOrdering = isOrdering;
this.toggleGridLinkActions_();
this.toggleOrderLink_();
this.toggleFinishControl_();
this.toggleItemsDragMode();
this.setupSortablePlugin();
this.setupNonOrderableMessage_();
};
/**
* Set rows sequence store, using
* the sequence of the passed items.
*
* @param {jQueryObject} $rows The rows to be used to get the sequence
* information.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.storeOrder =
function($rows) {
var index, limit, $row, elementId;
this.itemsOrder = [];
for (index = 0, limit = $rows.length; index < limit; index++) {
$row = $($rows[index]);
elementId = $row.attr('id');
this.itemsOrder.push(elementId);
// Give a chance to subclasses do extra operations to store
// the current row order.
this.storeRowOrder(index, $row);
}
};
/**
* Enable/disable the items drag mode.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.toggleItemsDragMode =
function() {
var isOrdering = this.isOrdering,
$rows = this.gridHandler.getRows(),
$orderableRows = $rows.filter('.orderable'),
moveClasses = this.getMoveItemClasses();
if (isOrdering) {
$orderableRows.addClass(moveClasses);
} else {
$orderableRows.removeClass(moveClasses);
}
this.toggleMoveItemRowAction(isOrdering);
};
/**
* Apply (disabled or enabled) the sortable plugin on passed elements.
* @param {jQueryObject} $container The element that contain all the orderable
* items.
* @param {string} itemsSelector The jQuery selector for orderable items.
* @param {Object?} extraParams Optional set of extra parameters for sortable.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.applySortPlgOnElements =
function($container, itemsSelector, extraParams) {
var isOrdering = this.isOrdering,
dragStartCallback = this.gridHandler.callbackWrapper(
this.dragStartCallback, this),
dragStopCallback = this.gridHandler.callbackWrapper(
this.dragStopCallback, this),
orderItemCallback = this.gridHandler.callbackWrapper(
this.updateOrderCallback, this),
config = {
disabled: !isOrdering,
items: itemsSelector,
activate: dragStartCallback,
deactivate: dragStopCallback,
update: orderItemCallback,
tolerance: 'pointer'};
if (typeof extraParams === 'object') {
config = $.extend(true, config, extraParams);
}
$container.sortable(config);
};
/**
* Get the data element id of all rows inside the passed
* container, in the current order.
* @param {jQueryObject} $rowsContainer The element that contains the rows
* that will be used to retrieve the id.
* @return {Array} A sequence array with data element ids as values.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.getRowsDataId =
function($rowsContainer) {
var index, rowDataIds = [], $row, rowDataId;
for (index in this.itemsOrder) {
$row = $('#' + this.itemsOrder[index], $rowsContainer);
if ($row.length < 1) {
continue;
}
rowDataId = this.gridHandler.getRowDataId($row);
rowDataIds.push(rowDataId);
}
return rowDataIds;
};
/**
* Show/hide the move item row action (position left).
* @param {boolean} enable New enable state.
*/
$.pkp.classes.features.OrderItemsFeature.prototype.toggleMoveItemRowAction =
function(enable) {
var $grid = this.getGridHtmlElement(),
$actionsContainer = $('div.row_actions', $grid),
allLinksButMoveItemSelector = 'a:not(' +
this.getMoveItemRowActionSelector() + ')',
$actions = $actionsContainer.find(allLinksButMoveItemSelector),
$moveItemRowAction = $(this.getMoveItemRowActionSelector(), $grid),
$rowActionsContainer, $rowActions;
if (enable) {
$actions.addClass('pkp_helpers_display_none');
$moveItemRowAction.show();
// Make sure row actions div is visible.
this.gridHandler.showRowActionsDiv();
} else {
$actions.removeClass('pkp_helpers_display_none');
$rowActionsContainer = $('.gridRow div.row_actions', $grid);
$rowActions = $rowActionsContainer.
find(allLinksButMoveItemSelector);
if ($rowActions.length === 0) {
// No link action to show, hide row actions div.
this.gridHandler.hideRowActionsDiv();
}
$moveItemRowAction.hide();
}
};
//
// Hooks implementation.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderItemsFeature.prototype.addElement =
function($element) {
this.addOrderingClassToRows();
this.toggleItemsDragMode();
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderItemsFeature.prototype.replaceElement =
function($content) {
this.addOrderingClassToRows();
this.toggleItemsDragMode();
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderItemsFeature.prototype.
replaceElementResponseHandler = function(handledJsonData) {
this.updateOrderLinkVisibility_();
this.setupNonOrderableMessage_();
return false;
};
//
// Private helper methods.
//
/**
* Make sure that the order action visibility state is correct,
* based on the grid rows number.
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.
updateOrderLinkVisibility_ = function() {
var $orderLink = $('.pkp_linkaction_orderItems', this.getGridHtmlElement());
if (this.gridHandler.getRows().length <= 1) {
$orderLink.hide();
} else {
$orderLink.show();
}
};
/**
* Set the state of the grid link actions, based on current ordering state.
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.toggleGridLinkActions_ =
function() {
var isOrdering = this.isOrdering,
// We want to enable/disable all link actions, except this
// features controls.
$gridLinkActions = $('.pkp_controllers_linkAction',
this.getGridHtmlElement()).not(
this.getMoveItemRowActionSelector()).not(
this.getOrderButton()).not(
this.getFinishControl().find('*'));
this.gridHandler.changeLinkActionsState(!isOrdering, $gridLinkActions);
};
/**
* Enable/disable the order link action.
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.toggleOrderLink_ =
function() {
if (this.isOrdering) {
this.$orderButton_.unbind('click');
this.$orderButton_.attr('disabled', 'disabled');
} else {
var clickHandler = this.gridHandler.callbackWrapper(
this.clickOrderHandler, this);
this.$orderButton_.click(clickHandler);
this.$orderButton_.removeAttr('disabled');
}
};
/**
* Show/hide the ordering process finish control, based
* on the current ordering state.
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.toggleFinishControl_ =
function() {
if (this.isOrdering) {
this.bindOrderFinishControlsHandlers_();
this.getFinishControl().slideDown(300);
} else {
this.unbindOrderFinishControlsHandlers_();
this.getFinishControl().slideUp(300);
}
};
/**
* Bind event handlers to the controls that finish the
* ordering action (save and cancel).
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.
bindOrderFinishControlsHandlers_ = function() {
var $saveButton = this.getSaveOrderButton(),
$cancelLink = this.getCancelOrderButton(),
cancelLinkHandler = this.gridHandler.callbackWrapper(
this.cancelOrderHandler, this),
saveButtonHandler = this.gridHandler.callbackWrapper(
this.saveOrderHandler, this);
$saveButton.click(saveButtonHandler);
$cancelLink.click(cancelLinkHandler);
};
/**
* Unbind event handlers from the controls that finish the
* ordering action (save and cancel).
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.
unbindOrderFinishControlsHandlers_ = function() {
this.getSaveOrderButton().unbind('click');
this.getCancelOrderButton().unbind('click');
};
/**
* Toggle hover action to show message for non orderable
* grid rows.
* @private
*/
$.pkp.classes.features.OrderItemsFeature.prototype.
setupNonOrderableMessage_ = function() {
if (this.isOrdering) {
this.gridHandler.getRows().hover(function() {
$(this).find('div.order_message').toggle();
});
} else {
this.gridHandler.getRows().unbind('mouseenter mouseleave');
}
};
}(jQuery));
@@ -0,0 +1,220 @@
/**
* @file js/classes/features/OrderListbuilderItemsFeature.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class OrderListbuilderItemsFeature
* @ingroup js_classes_features
*
* @brief Feature for ordering grid items.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.OrderItemsFeature
*/
$.pkp.classes.features.OrderListbuilderItemsFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.OrderListbuilderItemsFeature,
$.pkp.classes.features.OrderItemsFeature);
//
// Extended methods from OrderItemsFeature.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.addFeatureHtml =
function($gridElement, options) {
var $itemSequenceInput, $gridRows, index, limit, $gridRow,
$itemSequenceInputClone;
this.parent('addFeatureHtml', $gridElement, options);
$itemSequenceInput = this.getSequenceInput_();
$gridRows = this.gridHandler.getRows();
for (index = 0, limit = $gridRows.length; index < limit; index++) {
$gridRow = $($gridRows[index]);
$itemSequenceInputClone = $itemSequenceInput.clone();
$('td.first_column', $gridRow).append($itemSequenceInputClone);
}
};
/**
* Set up the sortable plugin.
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.
setupSortablePlugin = function() {
this.applySortPlgOnElements(
this.getGridHtmlElement(), 'tr.orderable', null);
};
//
// Extended methods from ToggleableOrderItemsFeature.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.init =
function() {
this.parent('init');
this.toggleItemsDragMode();
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.toggleState =
function(isOrdering) {
this.parent('toggleState', isOrdering);
this.toggleContentHandlers_();
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.storeRowOrder =
function(index, $row) {
var seq = index + 1,
$orderableInput = $row.find('.itemSequence'),
$modifiedInput;
$orderableInput.attr('value', seq);
$modifiedInput = $row.find('.isModified');
$modifiedInput.attr('value', 1);
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.saveOrderHandler =
function() {
this.parent('saveOrderHandler');
this.toggleState(false);
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.
updateOrderCallback = function(contextElement, event, ui) {
var $rows;
this.parent('updateOrderCallback');
$rows = this.gridHandler.getRows();
this.storeOrder($rows);
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.
clickOrderHandler = function() {
var $selects = $('select:visible', this.getGridHtmlElement()),
index, limit;
if ($selects.length > 0) {
for (index = 0, limit = $selects.length; index < limit; index++) {
this.gridHandler.saveRow($($selects[index]).parents('.gridRow'));
}
}
return /** @type {boolean} */ (this.parent('clickOrderHandler'));
};
//
// Implemented Feature template hook methods.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.addElement =
function($newElement) {
this.parent('addElement', $newElement);
this.formatAndStoreNewRow_($newElement);
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.replaceElement =
function($newContent) {
this.parent('replaceElement', $newContent);
this.formatAndStoreNewRow_($newContent);
return false;
};
//
// Private helper methods.
//
/**
* Get the sequence input html element.
* @private
* @return {jQueryObject} Sequence input.
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.
getSequenceInput_ = function() {
return $('<input type="hidden" name="newRowId[sequence]" ' +
'class="itemSequence" />');
};
/**
* Enable/disable row content handlers.
* @private
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.
toggleContentHandlers_ = function() {
var $rows = this.gridHandler.getRows(),
index, limit, $row;
for (index = 0, limit = $rows.length; index < limit; index++) {
$row = $($rows[index]);
if (this.isOrdering) {
$row.find('.gridCellDisplay').unbind('click');
} else {
this.gridHandler.attachContentHandlers_($row);
}
}
};
/**
* Format and store new row.
* @private
* @param {jQueryObject} $row The new row element.
*/
$.pkp.classes.features.OrderListbuilderItemsFeature.prototype.
formatAndStoreNewRow_ = function($row) {
var $rows;
$row.children().after(this.getSequenceInput_());
$rows = this.gridHandler.getRows();
this.storeOrder($rows);
};
}(jQuery));
@@ -0,0 +1,247 @@
/**
* @file js/classes/features/PagingFeature.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PagingFeature
* @ingroup js_classes_features
*
* @brief Feature that implements paging on grids.
*/
(function($) {
/**
* @constructor
* @inheritDoc
* @extends $.pkp.classes.features.GeneralPagingFeature
*/
$.pkp.classes.features.PagingFeature =
function(gridHandler, options) {
this.parent(gridHandler, options);
};
$.pkp.classes.Helper.inherits(
$.pkp.classes.features.PagingFeature,
$.pkp.classes.features.GeneralPagingFeature);
/**
* @inheritDoc
*/
$.pkp.classes.features.PagingFeature.prototype.init =
function() {
this.configPagingLinks_();
this.configItemsPerPageElement_();
};
/**
* @inheritDoc
*/
$.pkp.classes.features.PagingFeature.prototype.addFeatureHtml =
function($gridElement, options) {
$gridElement.append(options.pagingMarkup);
};
//
// Hooks implementation.
//
/**
* @inheritDoc
*/
$.pkp.classes.features.PagingFeature.prototype.resequenceRows =
function(sequenceMap) {
var $rows = this.gridHandler.getRows(),
extraRowsNum, index,
options = this.getOptions();
// Clean any extra rows that might still be visible from old range data.
extraRowsNum = $rows.length - options.currentItemsPerPage;
if (extraRowsNum > 0) {
for (index = 0; index < extraRowsNum; index++) {
this.gridHandler.deleteElement($rows.first(), true);
}
}
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.PagingFeature.prototype.refreshGrid =
function(opt_elementId) {
var options = this.getOptions(), params, $firstRow, $lastRow;
params = this.gridHandler.getFetchExtraParams();
params[options.pageParamName] = options.currentPage;
params[options.itemsPerPageParamName] = options.currentItemsPerPage;
$firstRow = this.gridHandler.getRows().first();
$lastRow = this.gridHandler.getRows().last();
if ($firstRow.length == 0) {
params.topLimitRowId = 0;
} else {
params.topLimitRowId = this.gridHandler.getRowDataId($firstRow);
}
if ($lastRow.length == 0) {
params.bottomLimitRowId = 0;
} else {
params.bottomLimitRowId = this.gridHandler.getRowDataId($lastRow);
}
this.setGridParams(params);
return false;
};
/**
* @inheritDoc
*/
$.pkp.classes.features.PagingFeature.prototype.replaceElementResponseHandler =
function(handledJsonData) {
var rowMarkup, rowDataId, pagingInfo, options, $rows, castJsonData;
options = this.getOptions();
castJsonData = /** @type {{deletedRowReplacement: string,
pagingInfo: string,
loadLastPage: boolean,
newTopRow: string}} */
(handledJsonData);
if (castJsonData.deletedRowReplacement != undefined) {
rowMarkup = handledJsonData.deletedRowReplacement;
this.gridHandler.insertOrReplaceElement(rowMarkup);
}
if (castJsonData.pagingInfo != undefined) {
pagingInfo = handledJsonData.pagingInfo;
this.setOptions(pagingInfo);
this.gridHandler.replacePartialWith(pagingInfo.pagingMarkup,
$('div.gridPaging', this.getGridHtmlElement()));
this.init();
}
if (castJsonData.loadLastPage) {
this.getGridHtmlElement().trigger('dataChanged');
}
if (castJsonData.newTopRow != undefined) {
// Check if we need to remove one row from the bottom
// to keep the same range info count value.
$rows = this.gridHandler.getRows();
if (options.currentItemsPerPage == $rows.length) {
this.gridHandler.deleteElement($rows.last(), true);
}
rowMarkup = handledJsonData.newTopRow;
this.gridHandler.insertOrReplaceElement(rowMarkup, true);
}
return false;
};
//
// Private helper methods.
//
/**
* Configure paging links.
*
* @private
*/
$.pkp.classes.features.PagingFeature.prototype.configPagingLinks_ =
function() {
var options, $pagingDiv, $links, index, limit, $link, regex, match,
clickPagesCallback;
options = this.getOptions();
$pagingDiv = $('div.gridPaging', this.getGridHtmlElement());
if ($pagingDiv) {
clickPagesCallback = this.callbackWrapper(
function(sourceElement, event) {
regex = new RegExp('[?&]' + options.pageParamName +
'(?:=([^&]*))?', 'i');
match = regex.exec($(event.target).attr('href'));
if (match != null) {
options.currentPage = parseInt(match[1], 10);
this.getGridHtmlElement().trigger('dataChanged');
}
// Stop event handling.
return false;
}, this);
$links = $pagingDiv.find('a').
not('.showMoreItems').not('.showLessItems');
for (index = 0, limit = $links.length; index < limit; index++) {
$link = $($links[index]);
$link.click(clickPagesCallback);
}
}
};
/**
* Configure items per page element.
*
* @private
*/
$.pkp.classes.features.PagingFeature.prototype.configItemsPerPageElement_ =
function() {
var options, $pagingDiv, index, limit, $select, itemsPerPageValues,
changeItemsPerPageCallback;
options = this.getOptions();
$pagingDiv = $('div.gridPaging', this.getGridHtmlElement());
if ($pagingDiv) {
changeItemsPerPageCallback = this.callbackWrapper(
function(sourceElement, event) {
options.currentItemsPerPage = parseInt($('option',
event.target).filter(':selected').attr('value'), 10);
// Reset to first page.
options.currentPage = 1;
this.getGridHtmlElement().trigger('dataChanged');
// Stop event handling.
return false;
}, this);
$select = $pagingDiv.find('select.itemsPerPage');
itemsPerPageValues = [10, 25, 50, 75, 100];
if ($.inArray(options.defaultItemsPerPage,
itemsPerPageValues) < 0) {
itemsPerPageValues.push(options.defaultItemsPerPage);
}
itemsPerPageValues.sort(function(a, b) { return a - b; });
if (options.itemsTotal <= itemsPerPageValues[0]) {
$('div.gridItemsPerPage', $pagingDiv).hide();
} else {
limit = itemsPerPageValues.length - 1;
for (index = 0; index <= limit; index++) {
$select.append($('<option value="' + itemsPerPageValues[index] +
'">' + itemsPerPageValues[index] + '</option>'));
}
$select.val(options.currentItemsPerPage.toString());
$select.change(changeItemsPerPageCallback);
}
}
};
}(jQuery));