flectra/addons/web/static/src/js/chrome/pager.js
2018-01-16 02:34:37 -08:00

278 lines
8.8 KiB
JavaScript

flectra.define('web.Pager', function (require) {
"use strict";
var utils = require('web.utils');
var Widget = require('web.Widget');
var direction = {
previous: -1,
next: 1,
};
var Pager = Widget.extend({
template: "Pager",
events: {
'click .o_pager_next': '_onNext',
'click .o_pager_previous': '_onPrevious',
'click .o_pager_value': '_onEdit',
},
/**
* The pager goes from 1 to size (included).
* The current value is current_min if limit === 1
* or the interval [current_min, current_min + limit[ if limit > 1
*
* @param {Widget} [parent] the parent widget
* @param {int} [size] the total number of elements
* @param {int} [current_min] the first element of the current_page
* @param {int} [limit] the number of elements per page
* @param {boolean} [options.can_edit] editable feature of the pager
* @param {boolean} [options.single_page_hidden] (not) to display the pager
* if only one page
* @param {function} [options.validate] callback returning a Deferred to
* validate changes
*/
init: function (parent, size, current_min, limit, options) {
this.state = {
size: size,
current_min: current_min,
limit: limit,
};
Object.defineProperty(this.state, 'current_max', {
get: function() {
return Math.min(this.current_min + this.limit - 1, this.size);
}
});
this.options = _.defaults({}, options, {
can_edit: true, // editable
single_page_hidden: false, // displayed even if there is a single page
validate: function() {
return $.Deferred().resolve();
},
withAccessKey: true, // can be disabled, for example, for x2m widgets
});
this._super(parent);
},
/**
* Renders the pager
*
* @returns {jQuery.Deferred}
*/
start: function () {
this.$value = this.$('.o_pager_value');
this.$limit = this.$('.o_pager_limit');
this._render();
return this._super();
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Disables the pager's arrows and the edition
*/
disable: function () {
this.disabled = true;
this._updateArrows();
},
/**
* Enables the pager's arrows and the edition
*/
enable: function () {
this.disabled = false;
this._updateArrows();
},
/**
* Executes the next action on the pager
*/
next: function () {
this._changeSelection(direction.next);
},
/**
* Executes the previous action on the pager
*/
previous: function () {
this._changeSelection(direction.previous);
},
/**
* Sets the state of the pager and renders it
* @param {Object} [state] the values to update (size, current_min and limit)
* @param {Object} [options]
* @param {boolean} [options.notifyChange] set to true to make the pager
* notify the environment that its state changed
*/
updateState: function (state, options) {
_.extend(this.state, state);
this._render();
if (options && options.notifyChange) {
this.trigger('pager_changed', _.clone(this.state));
}
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Private function that updates the pager's state according to a pager action
*
* @param {int} [direction] the action (previous or next) on the pager
*/
_changeSelection: function (direction) {
var self = this;
this.options.validate().then(function() {
var size = self.state.size;
var current_min = self.state.current_min;
var limit = self.state.limit;
// Compute the new current_min
current_min = (current_min + limit*direction);
if (current_min > size) {
current_min = 1;
} else if ((current_min < 1) && (limit === 1)) {
current_min = size;
} else if ((current_min < 1) && (limit > 1)) {
current_min = size - ((size % limit) || limit) + 1;
}
self.state.current_min = current_min;
// The re-rendering of the pager must be done before the trigger of
// event 'pager_changed' as the rendering may enable the pager
// (and a common use is to disable the pager when this event is
// triggered, and to re-enable it when the data have been reloaded)
self._render();
self.trigger('pager_changed', _.clone(self.state));
});
},
/**
* Private function that displays an input to edit the pager's state
*/
_edit: function () {
if (this.options.can_edit) {
var self = this;
var $input = $('<input>', {class: 'o_input', type: 'text', value: this.$value.html()});
this.$value.html($input);
$input.focus();
// Event handlers
$input.click(function(ev) {
ev.stopPropagation(); // ignore clicks on the input
});
$input.blur(function(ev) {
self._save($(ev.target)); // save the state when leaving the input
});
$input.on('keydown', function (ev) {
ev.stopPropagation();
if (ev.which === $.ui.keyCode.ENTER) {
self._save($(ev.target)); // save on enter
} else if (ev.which === $.ui.keyCode.ESCAPE) {
self._render(); // leave on escape
}
});
}
},
/**
* Private function that renders the pager's state
*/
_render: function () {
var size = this.state.size;
var current_min = this.state.current_min;
var current_max = this.state.current_max;
if (size === 0 || (this.options.single_page_hidden && this._singlePage())) {
this.do_hide();
} else {
this.do_show();
this._updateArrows();
var value = "" + current_min;
if (this.state.limit > 1) {
value += "-" + current_max;
}
this.$value.html(value);
this.$limit.html(size);
}
},
/**
* Private function that saves the state from the content of the input
*
* @param {jQuery} [$input] the jQuery element containing the new state
*/
_save: function ($input) {
var self = this;
this.options.validate().then(function() {
var value = $input.val().split("-");
var min = utils.confine(parseInt(value[0], 10), 1, self.state.size);
var max = utils.confine(parseInt(value[1], 10), 1, self.state.size);
if (!isNaN(min)) {
self.state.current_min = min;
if (!isNaN(max)) {
self.state.limit = utils.confine(max-min+1, 1, self.state.size);
} else {
// The state has been given as a single value -> set the limit to 1
self.state.limit = 1;
}
self.trigger('pager_changed', _.clone(self.state));
}
}).always(function() {
// Render the pager's new state (removes the input)
self._render();
});
},
/**
* @private
* @returns {boolean} true iff there is only one page
*/
_singlePage: function () {
var state = this.state;
return (1 === state.current_min) && (state.current_max === state.size);
},
/**
* Updates the arrows' disable attribute: true iff the pager is disabed or
* if there is only one page
*
* @private
*/
_updateArrows: function () {
var disabled = this.disabled || this._singlePage();
this.$('button').prop('disabled', disabled);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {MouseEvent} event
*/
_onEdit: function (event) {
event.stopPropagation();
if (!this.disabled) {
this._edit();
}
},
/**
* @private
* @param {MouseEvent} event
*/
_onNext: function (event) {
event.stopPropagation();
this.next();
},
/**
* @private
* @param {MouseEvent} event
*/
_onPrevious: function (event) {
event.stopPropagation();
this.previous();
},
});
return Pager;
});