]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @private\r | |
3 | */\r | |
4 | Ext.define('Ext.fx.runner.CssTransition', {\r | |
5 | extend: 'Ext.fx.runner.Css',\r | |
6 | requires: ['Ext.AnimationQueue'],\r | |
7 | alternateClassName: 'Ext.Animator',\r | |
8 | singleton: true,\r | |
9 | \r | |
10 | listenersAttached: false,\r | |
11 | \r | |
12 | constructor: function() {\r | |
13 | this.runningAnimationsData = {};\r | |
14 | \r | |
15 | return this.callParent(arguments);\r | |
16 | },\r | |
17 | \r | |
18 | attachListeners: function() {\r | |
19 | this.listenersAttached = true;\r | |
20 | \r | |
21 | Ext.getWin().on('transitionend', 'onTransitionEnd', this);\r | |
22 | },\r | |
23 | \r | |
24 | onTransitionEnd: function(e) {\r | |
25 | var target = e.target,\r | |
26 | id = target.id;\r | |
27 | \r | |
28 | if (id && this.runningAnimationsData.hasOwnProperty(id)) {\r | |
29 | this.refreshRunningAnimationsData(Ext.get(target), [e.browserEvent.propertyName]);\r | |
30 | }\r | |
31 | },\r | |
32 | \r | |
33 | onAnimationEnd: function(element, data, animation, isInterrupted, isReplaced) {\r | |
34 | var id = element.getId(),\r | |
35 | runningData = this.runningAnimationsData[id],\r | |
36 | endRules = {},\r | |
37 | endData = {},\r | |
38 | runningNameMap, toPropertyNames, i, ln, name;\r | |
39 | \r | |
40 | animation.un('stop', 'onAnimationStop', this);\r | |
41 | \r | |
42 | if (runningData) {\r | |
43 | runningNameMap = runningData.nameMap;\r | |
44 | }\r | |
45 | \r | |
46 | endRules[id] = endData;\r | |
47 | \r | |
48 | if (data.onBeforeEnd) {\r | |
49 | data.onBeforeEnd.call(data.scope || this, element, isInterrupted);\r | |
50 | }\r | |
51 | \r | |
52 | animation.fireEvent('animationbeforeend', animation, element, isInterrupted);\r | |
53 | this.fireEvent('animationbeforeend', this, animation, element, isInterrupted);\r | |
54 | \r | |
55 | if (isReplaced || (!isInterrupted && !data.preserveEndState)) {\r | |
56 | toPropertyNames = data.toPropertyNames;\r | |
57 | \r | |
58 | for (i = 0,ln = toPropertyNames.length; i < ln; i++) {\r | |
59 | name = toPropertyNames[i];\r | |
60 | \r | |
61 | if (runningNameMap && !runningNameMap.hasOwnProperty(name)) {\r | |
62 | endData[name] = null;\r | |
63 | }\r | |
64 | }\r | |
65 | }\r | |
66 | \r | |
67 | if (data.after) {\r | |
68 | Ext.merge(endData, data.after);\r | |
69 | }\r | |
70 | \r | |
71 | this.applyStyles(endRules);\r | |
72 | \r | |
73 | if (data.onEnd) {\r | |
74 | data.onEnd.call(data.scope || this, element, isInterrupted);\r | |
75 | }\r | |
76 | \r | |
77 | animation.fireEvent('animationend', animation, element, isInterrupted);\r | |
78 | this.fireEvent('animationend', this, animation, element, isInterrupted);\r | |
79 | Ext.AnimationQueue.stop(Ext.emptyFn, animation);\r | |
80 | },\r | |
81 | \r | |
82 | onAllAnimationsEnd: function(element) {\r | |
83 | var id = element.getId(),\r | |
84 | endRules = {};\r | |
85 | \r | |
86 | delete this.runningAnimationsData[id];\r | |
87 | \r | |
88 | endRules[id] = {\r | |
89 | 'transition-property': null,\r | |
90 | 'transition-duration': null,\r | |
91 | 'transition-timing-function': null,\r | |
92 | 'transition-delay': null\r | |
93 | };\r | |
94 | \r | |
95 | this.applyStyles(endRules);\r | |
96 | this.fireEvent('animationallend', this, element);\r | |
97 | },\r | |
98 | \r | |
99 | hasRunningAnimations: function(element) {\r | |
100 | var id = element.getId(),\r | |
101 | runningAnimationsData = this.runningAnimationsData;\r | |
102 | \r | |
103 | return runningAnimationsData.hasOwnProperty(id) && runningAnimationsData[id].sessions.length > 0;\r | |
104 | },\r | |
105 | \r | |
106 | refreshRunningAnimationsData: function(element, propertyNames, interrupt, replace) {\r | |
107 | var id = element.getId(),\r | |
108 | runningAnimationsData = this.runningAnimationsData,\r | |
109 | runningData = runningAnimationsData[id];\r | |
110 | \r | |
111 | if (!runningData) {\r | |
112 | return;\r | |
113 | }\r | |
114 | \r | |
115 | var nameMap = runningData.nameMap,\r | |
116 | nameList = runningData.nameList,\r | |
117 | sessions = runningData.sessions,\r | |
118 | ln, j, subLn, name,\r | |
119 | i, session, map, list,\r | |
120 | hasCompletedSession = false;\r | |
121 | \r | |
122 | interrupt = Boolean(interrupt);\r | |
123 | replace = Boolean(replace);\r | |
124 | \r | |
125 | if (!sessions) {\r | |
126 | return this;\r | |
127 | }\r | |
128 | \r | |
129 | ln = sessions.length;\r | |
130 | \r | |
131 | if (ln === 0) {\r | |
132 | return this;\r | |
133 | }\r | |
134 | \r | |
135 | if (replace) {\r | |
136 | runningData.nameMap = {};\r | |
137 | nameList.length = 0;\r | |
138 | \r | |
139 | for (i = 0; i < ln; i++) {\r | |
140 | session = sessions[i];\r | |
141 | this.onAnimationEnd(element, session.data, session.animation, interrupt, replace);\r | |
142 | }\r | |
143 | \r | |
144 | sessions.length = 0;\r | |
145 | }\r | |
146 | else {\r | |
147 | for (i = 0; i < ln; i++) {\r | |
148 | session = sessions[i];\r | |
149 | map = session.map;\r | |
150 | list = session.list;\r | |
151 | \r | |
152 | for (j = 0,subLn = propertyNames.length; j < subLn; j++) {\r | |
153 | name = propertyNames[j];\r | |
154 | \r | |
155 | if (map[name]) {\r | |
156 | delete map[name];\r | |
157 | Ext.Array.remove(list, name);\r | |
158 | session.length--;\r | |
159 | if (--nameMap[name] == 0) {\r | |
160 | delete nameMap[name];\r | |
161 | Ext.Array.remove(nameList, name);\r | |
162 | }\r | |
163 | }\r | |
164 | }\r | |
165 | \r | |
166 | if (session.length == 0) {\r | |
167 | sessions.splice(i, 1);\r | |
168 | i--;\r | |
169 | ln--;\r | |
170 | \r | |
171 | hasCompletedSession = true;\r | |
172 | this.onAnimationEnd(element, session.data, session.animation, interrupt);\r | |
173 | }\r | |
174 | }\r | |
175 | }\r | |
176 | \r | |
177 | if (!replace && !interrupt && sessions.length == 0 && hasCompletedSession) {\r | |
178 | this.onAllAnimationsEnd(element);\r | |
179 | }\r | |
180 | },\r | |
181 | \r | |
182 | getRunningData: function(id) {\r | |
183 | var runningAnimationsData = this.runningAnimationsData;\r | |
184 | \r | |
185 | if (!runningAnimationsData.hasOwnProperty(id)) {\r | |
186 | runningAnimationsData[id] = {\r | |
187 | nameMap: {},\r | |
188 | nameList: [],\r | |
189 | sessions: []\r | |
190 | };\r | |
191 | }\r | |
192 | \r | |
193 | return runningAnimationsData[id];\r | |
194 | },\r | |
195 | \r | |
196 | getTestElement: function() {\r | |
197 | var testElement = this.testElement,\r | |
198 | iframe, iframeDocument, iframeStyle;\r | |
199 | \r | |
200 | if (!testElement) {\r | |
201 | iframe = document.createElement('iframe');\r | |
202 | //<debug>\r | |
203 | // Set an attribute that tells the test runner to ignore this node when checking\r | |
204 | // for dom cleanup\r | |
205 | iframe.setAttribute('data-sticky', true);\r | |
206 | //</debug>\r | |
207 | iframe.setAttribute('tabIndex', -1);\r | |
208 | iframeStyle = iframe.style;\r | |
209 | iframeStyle.setProperty('visibility', 'hidden', 'important');\r | |
210 | iframeStyle.setProperty('width', '0px', 'important');\r | |
211 | iframeStyle.setProperty('height', '0px', 'important');\r | |
212 | iframeStyle.setProperty('position', 'absolute', 'important');\r | |
213 | iframeStyle.setProperty('border', '0px', 'important');\r | |
214 | iframeStyle.setProperty('zIndex', '-1000', 'important');\r | |
215 | \r | |
216 | document.body.appendChild(iframe);\r | |
217 | iframeDocument = iframe.contentDocument;\r | |
218 | \r | |
219 | iframeDocument.open();\r | |
220 | iframeDocument.writeln('</body>');\r | |
221 | iframeDocument.close();\r | |
222 | \r | |
223 | this.testElement = testElement = iframeDocument.createElement('div');\r | |
224 | testElement.style.setProperty('position', 'absolute', 'important');\r | |
225 | iframeDocument.body.appendChild(testElement);\r | |
226 | this.testElementComputedStyle = window.getComputedStyle(testElement);\r | |
227 | }\r | |
228 | \r | |
229 | return testElement;\r | |
230 | },\r | |
231 | \r | |
232 | getCssStyleValue: function(name, value) {\r | |
233 | var testElement = this.getTestElement(),\r | |
234 | computedStyle = this.testElementComputedStyle,\r | |
235 | style = testElement.style;\r | |
236 | \r | |
237 | style.setProperty(name, value);\r | |
238 | \r | |
239 | if (Ext.browser.is.Firefox) {\r | |
240 | // We force a repaint of the element in Firefox to make sure the computedStyle to be updated\r | |
241 | testElement.offsetHeight;\r | |
242 | }\r | |
243 | \r | |
244 | value = computedStyle.getPropertyValue(name);\r | |
245 | style.removeProperty(name);\r | |
246 | \r | |
247 | return value;\r | |
248 | },\r | |
249 | \r | |
250 | run: function(animations) {\r | |
251 | var me = this,\r | |
252 | isLengthPropertyMap = me.lengthProperties,\r | |
253 | fromData = {},\r | |
254 | toData = {},\r | |
255 | data = {},\r | |
256 | element, elementId, from, to, before,\r | |
257 | fromPropertyNames, toPropertyNames,\r | |
258 | doApplyTo, message,\r | |
259 | runningData, elementData,\r | |
260 | i, j, ln, animation, propertiesLength, sessionNameMap,\r | |
261 | computedStyle, formattedName, name, toFormattedValue,\r | |
262 | computedValue, fromFormattedValue, isLengthProperty,\r | |
263 | runningNameMap, runningNameList, runningSessions, runningSession;\r | |
264 | \r | |
265 | if (!me.listenersAttached) {\r | |
266 | me.attachListeners();\r | |
267 | }\r | |
268 | \r | |
269 | animations = Ext.Array.from(animations);\r | |
270 | \r | |
271 | for (i = 0,ln = animations.length; i < ln; i++) {\r | |
272 | animation = animations[i];\r | |
273 | animation = Ext.factory(animation, Ext.fx.Animation);\r | |
274 | element = animation.getElement();\r | |
275 | \r | |
276 | // Empty function to prevent idleTasks from running while we animate.\r | |
277 | Ext.AnimationQueue.start(Ext.emptyFn, animation);\r | |
278 | \r | |
279 | computedStyle = window.getComputedStyle(element.dom);\r | |
280 | \r | |
281 | elementId = element.getId();\r | |
282 | \r | |
283 | data = Ext.merge({}, animation.getData());\r | |
284 | \r | |
285 | if (animation.onBeforeStart) {\r | |
286 | animation.onBeforeStart.call(animation.scope || me, element);\r | |
287 | }\r | |
288 | animation.fireEvent('animationstart', animation);\r | |
289 | me.fireEvent('animationstart', me, animation);\r | |
290 | \r | |
291 | data[elementId] = data;\r | |
292 | \r | |
293 | before = data.before;\r | |
294 | from = data.from;\r | |
295 | to = data.to;\r | |
296 | \r | |
297 | data.fromPropertyNames = fromPropertyNames = [];\r | |
298 | data.toPropertyNames = toPropertyNames = [];\r | |
299 | \r | |
300 | for (name in to) {\r | |
301 | if (to.hasOwnProperty(name)) {\r | |
302 | to[name] = toFormattedValue = me.formatValue(to[name], name);\r | |
303 | formattedName = me.formatName(name);\r | |
304 | isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);\r | |
305 | \r | |
306 | if (!isLengthProperty) {\r | |
307 | toFormattedValue = me.getCssStyleValue(formattedName, toFormattedValue);\r | |
308 | }\r | |
309 | \r | |
310 | if (from.hasOwnProperty(name)) {\r | |
311 | from[name] = fromFormattedValue = me.formatValue(from[name], name);\r | |
312 | \r | |
313 | if (!isLengthProperty) {\r | |
314 | fromFormattedValue = me.getCssStyleValue(formattedName, fromFormattedValue);\r | |
315 | }\r | |
316 | \r | |
317 | if (toFormattedValue !== fromFormattedValue) {\r | |
318 | fromPropertyNames.push(formattedName);\r | |
319 | toPropertyNames.push(formattedName);\r | |
320 | }\r | |
321 | }\r | |
322 | else {\r | |
323 | computedValue = computedStyle.getPropertyValue(formattedName);\r | |
324 | \r | |
325 | if (toFormattedValue !== computedValue) {\r | |
326 | toPropertyNames.push(formattedName);\r | |
327 | }\r | |
328 | }\r | |
329 | }\r | |
330 | }\r | |
331 | \r | |
332 | propertiesLength = toPropertyNames.length;\r | |
333 | \r | |
334 | if (propertiesLength === 0) {\r | |
335 | me.onAnimationEnd(element, data, animation);\r | |
336 | continue;\r | |
337 | }\r | |
338 | \r | |
339 | runningData = me.getRunningData(elementId);\r | |
340 | runningSessions = runningData.sessions;\r | |
341 | \r | |
342 | if (runningSessions.length > 0) {\r | |
343 | me.refreshRunningAnimationsData(\r | |
344 | element, Ext.Array.merge(fromPropertyNames, toPropertyNames), true, data.replacePrevious\r | |
345 | );\r | |
346 | }\r | |
347 | \r | |
348 | runningNameMap = runningData.nameMap;\r | |
349 | runningNameList = runningData.nameList;\r | |
350 | \r | |
351 | sessionNameMap = {};\r | |
352 | for (j = 0; j < propertiesLength; j++) {\r | |
353 | name = toPropertyNames[j];\r | |
354 | sessionNameMap[name] = true;\r | |
355 | \r | |
356 | if (!runningNameMap.hasOwnProperty(name)) {\r | |
357 | runningNameMap[name] = 1;\r | |
358 | runningNameList.push(name);\r | |
359 | }\r | |
360 | else {\r | |
361 | runningNameMap[name]++;\r | |
362 | }\r | |
363 | }\r | |
364 | \r | |
365 | runningSession = {\r | |
366 | element: element,\r | |
367 | map: sessionNameMap,\r | |
368 | list: toPropertyNames.slice(),\r | |
369 | length: propertiesLength,\r | |
370 | data: data,\r | |
371 | animation: animation\r | |
372 | };\r | |
373 | runningSessions.push(runningSession);\r | |
374 | \r | |
375 | animation.on('stop', 'onAnimationStop', me);\r | |
376 | \r | |
377 | elementData = Ext.apply({}, before);\r | |
378 | Ext.apply(elementData, from);\r | |
379 | \r | |
380 | if (runningNameList.length > 0) {\r | |
381 | fromPropertyNames = Ext.Array.difference(runningNameList, fromPropertyNames);\r | |
382 | toPropertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);\r | |
383 | elementData['transition-property'] = fromPropertyNames;\r | |
384 | }\r | |
385 | \r | |
386 | fromData[elementId] = elementData;\r | |
387 | toData[elementId] = Ext.apply({}, to);\r | |
388 | \r | |
389 | toData[elementId]['transition-property'] = toPropertyNames;\r | |
390 | toData[elementId]['transition-duration'] = data.duration;\r | |
391 | toData[elementId]['transition-timing-function'] = data.easing;\r | |
392 | toData[elementId]['transition-delay'] = data.delay;\r | |
393 | \r | |
394 | animation.startTime = Date.now();\r | |
395 | }\r | |
396 | \r | |
397 | message = me.$className;\r | |
398 | \r | |
399 | me.applyStyles(fromData);\r | |
400 | \r | |
401 | doApplyTo = function(e) {\r | |
402 | if (e.data === message && e.source === window) {\r | |
403 | window.removeEventListener('message', doApplyTo, false);\r | |
404 | me.applyStyles(toData);\r | |
405 | }\r | |
406 | };\r | |
407 | \r | |
408 | if (window.requestAnimationFrame) {\r | |
409 | window.requestAnimationFrame(function() {\r | |
410 | window.addEventListener('message', doApplyTo, false);\r | |
411 | window.postMessage(message, '*');\r | |
412 | });\r | |
413 | }else {\r | |
414 | Ext.defer(function() {\r | |
415 | window.addEventListener('message', doApplyTo, false);\r | |
416 | window.postMessage(message, '*');\r | |
417 | }, 1)\r | |
418 | }\r | |
419 | },\r | |
420 | \r | |
421 | onAnimationStop: function(animation) {\r | |
422 | var runningAnimationsData = this.runningAnimationsData,\r | |
423 | id, runningData, sessions, i, ln, session;\r | |
424 | \r | |
425 | for (id in runningAnimationsData) {\r | |
426 | if (runningAnimationsData.hasOwnProperty(id)) {\r | |
427 | runningData = runningAnimationsData[id];\r | |
428 | sessions = runningData.sessions;\r | |
429 | \r | |
430 | for (i = 0,ln = sessions.length; i < ln; i++) {\r | |
431 | session = sessions[i];\r | |
432 | if (session.animation === animation) {\r | |
433 | this.refreshRunningAnimationsData(session.element, session.list.slice(), false);\r | |
434 | }\r | |
435 | }\r | |
436 | }\r | |
437 | }\r | |
438 | }\r | |
439 | });\r |