/**
* @class Ext.draw.modifier.Highlight
* @extends Ext.draw.modifier.Modifier
*
* Highlight is a modifier that will override sprite attributes
* with {@link Ext.draw.modifier.Highlight#style style} attributes
* when sprite's `highlighted` attribute is true.
*/
Ext.define('Ext.draw.modifier.Highlight', {
extend: 'Ext.draw.modifier.Modifier',
alias: 'modifier.highlight',
config: {
/**
* @cfg {Boolean} enabled 'true' if the highlight is applied.
*/
enabled: false,
/**
* @cfg {Object} style The style attributes of the highlight modifier.
*/
style: null
},
preFx: true,
applyStyle: function(style, oldStyle) {
oldStyle = oldStyle || {};
if (this.getSprite()) {
Ext.apply(oldStyle, this.getSprite().self.def.normalize(style));
}
else {
Ext.apply(oldStyle, style);
}
return oldStyle;
},
prepareAttributes: function(attr) {
if (!attr.hasOwnProperty('highlightOriginal')) {
attr.highlighted = false;
attr.highlightOriginal = Ext.Object.chain(attr);
attr.highlightOriginal.prototype = attr;
// A list of attributes that should be removed from a sprite instance
// when it is unhighlighted.
attr.highlightOriginal.removeFromInstance = {};
}
if (this._lower) {
this._lower.prepareAttributes(attr.highlightOriginal);
}
},
updateSprite: function(sprite, oldSprite) {
var me = this,
style = me.getStyle(),
attributeDefinitions;
if (sprite) {
attributeDefinitions = sprite.self.def;
if (style) {
me._style = attributeDefinitions.normalize(style);
}
me.setStyle(sprite.config.highlight);
// Add highlight related attributes to sprite's attribute definition.
// This will affect all sprites of the same type, even those without
// the highlight modifier.
attributeDefinitions.setConfig({
defaults: {
highlighted: false
},
processors: {
highlighted: 'bool'
}
});
}
this.setSprite(sprite);
},
/**
* @private
* Filter out modifier changes that override highlight style or source attributes.
* @param {Object} attr The source attributes.
* @param {Object} changes The modifier changes.
* @return {*} The filtered changes.
*/
filterChanges: function(attr, changes) {
var me = this,
highlightOriginal = attr.highlightOriginal,
style = me.getStyle(),
name;
if (attr.highlighted) {
for (name in changes) {
if (style.hasOwnProperty(name)) {
// If sprite is highlighted, then stash the changes
// to the `style` attributes made by lower level modifiers
// to apply them later when sprite is unhighlighted.
highlightOriginal[name] = changes[name];
delete changes[name];
}
}
}
return changes;
},
pushDown: function(attr, changes) {
var style = this.getStyle(),
highlightOriginal = attr.highlightOriginal,
removeFromInstance = highlightOriginal.removeFromInstance,
highlighted, name, tplAttr, timer;
if (changes.hasOwnProperty('highlighted')) {
highlighted = changes.highlighted;
// Hide `highlighted` and `style` from underlying modifiers.
delete changes.highlighted;
if (this._lower) {
changes = this._lower.pushDown(highlightOriginal, changes);
}
changes = this.filterChanges(attr, changes);
if (highlighted !== attr.highlighted) {
if (highlighted) {
// Switching ON.
// At this time, original should be empty.
for (name in style) {
// Remember the values of attributes to revert back to them on unhighlight.
if (name in changes) {
// Remember value set by lower level modifiers.
highlightOriginal[name] = changes[name];
}
else {
// Remember the original value.
// If this is a sprite instance and it doesn't have its own
// 'name' attribute, (i.e. inherits template's attribute value)
// than we have to get the value for the 'name' attribute from
// the template's 'targets' object instead of its
// 'attr' object (which is the prototype of the instance),
// because the 'name' attribute of the template may be animating.
// Check out the prepareAttributes method of the Animation
// modifier for more details on the 'targets' object.
tplAttr = attr.template && attr.template.ownAttr;
if (tplAttr && !attr.prototype.hasOwnProperty(name)) {
removeFromInstance[name] = true;
highlightOriginal[name] = tplAttr.targets[name];
}
else {
// Even if a sprite instance has its own property, it may
// still have to be removed from the instance after
// unhighlighting is done.
// Consider a situation where an instance doesn't originally
// have its own attribute (that is used for highlighting and
// unhighlighting). It will however have that attribute as
// its own when the highlight/unhighlight animation is in
// progress, until the attribute is removed from the instance
// when the unhighlighting is done.
// So in a scenario where the instance is highlighted, then
// unhighlighted (i.e. starts animating back to its original
// value) and then highlighted again before the unhighlight
// animation is done, we should still mark the attribute
// for removal from the instance, if it was our original
// intention. To tell if it was, we can check the timer
// for the attribute and see if the 'remove' flag is set.
timer = highlightOriginal.timers[name];
if (timer && timer.remove) {
removeFromInstance[name] = true;
}
highlightOriginal[name] = attr[name];
}
}
if (highlightOriginal[name] !== style[name]) {
changes[name] = style[name];
}
}
}
else {
// Switching OFF.
for (name in style) {
if (!(name in changes)) {
changes[name] = highlightOriginal[name];
}
delete highlightOriginal[name];
}
changes.removeFromInstance = changes.removeFromInstance || {};
// Let the higher lever animation modifier know which attributes
// should be removed from instance when the animation is done.
Ext.apply(changes.removeFromInstance, removeFromInstance);
highlightOriginal.removeFromInstance = {};
}
changes.highlighted = highlighted;
}
}
else {
if (this._lower) {
changes = this._lower.pushDown(highlightOriginal, changes);
}
changes = this.filterChanges(attr, changes);
}
return changes;
},
popUp: function(attr, changes) {
changes = this.filterChanges(attr, changes);
this.callParent([attr, changes]);
}
});