web-apps/vendor/touch/src/slider/Slider.js
Maxim Kadushkin 741b10515d webapps added
2016-03-10 21:48:53 -03:00

546 lines
15 KiB
JavaScript

/**
* Utility class used by Ext.field.Slider.
* @private
*/
Ext.define('Ext.slider.Slider', {
extend: 'Ext.Container',
xtype: 'slider',
requires: [
'Ext.slider.Thumb',
'Ext.fx.easing.EaseOut'
],
/**
* @event change
* Fires when the value changes
* @param {Ext.slider.Slider} this
* @param {Ext.slider.Thumb} thumb The thumb being changed
* @param {Number} newValue The new value
* @param {Number} oldValue The old value
*/
/**
* @event dragstart
* Fires when the slider thumb starts a drag
* @param {Ext.slider.Slider} this
* @param {Ext.slider.Thumb} thumb The thumb being dragged
* @param {Array} value The start value
* @param {Ext.EventObject} e
*/
/**
* @event drag
* Fires when the slider thumb starts a drag
* @param {Ext.slider.Slider} this
* @param {Ext.slider.Thumb} thumb The thumb being dragged
* @param {Ext.EventObject} e
*/
/**
* @event dragend
* Fires when the slider thumb starts a drag
* @param {Ext.slider.Slider} this
* @param {Ext.slider.Thumb} thumb The thumb being dragged
* @param {Array} value The end value
* @param {Ext.EventObject} e
*/
config: {
baseCls: 'x-slider',
/**
* @cfg {Object} thumbConfig The config object to factory {@link Ext.slider.Thumb} instances
* @accessor
*/
thumbConfig: {
draggable: {
translatable: {
easingX: {
duration: 300,
type: 'ease-out'
}
}
}
},
/**
* @cfg {Number} increment The increment by which to snap each thumb when its value changes. Any thumb movement
* will be snapped to the nearest value that is a multiple of the increment (e.g. if increment is 10 and the user
* tries to move the thumb to 67, it will be snapped to 70 instead)
* @accessor
*/
increment : 1,
/**
* @cfg {Number/Number[]} value The value(s) of this slider's thumbs. If you pass
* a number, it will assume you have just 1 thumb.
* @accessor
*/
value: 0,
/**
* @cfg {Number} minValue The lowest value any thumb on this slider can be set to.
* @accessor
*/
minValue: 0,
/**
* @cfg {Number} maxValue The highest value any thumb on this slider can be set to.
* @accessor
*/
maxValue: 100,
/**
* @cfg {Boolean} allowThumbsOverlapping Whether or not to allow multiple thumbs to overlap each other.
* Setting this to true guarantees the ability to select every possible value in between {@link #minValue}
* and {@link #maxValue} that satisfies {@link #increment}
* @accessor
*/
allowThumbsOverlapping: false,
/**
* @cfg {Boolean/Object} animation
* The animation to use when moving the slider. Possible properties are:
*
* - duration
* - easingX
* - easingY
*
* @accessor
*/
animation: true,
/**
* Will make this field read only, meaning it cannot be changed with used interaction.
* @cfg {Boolean} readOnly
* @accessor
*/
readOnly: false
},
/**
* @cfg {Number/Number[]} values Alias to {@link #value}
*/
elementWidth: 0,
offsetValueRatio: 0,
activeThumb: null,
constructor: function(config) {
config = config || {};
if (config.hasOwnProperty('values')) {
config.value = config.values;
}
this.callParent([config]);
},
// @private
initialize: function() {
var element = this.element;
this.callParent();
element.on({
scope: this,
tap: 'onTap',
resize: 'onResize'
});
this.on({
scope: this,
delegate: '> thumb',
dragstart: 'onThumbDragStart',
drag: 'onThumbDrag',
dragend: 'onThumbDragEnd'
});
},
/**
* @private
*/
factoryThumb: function() {
return Ext.factory(this.getThumbConfig(), Ext.slider.Thumb);
},
/**
* Returns the Thumb instances bound to this Slider
* @return {Ext.slider.Thumb[]} The thumb instances
*/
getThumbs: function() {
return this.innerItems;
},
/**
* Returns the Thumb instance bound to this Slider
* @param {Number} [index=0] The index of Thumb to return.
* @return {Ext.slider.Thumb} The thumb instance
*/
getThumb: function(index) {
if (typeof index != 'number') {
index = 0;
}
return this.innerItems[index];
},
refreshOffsetValueRatio: function() {
var valueRange = this.getMaxValue() - this.getMinValue(),
trackWidth = this.elementWidth - this.thumbWidth;
this.offsetValueRatio = trackWidth / valueRange;
},
onResize: function(element, info) {
var thumb = this.getThumb(0);
if (thumb) {
this.thumbWidth = thumb.getElementWidth();
}
this.elementWidth = info.width;
this.refresh();
},
refresh: function() {
this.refreshValue();
},
setActiveThumb: function(thumb) {
var oldActiveThumb = this.activeThumb;
if (oldActiveThumb && oldActiveThumb !== thumb) {
oldActiveThumb.setZIndex(null);
}
this.activeThumb = thumb;
thumb.setZIndex(2);
return this;
},
onThumbDragStart: function(thumb, e) {
if (e.absDeltaX <= e.absDeltaY || this.getReadOnly()) {
return false;
}
else {
e.stopPropagation();
}
if (this.getAllowThumbsOverlapping()) {
this.setActiveThumb(thumb);
}
this.dragStartValue = this.getValue()[this.getThumbIndex(thumb)];
this.fireEvent('dragstart', this, thumb, this.dragStartValue, e);
},
onThumbDrag: function(thumb, e, offsetX) {
var index = this.getThumbIndex(thumb),
offsetValueRatio = this.offsetValueRatio,
constrainedValue = this.constrainValue(this.getMinValue() + offsetX / offsetValueRatio);
e.stopPropagation();
this.setIndexValue(index, constrainedValue);
this.fireEvent('drag', this, thumb, this.getValue(), e);
return false;
},
setIndexValue: function(index, value, animation) {
var thumb = this.getThumb(index),
values = this.getValue(),
offsetValueRatio = this.offsetValueRatio,
draggable = thumb.getDraggable();
draggable.setOffset((value - this.getMinValue()) * offsetValueRatio, null, animation);
values[index] = value;
},
onThumbDragEnd: function(thumb, e) {
this.refreshThumbConstraints(thumb);
var index = this.getThumbIndex(thumb),
newValue = this.getValue()[index],
oldValue = this.dragStartValue;
this.fireEvent('dragend', this, thumb, this.getValue(), e);
if (oldValue !== newValue) {
this.fireEvent('change', this, thumb, newValue, oldValue);
}
},
getThumbIndex: function(thumb) {
return this.getThumbs().indexOf(thumb);
},
refreshThumbConstraints: function(thumb) {
var allowThumbsOverlapping = this.getAllowThumbsOverlapping(),
offsetX = thumb.getDraggable().getOffset().x,
thumbs = this.getThumbs(),
index = this.getThumbIndex(thumb),
previousThumb = thumbs[index - 1],
nextThumb = thumbs[index + 1],
thumbWidth = this.thumbWidth;
if (previousThumb) {
previousThumb.getDraggable().addExtraConstraint({
max: {
x: offsetX - ((allowThumbsOverlapping) ? 0 : thumbWidth)
}
});
}
if (nextThumb) {
nextThumb.getDraggable().addExtraConstraint({
min: {
x: offsetX + ((allowThumbsOverlapping) ? 0 : thumbWidth)
}
});
}
},
// @private
onTap: function(e) {
if (this.isDisabled()) {
return;
}
var targetElement = Ext.get(e.target);
if (!targetElement || targetElement.hasCls('x-thumb')) {
return;
}
var touchPointX = e.touch.point.x,
element = this.element,
elementX = element.getX(),
offset = touchPointX - elementX - (this.thumbWidth / 2),
value = this.constrainValue(this.getMinValue() + offset / this.offsetValueRatio),
values = this.getValue(),
minDistance = Infinity,
ln = values.length,
i, absDistance, testValue, closestIndex, oldValue, thumb;
if (ln === 1) {
closestIndex = 0;
}
else {
for (i = 0; i < ln; i++) {
testValue = values[i];
absDistance = Math.abs(testValue - value);
if (absDistance < minDistance) {
minDistance = absDistance;
closestIndex = i;
}
}
}
oldValue = values[closestIndex];
thumb = this.getThumb(closestIndex);
this.setIndexValue(closestIndex, value, this.getAnimation());
this.refreshThumbConstraints(thumb);
if (oldValue !== value) {
this.fireEvent('change', this, thumb, value, oldValue);
}
},
// @private
updateThumbs: function(newThumbs) {
this.add(newThumbs);
},
applyValue: function(value) {
var values = Ext.Array.from(value || 0),
filteredValues = [],
previousFilteredValue = this.getMinValue(),
filteredValue, i, ln;
for (i = 0,ln = values.length; i < ln; i++) {
filteredValue = this.constrainValue(values[i]);
if (filteredValue < previousFilteredValue) {
//<debug warn>
Ext.Logger.warn("Invalid values of '"+Ext.encode(values)+"', values at smaller indexes must " +
"be smaller than or equal to values at greater indexes");
//</debug>
filteredValue = previousFilteredValue;
}
filteredValues.push(filteredValue);
previousFilteredValue = filteredValue;
}
return filteredValues;
},
/**
* Updates the sliders thumbs with their new value(s)
*/
updateValue: function(newValue, oldValue) {
var thumbs = this.getThumbs(),
ln = newValue.length,
minValue = this.getMinValue(),
offset = this.offsetValueRatio,
i;
this.setThumbsCount(ln);
for (i = 0; i < ln; i++) {
thumbs[i].getDraggable().setExtraConstraint(null).setOffset((newValue[i] - minValue) * offset);
}
for (i = 0; i < ln; i++) {
this.refreshThumbConstraints(thumbs[i]);
}
},
/**
* @private
*/
refreshValue: function() {
this.refreshOffsetValueRatio();
this.setValue(this.getValue());
},
/**
* @private
* Takes a desired value of a thumb and returns the nearest snap value. e.g if minValue = 0, maxValue = 100, increment = 10 and we
* pass a value of 67 here, the returned value will be 70. The returned number is constrained within {@link #minValue} and {@link #maxValue},
* so in the above example 68 would be returned if {@link #maxValue} was set to 68.
* @param {Number} value The value to snap
* @return {Number} The snapped value
*/
constrainValue: function(value) {
var me = this,
minValue = me.getMinValue(),
maxValue = me.getMaxValue(),
increment = me.getIncrement(),
remainder;
value = parseFloat(value);
if (isNaN(value)) {
value = minValue;
}
remainder = (value - minValue) % increment;
value -= remainder;
if (Math.abs(remainder) >= (increment / 2)) {
value += (remainder > 0) ? increment : -increment;
}
value = Math.max(minValue, value);
value = Math.min(maxValue, value);
return value;
},
setThumbsCount: function(count) {
var thumbs = this.getThumbs(),
thumbsCount = thumbs.length,
i, ln, thumb;
if (thumbsCount > count) {
for (i = 0,ln = thumbsCount - count; i < ln; i++) {
thumb = thumbs[thumbs.length - 1];
thumb.destroy();
}
}
else if (thumbsCount < count) {
for (i = 0,ln = count - thumbsCount; i < ln; i++) {
this.add(this.factoryThumb());
}
}
return this;
},
/**
* Convenience method. Calls {@link #setValue}.
*/
setValues: function(value) {
this.setValue(value);
},
/**
* Convenience method. Calls {@link #getValue}.
* @return {Object}
*/
getValues: function() {
return this.getValue();
},
/**
* Sets the {@link #increment} configuration.
* @param {Number} increment
* @return {Number}
*/
applyIncrement: function(increment) {
if (increment === 0) {
increment = 1;
}
return Math.abs(increment);
},
// @private
updateAllowThumbsOverlapping: function(newValue, oldValue) {
if (typeof oldValue != 'undefined') {
this.refreshValue();
}
},
// @private
updateMinValue: function(newValue, oldValue) {
if (typeof oldValue != 'undefined') {
this.refreshValue();
}
},
// @private
updateMaxValue: function(newValue, oldValue) {
if (typeof oldValue != 'undefined') {
this.refreshValue();
}
},
// @private
updateIncrement: function(newValue, oldValue) {
if (typeof oldValue != 'undefined') {
this.refreshValue();
}
},
doSetDisabled: function(disabled) {
this.callParent(arguments);
var items = this.getItems().items,
ln = items.length,
i;
for (i = 0; i < ln; i++) {
items[i].setDisabled(disabled);
}
}
}, function() {
//<deprecated product=touch since=2.0>
/**
* @cfg {Boolean} animationDuration
* Animation duration in ms.
* @removed 2.0.0 Use the duration property on the animation config instead.
*/
Ext.deprecateProperty(this, 'animationDuration', null, "Ext.slider.Slider.animationDuration has been removed");
//</deprecated>
});