2015-02-10 19:41:24 +00:00
|
|
|
/*global Firmin */
|
|
|
|
|
|
|
|
define(function(require, exports, module) {
|
|
|
|
main.consumes = ["Plugin", "settings", "util"];
|
|
|
|
main.provides = ["anims"];
|
|
|
|
return main;
|
|
|
|
|
|
|
|
function main(options, imports, register) {
|
|
|
|
var Plugin = imports.Plugin;
|
|
|
|
var settings = imports.settings;
|
|
|
|
var util = imports.util;
|
|
|
|
|
|
|
|
require("./lib_firmin");
|
|
|
|
|
|
|
|
var EventEmitter = require("events").EventEmitter;
|
|
|
|
|
|
|
|
/***** Initialization *****/
|
|
|
|
|
|
|
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
|
|
|
var emit = plugin.getEmitter();
|
|
|
|
|
|
|
|
var animating = false;
|
|
|
|
|
|
|
|
/***** Methods *****/
|
|
|
|
|
|
|
|
var pfx = apf.isWebkit ? "webkit" : (apf.isIE ? "MS" : "");
|
|
|
|
function cssAnimate(el, options, finish) {
|
|
|
|
if (options.splitbox)
|
|
|
|
return util.nextFrame($cssAnimate.bind(null, el, options, finish));
|
|
|
|
// todo using $cssAnimate instead of firmin displays spurious animations when dragging tabs
|
|
|
|
Firmin.animate(el, options, options && options.duration || 0.2, function() {
|
|
|
|
el.style[apf.CSSPREFIX + "Transition"] = "";
|
|
|
|
finish && finish();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function $cssAnimate(el, options, finish) {
|
|
|
|
// Duration
|
|
|
|
var duration = options.duration || "0.2s";
|
|
|
|
if (typeof duration == "string") {
|
|
|
|
duration = parseFloat(duration)
|
|
|
|
* (duration[duration.length - 2] == "m" ? 1 : 0.001);
|
|
|
|
}
|
|
|
|
el.style[apf.CSSPREFIX + "TransitionDuration"] = duration + "s";
|
|
|
|
|
|
|
|
// Delay
|
|
|
|
var delay = options.delay || "0s";
|
|
|
|
el.style[apf.CSSPREFIX + "TransitionDelay"] =
|
|
|
|
typeof delay == "string" ? delay : delay + "s";
|
|
|
|
|
|
|
|
// Timing Function
|
|
|
|
var timingFunction = options.timingFunction || 'linear';
|
|
|
|
if (timingFunction)
|
|
|
|
el.style[apf.CSSPREFIX + "TransitionTimingFunction"] =
|
|
|
|
timingFunction;
|
|
|
|
|
|
|
|
// Properties
|
|
|
|
var props = [];
|
|
|
|
for (var prop in options){
|
|
|
|
if (/duration|delay|timingFunction|immediate/.test(prop)) continue;
|
|
|
|
props.push(prop);
|
|
|
|
}
|
|
|
|
el.style[apf.CSSPREFIX + "TransitionProperty"] = props.join(",");
|
|
|
|
|
|
|
|
var eventName = pfx ? pfx + "TransitionEnd" : "transitionend";
|
|
|
|
var wait = el.wait = function(e) {
|
|
|
|
if (!wait) return;
|
|
|
|
el.removeEventListener(eventName, wait, false);
|
|
|
|
el.style[apf.CSSPREFIX + "Transition"] = "";
|
|
|
|
if (el.wait !== wait)
|
|
|
|
return;
|
|
|
|
wait = el.wait = null;
|
|
|
|
finish && finish();
|
|
|
|
};
|
|
|
|
el.addEventListener(eventName, wait, false);
|
|
|
|
// fallback in case there is no event
|
|
|
|
setTimeout(function() {
|
|
|
|
wait && wait();
|
|
|
|
}, duration * 1000 + 10);
|
|
|
|
|
|
|
|
props.forEach(function(name){ el.style[name] = options[name] });
|
|
|
|
}
|
|
|
|
|
|
|
|
function animateMultiple(tweens, finish) {
|
|
|
|
var shouldAnimate = settings.getBool("user/general/@animateui");
|
|
|
|
|
|
|
|
if (shouldAnimate) {
|
|
|
|
animating = true;
|
|
|
|
|
|
|
|
var duration = 0, called;
|
|
|
|
tweens.forEach(function(options) {
|
|
|
|
var node = options.node;
|
|
|
|
cssAnimate(node.$ext || node, options, function() {
|
|
|
|
if (options.duration == duration && !called) {
|
|
|
|
called = true;
|
|
|
|
finish && finish();
|
|
|
|
}
|
|
|
|
animating = false;
|
|
|
|
});
|
|
|
|
duration = Math.max(duration, options.duration || 0.2);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//@todo set value
|
|
|
|
|
|
|
|
finish && finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function animate(aNode, options, finish) {
|
|
|
|
var shouldAnimate = settings.getBool("user/general/@animateui");
|
|
|
|
|
|
|
|
if (shouldAnimate) {
|
|
|
|
animating = true;
|
|
|
|
|
|
|
|
cssAnimate(aNode.$ext || aNode, options, function() {
|
|
|
|
finish && finish(); //setTimeout(finish, 30);
|
|
|
|
|
|
|
|
animating = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var htmlNode = aNode.$ext || aNode;
|
|
|
|
for (var prop in options) {
|
|
|
|
if (prop == "duration" || prop == "timingFunction")
|
|
|
|
continue;
|
|
|
|
htmlNode.style[prop] = options[prop];
|
|
|
|
}
|
|
|
|
|
|
|
|
//@todo set value
|
|
|
|
finish && finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function animateSplitBoxNode(aNode, options, finish) {
|
|
|
|
var shouldAnimate = settings.getBool("user/general/@animateui");
|
|
|
|
|
|
|
|
var pNode = aNode.parentNode;
|
|
|
|
if (!pNode.$box) return oNode && animate(oNode, options, finish);
|
|
|
|
|
|
|
|
var firstChild = pNode.getFirstChild();
|
|
|
|
var lastChild = pNode.getSecondChild();
|
|
|
|
var isFirst, oNode = (isFirst = aNode == firstChild) ? lastChild : firstChild;
|
|
|
|
if (oNode == aNode || !oNode.visible)
|
|
|
|
throw new Error("animating object that has no partner");
|
|
|
|
|
|
|
|
var to2 = {
|
|
|
|
timingFunction : options.timingFunction,
|
|
|
|
duration: options.duration
|
|
|
|
};
|
|
|
|
if (pNode.$vbox) {
|
|
|
|
if (isFirst)
|
|
|
|
to2.top = (parseInt(options.height, 10) + pNode.$edge[0] + pNode.padding) + "px";
|
|
|
|
else
|
|
|
|
to2.bottom = (parseInt(options.height, 10) + pNode.$edge[2] + pNode.padding) + "px";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (isFirst)
|
|
|
|
to2.left = (parseInt(options.width, 10) + pNode.$edge[3] + pNode.padding) + "px";
|
|
|
|
else
|
|
|
|
to2.right = (parseInt(options.width, 10) + pNode.$edge[1] + pNode.padding) + "px";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create Event Object
|
|
|
|
var e = new EventEmitter();
|
|
|
|
e.type = "splitbox";
|
|
|
|
e.which = aNode;
|
|
|
|
e.other = oNode;
|
|
|
|
e.options = options;
|
|
|
|
e.options2 = to2;
|
|
|
|
e.duration = options.duration || 0.2;
|
|
|
|
|
|
|
|
// Emit animate event
|
|
|
|
emit("animate", e);
|
|
|
|
|
|
|
|
if (shouldAnimate && !options.immediate) {
|
|
|
|
animating = true;
|
|
|
|
|
|
|
|
options.splitbox = to2.splitbox = true;
|
|
|
|
cssAnimate(aNode.$ext, options);
|
|
|
|
cssAnimate(oNode.$ext, to2, function(){
|
|
|
|
if (aNode.parentNode) {
|
|
|
|
if (pNode.$vbox)
|
|
|
|
aNode.setHeight(parseInt(options.height, 10));
|
|
|
|
else
|
|
|
|
aNode.setWidth(parseInt(options.width, 10));
|
|
|
|
}
|
|
|
|
|
|
|
|
finish && finish();
|
|
|
|
e.emit("finish");
|
|
|
|
|
|
|
|
animating = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var dir;
|
|
|
|
if (pNode.$vbox) {
|
|
|
|
aNode.setHeight(options.height);
|
|
|
|
dir = isFirst ? "top" : "bottom";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
aNode.setWidth(options.width);
|
|
|
|
dir = isFirst ? "left" : "right";
|
|
|
|
}
|
|
|
|
oNode.$ext.style[dir] = to2[dir];
|
|
|
|
|
|
|
|
finish && finish();
|
|
|
|
e.emit("finish");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function emitAnimate(e) {
|
|
|
|
emit("animate", e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***** Register and define API *****/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Animation API for Cloud9. Use this object to animate HTML
|
|
|
|
* elements and APF elements. The implementation uses CSS animations
|
|
|
|
* to animate the HTML elements.
|
|
|
|
*
|
|
|
|
* anims.animate(someDiv, {
|
|
|
|
* width : "200px",
|
|
|
|
* duration : 0.2
|
|
|
|
* }, function(){
|
|
|
|
* console.log("done");
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* @singleton
|
|
|
|
**/
|
|
|
|
plugin.freezePublicAPI({
|
|
|
|
/**
|
|
|
|
* Specifies whether at least one animationg is running at this moment.
|
|
|
|
* @property {Boolean} animating
|
|
|
|
*/
|
|
|
|
get animating(){ return animating; },
|
|
|
|
|
|
|
|
_events: [
|
|
|
|
/**
|
|
|
|
* Fires when an animation starts
|
|
|
|
* @event animate
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.type The animation type. Possible values are "splitbox", and others.
|
|
|
|
* @param {HTMLElement/AMLElement} e.which The element that is animated
|
|
|
|
* @param {HTMLElement/AMLElement} e.other This is only relevant for a splitbox resize, where there is a 2nd element that is animated.
|
|
|
|
* @param {Object} e.options The options passed to the {@link #method-animate} method.
|
|
|
|
* @param {Object} e.options2 This is only relevant for a splitbox resize. There are the options for the 2nd animation.
|
|
|
|
* @param {Number} [e.duration=0.2] The duration of the animation expressed in seconds
|
|
|
|
*/
|
|
|
|
"animate"
|
|
|
|
],
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Animate multiple elements and/or multiple properties at the
|
|
|
|
* same time. Note that each tween object can specify multiple
|
|
|
|
* css properties by adding the names of the css property as a key
|
|
|
|
* and the value as it's value.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* anims.animateMultiple([{
|
|
|
|
* node : someDiv,
|
|
|
|
* width : "200px",
|
|
|
|
* height : "300px"
|
|
|
|
* }, {
|
|
|
|
* node : anotherDiv,
|
|
|
|
* width : "100px",
|
|
|
|
* height : "100px"
|
|
|
|
* }], function(){});
|
|
|
|
*
|
|
|
|
* @param {Array} tweens Array of tween elements.
|
|
|
|
* @param {HTMLElement/AMLElement} tweens.node The element that is animated
|
|
|
|
* @param {Number} [tweens.duration=0.2] The duration of the animation expressed in seconds.
|
|
|
|
* @param {String} [tweens.timingFunction] The [CSS timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/timing-function).
|
|
|
|
* @param {Function} finish Called when all animations have completed.
|
|
|
|
*/
|
|
|
|
animateMultiple: animateMultiple,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Animates a single element and one or more properties.
|
|
|
|
* Note that the tween object can contain multiple
|
|
|
|
* css properties by adding the names of the css property as a key
|
|
|
|
* and the value as it's value.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
2016-02-19 20:24:23 +00:00
|
|
|
* anims.animate(node, {
|
2015-02-10 19:41:24 +00:00
|
|
|
* width : "200px",
|
|
|
|
* height : "300px"
|
|
|
|
* }, function(){});
|
|
|
|
*
|
|
|
|
* @param {HTMLElement/AMLElement} node The element that is animated
|
|
|
|
* @param {Object} tween
|
|
|
|
* @param {Number} [tween.duration=0.2] The duration of the animation expressed in seconds.
|
|
|
|
* @param {String} [tween.timingFunction] The [CSS timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/timing-function).
|
|
|
|
* @param {Function} finish Called when all animations have completed.
|
|
|
|
*/
|
|
|
|
animate: animate,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is dedicated to animating APF Elements that are
|
|
|
|
* part of a splitbox layout element. A splitbox is a box that can
|
|
|
|
* be split in 2 by adding 2 children to it. If you are using a
|
|
|
|
* splitbox in your UI and need to animate a child element, then
|
|
|
|
* use this function.
|
|
|
|
*
|
|
|
|
* Note that the tween object can contain multiple
|
|
|
|
* css properties by adding the names of the css property as a key
|
|
|
|
* and the value as it's value.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* anims.animateSplitBoxNode(panel, {
|
|
|
|
* duration : 0.5,
|
|
|
|
* width : "200px"
|
|
|
|
* }, function(){})
|
|
|
|
*
|
|
|
|
* @param {AMLElement} node The element that is animated
|
|
|
|
* @param {Object} tween
|
|
|
|
* @param {Number} [tween.duration=0.2] The duration of the animation expressed in seconds.
|
|
|
|
* @param {String} [tween.timingFunction] The [CSS timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/timing-function).
|
|
|
|
* @param {Function} finish Called when all animations have completed.
|
|
|
|
*/
|
|
|
|
animateSplitBoxNode: animateSplitBoxNode,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Emits the animate event, forcing a resize amongst editors.
|
|
|
|
* @param {Object} e
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
emitAnimate: emitAnimate
|
|
|
|
});
|
|
|
|
|
|
|
|
register(null, {
|
|
|
|
anims: plugin
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|