/**
* @author Jacky Nguyen <jacky@sencha.com>
* @private
*/
Ext.define('Ext.fx.runner.CssTransition', {
extend: 'Ext.fx.runner.Css',
listenersAttached: false,
constructor: function() {
this.runningAnimationsData = {};
return this.callParent(arguments);
},
attachListeners: function() {
this.listenersAttached = true;
this.getEventDispatcher().addListener('element', '*', 'transitionend', 'onTransitionEnd', this);
},
onTransitionEnd: function(e) {
var target = e.target,
id = target.id;
if (id && this.runningAnimationsData.hasOwnProperty(id)) {
this.refreshRunningAnimationsData(Ext.get(target), [e.browserEvent.propertyName]);
}
},
onAnimationEnd: function(element, data, animation, isInterrupted, isReplaced) {
var id = element.getId(),
runningData = this.runningAnimationsData[id],
endRules = {},
endData = {},
runningNameMap, toPropertyNames, i, ln, name;
animation.un('stop', 'onAnimationStop', this);
if (runningData) {
runningNameMap = runningData.nameMap;
}
endRules[id] = endData;
if (data.onBeforeEnd) {
data.onBeforeEnd.call(data.scope || this, element, isInterrupted);
}
animation.fireEvent('animationbeforeend', animation, element, isInterrupted);
this.fireEvent('animationbeforeend', this, animation, element, isInterrupted);
if (isReplaced || (!isInterrupted && !data.preserveEndState)) {
toPropertyNames = data.toPropertyNames;
for (i = 0,ln = toPropertyNames.length; i < ln; i++) {
name = toPropertyNames[i];
if (runningNameMap && !runningNameMap.hasOwnProperty(name)) {
endData[name] = null;
}
}
}
if (data.after) {
Ext.merge(endData, data.after);
}
this.applyStyles(endRules);
if (data.onEnd) {
data.onEnd.call(data.scope || this, element, isInterrupted);
}
animation.fireEvent('animationend', animation, element, isInterrupted);
this.fireEvent('animationend', this, animation, element, isInterrupted);
},
onAllAnimationsEnd: function(element) {
var id = element.getId(),
endRules = {};
delete this.runningAnimationsData[id];
endRules[id] = {
'transition-property': null,
'transition-duration': null,
'transition-timing-function': null,
'transition-delay': null
};
this.applyStyles(endRules);
this.fireEvent('animationallend', this, element);
},
hasRunningAnimations: function(element) {
var id = element.getId(),
runningAnimationsData = this.runningAnimationsData;
return runningAnimationsData.hasOwnProperty(id) && runningAnimationsData[id].sessions.length > 0;
},
refreshRunningAnimationsData: function(element, propertyNames, interrupt, replace) {
var id = element.getId(),
runningAnimationsData = this.runningAnimationsData,
runningData = runningAnimationsData[id];
if (!runningData) {
return;
}
var nameMap = runningData.nameMap,
nameList = runningData.nameList,
sessions = runningData.sessions,
ln, j, subLn, name,
i, session, map, list,
hasCompletedSession = false;
interrupt = Boolean(interrupt);
replace = Boolean(replace);
if (!sessions) {
return this;
}
ln = sessions.length;
if (ln === 0) {
return this;
}
if (replace) {
runningData.nameMap = {};
nameList.length = 0;
for (i = 0; i < ln; i++) {
session = sessions[i];
this.onAnimationEnd(element, session.data, session.animation, interrupt, replace);
}
sessions.length = 0;
}
else {
for (i = 0; i < ln; i++) {
session = sessions[i];
map = session.map;
list = session.list;
for (j = 0,subLn = propertyNames.length; j < subLn; j++) {
name = propertyNames[j];
if (map[name]) {
delete map[name];
Ext.Array.remove(list, name);
session.length--;
if (--nameMap[name] == 0) {
delete nameMap[name];
Ext.Array.remove(nameList, name);
}
}
}
if (session.length == 0) {
sessions.splice(i, 1);
i--;
ln--;
hasCompletedSession = true;
this.onAnimationEnd(element, session.data, session.animation, interrupt);
}
}
}
if (!replace && !interrupt && sessions.length == 0 && hasCompletedSession) {
this.onAllAnimationsEnd(element);
}
},
getRunningData: function(id) {
var runningAnimationsData = this.runningAnimationsData;
if (!runningAnimationsData.hasOwnProperty(id)) {
runningAnimationsData[id] = {
nameMap: {},
nameList: [],
sessions: []
};
}
return runningAnimationsData[id];
},
getTestElement: function() {
var testElement = this.testElement,
iframe, iframeDocument, iframeStyle;
if (!testElement) {
iframe = document.createElement('iframe');
iframeStyle = iframe.style;
iframeStyle.setProperty('visibility', 'hidden', 'important');
iframeStyle.setProperty('width', '0px', 'important');
iframeStyle.setProperty('height', '0px', 'important');
iframeStyle.setProperty('position', 'absolute', 'important');
iframeStyle.setProperty('border', '0px', 'important');
iframeStyle.setProperty('zIndex', '-1000', 'important');
document.body.appendChild(iframe);
iframeDocument = iframe.contentDocument;
iframeDocument.open();
iframeDocument.writeln('</body>');
iframeDocument.close();
this.testElement = testElement = iframeDocument.createElement('div');
testElement.style.setProperty('position', 'absolute', '!important');
iframeDocument.body.appendChild(testElement);
this.testElementComputedStyle = window.getComputedStyle(testElement);
}
return testElement;
},
getCssStyleValue: function(name, value) {
var testElement = this.getTestElement(),
computedStyle = this.testElementComputedStyle,
style = testElement.style;
style.setProperty(name, value);
value = computedStyle.getPropertyValue(name);
style.removeProperty(name);
return value;
},
run: function(animations) {
var me = this,
isLengthPropertyMap = this.lengthProperties,
fromData = {},
toData = {},
data = {},
element, elementId, from, to, before,
fromPropertyNames, toPropertyNames,
doApplyTo, message,
runningData,
i, j, ln, animation, propertiesLength, sessionNameMap,
computedStyle, formattedName, name, toFormattedValue,
computedValue, fromFormattedValue, isLengthProperty,
runningNameMap, runningNameList, runningSessions, runningSession;
if (!this.listenersAttached) {
this.attachListeners();
}
animations = Ext.Array.from(animations);
for (i = 0,ln = animations.length; i < ln; i++) {
animation = animations[i];
animation = Ext.factory(animation, Ext.fx.Animation);
element = animation.getElement();
computedStyle = window.getComputedStyle(element.dom);
elementId = element.getId();
data = Ext.merge({}, animation.getData());
if (animation.onBeforeStart) {
animation.onBeforeStart.call(animation.scope || this, element);
}
animation.fireEvent('animationstart', animation);
this.fireEvent('animationstart', this, animation);
data[elementId] = data;
before = data.before;
from = data.from;
to = data.to;
data.fromPropertyNames = fromPropertyNames = [];
data.toPropertyNames = toPropertyNames = [];
for (name in to) {
if (to.hasOwnProperty(name)) {
to[name] = toFormattedValue = this.formatValue(to[name], name);
formattedName = this.formatName(name);
isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);
if (!isLengthProperty) {
toFormattedValue = this.getCssStyleValue(formattedName, toFormattedValue);
}
if (from.hasOwnProperty(name)) {
from[name] = fromFormattedValue = this.formatValue(from[name], name);
if (!isLengthProperty) {
fromFormattedValue = this.getCssStyleValue(formattedName, fromFormattedValue);
}
if (toFormattedValue !== fromFormattedValue) {
fromPropertyNames.push(formattedName);
toPropertyNames.push(formattedName);
}
}
else {
computedValue = computedStyle.getPropertyValue(formattedName);
if (toFormattedValue !== computedValue) {
toPropertyNames.push(formattedName);
}
}
}
}
propertiesLength = toPropertyNames.length;
if (propertiesLength === 0) {
this.onAnimationEnd(element, data, animation);
continue;
}
runningData = this.getRunningData(elementId);
runningSessions = runningData.sessions;
if (runningSessions.length > 0) {
this.refreshRunningAnimationsData(
element, Ext.Array.merge(fromPropertyNames, toPropertyNames), true, data.replacePrevious
);
}
runningNameMap = runningData.nameMap;
runningNameList = runningData.nameList;
sessionNameMap = {};
for (j = 0; j < propertiesLength; j++) {
name = toPropertyNames[j];
sessionNameMap[name] = true;
if (!runningNameMap.hasOwnProperty(name)) {
runningNameMap[name] = 1;
runningNameList.push(name);
}
else {
runningNameMap[name]++;
}
}
runningSession = {
element: element,
map: sessionNameMap,
list: toPropertyNames.slice(),
length: propertiesLength,
data: data,
animation: animation
};
runningSessions.push(runningSession);
animation.on('stop', 'onAnimationStop', this);
fromData[elementId] = from = Ext.apply(Ext.Object.chain(before), from);
if (runningNameList.length > 0) {
fromPropertyNames = Ext.Array.difference(runningNameList, fromPropertyNames);
toPropertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);
from['transition-property'] = fromPropertyNames;
}
toData[elementId] = to = Ext.Object.chain(to);
to['transition-property'] = toPropertyNames;
to['transition-duration'] = data.duration;
to['transition-timing-function'] = data.easing;
to['transition-delay'] = data.delay;
animation.startTime = Date.now();
}
message = this.$className;
this.applyStyles(fromData);
doApplyTo = function(e) {
if (e.data === message && e.source === window) {
window.removeEventListener('message', doApplyTo, false);
me.applyStyles(toData);
}
};
window.addEventListener('message', doApplyTo, false);
window.postMessage(message, '*');
},
onAnimationStop: function(animation) {
var runningAnimationsData = this.runningAnimationsData,
id, runningData, sessions, i, ln, session;
for (id in runningAnimationsData) {
if (runningAnimationsData.hasOwnProperty(id)) {
runningData = runningAnimationsData[id];
sessions = runningData.sessions;
for (i = 0,ln = sessions.length; i < ln; i++) {
session = sessions[i];
if (session.animation === animation) {
this.refreshRunningAnimationsData(session.element, session.list.slice(), true);
}
}
}
}
}
});