Type.registerNamespace('Sys.Extended.UI.Animation');
Sys.Extended.UI.Animation.AnimationBehavior = function(element) {
// The AnimationBehavior allows us to associate animations (described by JSON) with events and
// have them play when the events are fired. It relies heavily on the AJAX Control Toolkit
// animation framework provided in Animation.js, and the GenericAnimationBehavior defined below.
Sys.Extended.UI.Animation.AnimationBehavior.initializeBase(this, [element]);
// Generic animation behaviors that automatically build animations from JSON descriptions
this._onLoad = null;
this._onClick = null;
this._onMouseOver = null;
this._onMouseOut = null;
this._onHoverOver = null;
this._onHoverOut = null;
// Handlers for the events
this._onClickHandler = null;
this._onMouseOverHandler = null;
this._onMouseOutHandler = null;
}
Sys.Extended.UI.Animation.AnimationBehavior.prototype = {
initialize: function() {
Sys.Extended.UI.Animation.AnimationBehavior.callBaseMethod(this, 'initialize');
// Wireup the event handlers
var element = this.get_element();
if(element) {
this._onClickHandler = Function.createDelegate(this, this.OnClick);
$addHandler(element, 'click', this._onClickHandler);
this._onMouseOverHandler = Function.createDelegate(this, this.OnMouseOver);
$addHandler(element, 'mouseover', this._onMouseOverHandler);
this._onMouseOutHandler = Function.createDelegate(this, this.OnMouseOut);
$addHandler(element, 'mouseout', this._onMouseOutHandler);
}
},
dispose: function() {
// Remove the event handlers
var element = this.get_element();
if(element) {
if(this._onClickHandler) {
$removeHandler(element, 'click', this._onClickHandler);
this._onClickHandler = null;
}
if(this._onMouseOverHandler) {
$removeHandler(element, 'mouseover', this._onMouseOverHandler);
this._onMouseOverHandler = null;
}
if(this._onMouseOutHandler) {
$removeHandler(element, 'mouseout', this._onMouseOutHandler);
this._onMouseOutHandler = null;
}
}
// Wipe the behaviors (we don't need to dispose them because
// that will happen automatically in our base dispose)
this._onLoad = null;
this._onClick = null;
this._onMouseOver = null;
this._onMouseOut = null;
this._onHoverOver = null;
this._onHoverOut = null;
Sys.Extended.UI.Animation.AnimationBehavior.callBaseMethod(this, 'dispose');
},
get_OnLoad: function() {
// Generic OnLoad Animation's JSON definition
// Setting the OnLoad property will cause it to be played immediately
return this._onLoad ? this._onLoad.get_json() : null;
},
set_OnLoad: function(value) {
if(!this._onLoad) {
this._onLoad = new Sys.Extended.UI.Animation.GenericAnimationBehavior(this.get_element());
this._onLoad.initialize();
}
this._onLoad.set_json(value);
this.raisePropertyChanged('OnLoad');
this._onLoad.play();
},
get_OnLoadBehavior: function() {
// Generic OnLoad Animation's behavior
return this._onLoad;
},
get_OnClick: function() {
// Generic OnClick Animation's JSON definition
return this._onClick ? this._onClick.get_json() : null;
},
set_OnClick: function(value) {
if(!this._onClick) {
this._onClick = new Sys.Extended.UI.Animation.GenericAnimationBehavior(this.get_element());
this._onClick.initialize();
}
this._onClick.set_json(value);
this.raisePropertyChanged('OnClick');
},
get_OnClickBehavior: function() {
// Generic OnClick Animation's behavior
return this._onClick;
},
OnClick: function() {
if(this._onClick) {
this._onClick.play();
}
},
get_OnMouseOver: function() {
// Generic OnMouseOver Animation's JSON definition
return this._onMouseOver ? this._onMouseOver.get_json() : null;
},
set_OnMouseOver: function(value) {
if(!this._onMouseOver) {
this._onMouseOver = new Sys.Extended.UI.Animation.GenericAnimationBehavior(this.get_element());
this._onMouseOver.initialize();
}
this._onMouseOver.set_json(value);
this.raisePropertyChanged('OnMouseOver');
},
get_OnMouseOverBehavior: function() {
// Generic OnMouseOver Animation's behavior
return this._onMouseOver;
},
OnMouseOver: function() {
// Play the OnMouseOver/OnHoverOver animations
// Private property to track whether a mouseOver event has already occured, and thus eliminate dups
if(this._mouseHasEntered) {
// This is an additional onMouseOver triggered by child content
return;
}
if(this._onMouseOver) {
this._onMouseOver.play();
}
if(this._onHoverOver) {
if(this._onHoverOut) {
this._onHoverOut.quit();
}
this._onHoverOver.play();
}
this._mouseHasEntered = true;
},
get_OnMouseOut: function() {
// Generic OnMouseOut Animation's JSON definition
return this._onMouseOut ? this._onMouseOut.get_json() : null;
},
set_OnMouseOut: function(value) {
if(!this._onMouseOut) {
this._onMouseOut = new Sys.Extended.UI.Animation.GenericAnimationBehavior(this.get_element());
this._onMouseOut.initialize();
}
this._onMouseOut.set_json(value);
this.raisePropertyChanged('OnMouseOut');
},
get_OnMouseOutBehavior: function() {
// Generic OnMouseOut Animation's behavior
return this._onMouseOut;
},
OnMouseOut: function(e) {
// Play the OnMouseOver/OnHoverOver animations
var ev = e.rawEvent;
var pt = this.get_element();
var tg = e.target;
if(tg.nodeName !== pt.nodeName) return;
var rel = ev.relatedTarget || ev.toElement;
if(pt != rel && !this._isChild(pt, rel)) {
this._mouseHasEntered = false;
if(this._onMouseOut) {
this._onMouseOut.play();
}
if(this._onHoverOut) {
if(this._onHoverOver) {
this._onHoverOver.quit();
}
this._onHoverOut.play();
}
}
},
_isChild: function(elmtA, elmtB) {
var body = document.body;
while(elmtB && elmtA != elmtB && body != elmtB) {
try {
elmtB = elmtB.parentNode;
} catch(e) {
// Firefox sometimes fires events for XUL elements, which throws
// a "permission denied" error. so this is not a child.
return false;
}
}
return elmtA == elmtB;
},
get_OnHoverOver: function() {
// Generic OnHoverOver Animation's JSON definition
return this._onHoverOver ? this._onHoverOver.get_json() : null;
},
set_OnHoverOver: function(value) {
if(!this._onHoverOver) {
this._onHoverOver = new Sys.Extended.UI.Animation.GenericAnimationBehavior(this.get_element());
this._onHoverOver.initialize();
}
this._onHoverOver.set_json(value);
this.raisePropertyChanged('OnHoverOver');
},
get_OnHoverOverBehavior: function() {
// Generic OnHoverOver Animation's behavior
return this._onHoverOver;
},
get_OnHoverOut: function() {
// Generic OnHoverOut Animation's JSON definition
return this._onHoverOut ? this._onHoverOut.get_json() : null;
},
set_OnHoverOut: function(value) {
if(!this._onHoverOut) {
this._onHoverOut = new Sys.Extended.UI.Animation.GenericAnimationBehavior(this.get_element());
this._onHoverOut.initialize();
}
this._onHoverOut.set_json(value);
this.raisePropertyChanged('OnHoverOut');
},
get_OnHoverOutBehavior: function() {
// Generic OnHoverOut Animation's behavior
return this._onHoverOut;
}
}
Sys.Extended.UI.Animation.AnimationBehavior.registerClass('Sys.Extended.UI.Animation.AnimationBehavior', Sys.Extended.UI.BehaviorBase);
Sys.Extended.UI.Animation.GenericAnimationBehavior = function(element) {
// The GenericAnimationBehavior handles the creation, playing, and disposing of animations
// created from a JSON description. As we intend to expose a lot of generic animations
// across the Toolkit, this behavior serves to simplify the amount of work required.
Sys.Extended.UI.Animation.GenericAnimationBehavior.initializeBase(this, [element]);
// JSON description of the animation that will be used to create it
this._json = null;
// Animation created from the JSON description that will be played
this._animation = null;
}
Sys.Extended.UI.Animation.GenericAnimationBehavior.prototype = {
dispose: function() {
// Dispose the behavior and its animation
this.disposeAnimation();
Sys.Extended.UI.Animation.GenericAnimationBehavior.callBaseMethod(this, 'dispose');
},
disposeAnimation: function() {
if(this._animation) {
this._animation.dispose();
}
this._animation = null;
},
play: function() {
// Play the animation if it isn't already playing. If it's already playing, this does nothing.
if(this._animation && !this._animation.get_isPlaying()) {
this.stop();
this._animation.play();
}
},
stop: function() {
if(this._animation) {
if(this._animation.get_isPlaying()) {
this._animation.stop(true);
}
}
},
quit: function() {
// Quit playing the animation without updating the final state (i.e. if
// the animation was moving, this would leave it in the middle of its path).
// This differs from the stop function which will update the final state. The
// quit function is most useful for scenarios where you're toggling back and forth
// between two animations (like those used in OnHoverOver/OnHoverOut) and you don't
// want to completely finish one animation if its counterpart is triggered.
if(this._animation) {
if(this._animation.get_isPlaying()) {
this._animation.stop(false);
}
}
},
get_json: function() {
return this._json;
},
set_json: function(value) {
// Only wipe and rebuild if they're changing the value
if(this._json != value) {
this._json = value;
this.raisePropertyChanged('json');
// Build the new animation
this.disposeAnimation();
var element = this.get_element();
if(element) {
this._animation = Sys.Extended.UI.Animation.buildAnimation(this._json, element);
if(this._animation) {
this._animation.initialize();
}
this.raisePropertyChanged('animation');
}
}
},
get_animation: function() {
// Animation created from the JSON description
return this._animation;
}
}
Sys.Extended.UI.Animation.GenericAnimationBehavior.registerClass('Sys.Extended.UI.Animation.GenericAnimationBehavior', Sys.Extended.UI.BehaviorBase);