/**
* @class Ext.chart.interactions.Abstract
*
* Defines a common abstract parent class for all interactions.
*
*/
Ext.define('Ext.chart.interactions.Abstract', {
xtype: 'interaction',
mixins: {
observable: 'Ext.mixin.Observable'
},
config: {
/**
* @cfg {Object} gesture
* Maps gestures that should be used for starting/maintaining/ending the interaction
* to corresponding class methods.
* @private
*/
gestures: {
tap: 'onGesture'
},
/**
* @cfg {Ext.chart.AbstractChart} chart The chart that the interaction is bound.
*/
chart: null,
/**
* @cfg {Boolean} enabled 'true' if the interaction is enabled.
*/
enabled: true
},
/**
* Android device is emerging too many events so if we re-render every frame it will
* take forever to finish a frame.
* This throttle technique will limit the timespan between two frames.
*/
throttleGap: 0,
stopAnimationBeforeSync: false,
constructor: function(config) {
var me = this,
id;
config = config || {};
if ('id' in config) {
id = config.id;
}
else if ('id' in me.config) {
id = me.config.id;
}
else {
id = me.getId();
}
me.setId(id);
me.mixins.observable.constructor.call(me, config);
},
updateChart: function(newChart, oldChart) {
var me = this;
if (oldChart === newChart) {
return;
}
if (oldChart) {
oldChart.unregister(me);
me.removeChartListener(oldChart);
}
if (newChart) {
newChart.register(me);
me.addChartListener();
}
},
updateEnabled: function(enabled) {
var me = this,
chart = me.getChart();
if (chart) {
if (enabled) {
me.addChartListener();
}
else {
me.removeChartListener(chart);
}
}
},
/**
* @method
* @protected
* Placeholder method.
*/
onGesture: Ext.emptyFn,
/**
* @protected
* Find and return a single series item corresponding to the given event,
* or null if no matching item is found.
* @param {Event} e
* @return {Object} the item object or null if none found.
*/
getItemForEvent: function(e) {
var me = this,
chart = me.getChart(),
chartXY = chart.getEventXY(e);
return chart.getItemForPoint(chartXY[0], chartXY[1]);
},
/**
* Find and return all series items corresponding to the given event.
* @param {Event} e
* @return {Array} array of matching item objects
* @private
* @deprecated 6.5.2 This method is deprecated
*/
getItemsForEvent: function(e) {
var me = this,
chart = me.getChart(),
chartXY = chart.getEventXY(e);
return chart.getItemsForPoint(chartXY[0], chartXY[1]);
},
/**
* @private
*/
addChartListener: function() {
var me = this,
chart = me.getChart(),
gestures = me.getGestures(),
gesture;
if (!me.getEnabled()) {
return;
}
function insertGesture(name, fn) {
chart.addElementListener(
name,
// wrap the handler so it does not fire if the event is locked
// by another interaction
me.listeners[name] = function(e) {
var locks = me.getLocks(),
result;
if (me.getEnabled() && (!(name in locks) || locks[name] === me)) {
result = (Ext.isFunction(fn) ? fn : me[fn]).apply(this, arguments);
if (result === false && e && e.stopPropagation) {
e.stopPropagation();
}
return result;
}
},
me
);
}
me.listeners = me.listeners || {};
for (gesture in gestures) {
insertGesture(gesture, gestures[gesture]);
}
},
removeChartListener: function(chart) {
var me = this,
gestures = me.getGestures(),
gesture;
function removeGesture(name) {
var fn = me.listeners[name];
if (fn) {
chart.removeElementListener(name, fn);
delete me.listeners[name];
}
}
if (me.listeners) {
for (gesture in gestures) {
removeGesture(gesture);
}
}
},
lockEvents: function() {
var me = this,
locks = me.getLocks(),
args = Array.prototype.slice.call(arguments),
i = args.length;
while (i--) {
locks[args[i]] = me;
}
},
unlockEvents: function() {
var locks = this.getLocks(),
args = Array.prototype.slice.call(arguments),
i = args.length;
while (i--) {
delete locks[args[i]];
}
},
getLocks: function() {
var chart = this.getChart();
return chart.lockedEvents || (chart.lockedEvents = {});
},
doSync: function() {
var me = this,
chart = me.getChart();
if (me.syncTimer) {
Ext.undefer(me.syncTimer);
me.syncTimer = null;
}
if (me.stopAnimationBeforeSync) {
chart.animationSuspendCount++;
}
chart.redraw();
if (me.stopAnimationBeforeSync) {
chart.animationSuspendCount--;
}
me.syncThrottle = Date.now() + me.throttleGap;
},
sync: function() {
var me = this;
if (me.throttleGap && Ext.frameStartTime < me.syncThrottle) {
if (me.syncTimer) {
return;
}
me.syncTimer = Ext.defer(function() {
me.doSync();
}, me.throttleGap);
}
else {
me.doSync();
}
},
getItemId: function() {
return this.getId();
},
isXType: function(xtype) {
return xtype === 'interaction';
},
destroy: function() {
var me = this;
me.setChart(null);
delete me.listeners;
me.callParent();
}
}, function() {
if (Ext.os.is.Android4) {
this.prototype.throttleGap = 40;
}
});