Branch data Line data Source code
1 : 1 : /* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil; -*- */
2 : : /* eslint-disable block-scoped-var, eqeqeq, no-shadow, prefer-rest-params */
3 : : // SPDX-License-Identifier: MIT
4 : : // SPDX-FileCopyrightText: 2006-2007 Zeh Fernando and Nate Chatellier
5 : : // SPDX-FileCopyrightText: 2008 litl, LLC.
6 : :
7 : : /**
8 : : * Tweener
9 : : * Transition controller for movieclips, sounds, textfields and other objects
10 : : *
11 : : * @author Zeh Fernando, Nate Chatellier, Arthur Debert
12 : : * @version 1.31.71
13 : : */
14 : : /* exported addCaller, addTween, FrameTicker, getTweenCount, getTimeScale,
15 : : pauseAllTweens, pauseTweens, PropertyList, registerSpecialProperty,
16 : : registerSpecialPropertyModifier, registerSpecialPropertySplitter,
17 : : removeAllTweens, removeTweens, restrictedWords, resumeAllTweens, resumeTweens,
18 : : setFrameTicker, setTimeScale */
19 : :
20 : : /*
21 : : http://code.google.com/p/tweener/
22 : : http://code.google.com/p/tweener/wiki/License
23 : : */
24 : :
25 : 1 : const GLib = imports.gi.GLib;
26 : :
27 : 1 : const TweenList = imports.tweener.tweenList;
28 : 1 : const Signals = imports.signals;
29 : :
30 : 1 : var _inited = false;
31 : 1 : var _engineExists = false;
32 : 1 : var _tweenList = null;
33 : :
34 : 1 : var _timeScale = 1;
35 : :
36 : 1 : var _specialPropertyList = [];
37 : 1 : var _specialPropertyModifierList = [];
38 : 1 : var _specialPropertySplitterList = [];
39 : :
40 : : /*
41 : : * Ticker should implement:
42 : : *
43 : : * property FRAME_RATE
44 : : * start()
45 : : * stop()
46 : : * getTime() gets time in milliseconds from start()
47 : : * signal prepare-frame
48 : : *
49 : : */
50 : 1 : var _ticker = null;
51 : :
52 : 1 : var _prepareFrameId = 0;
53 : :
54 : : /* default frame ticker */
55 : 1 : function FrameTicker() {
56 : 1 : this._init();
57 : : }
58 : :
59 : 1 : FrameTicker.prototype = {
60 : 1 : FRAME_RATE: 65,
61 : :
62 : 1 : _init() {
63 : : },
64 : :
65 : 1 : start() {
66 : 0 : this._currentTime = 0;
67 : :
68 : 0 : let me = this;
69 : :
70 : 0 : this._timeoutID = GLib.timeout_add(
71 : 0 : GLib.PRIORITY_DEFAULT,
72 : 0 : Math.floor(1000 / me.FRAME_RATE),
73 : 0 : function () {
74 : 0 : me._currentTime += 1000 / me.FRAME_RATE;
75 : 0 : me.emit('prepare-frame');
76 : 0 : return true;
77 : : });
78 : : },
79 : :
80 : 1 : stop() {
81 [ # # ]: 0 : if ('_timeoutID' in this) {
82 : 0 : GLib.source_remove(this._timeoutID);
83 : 0 : delete this._timeoutID;
84 : : }
85 : :
86 : 0 : this._currentTime = 0;
87 : : },
88 : :
89 : 1 : getTime() {
90 : 0 : return this._currentTime;
91 : : },
92 : : };
93 : 1 : Signals.addSignalMethods(FrameTicker.prototype);
94 : :
95 : 1 : _ticker = new FrameTicker();
96 : :
97 : : /* TODOs:
98 : : *
99 : : * Special properties:
100 : : *
101 : : * Special properties are 'proxy' properties used in Tweener to tween
102 : : * (animate) things that are not proper properties per se. One example
103 : : * given is the 'frame' of an object in ActionScript, which is not an
104 : : * object property. Using the special property '_frame' you could animate
105 : : * it like this:
106 : : *
107 : : * Tweener.addTween(myMovieClip, {_frame:20, time:1});
108 : : *
109 : : * which would be equivalent to applying a fast-forward to it.
110 : : *
111 : : * This properties need a special support in the code, and I've removed it
112 : : * for now until we see the need for it in our clutter based stuff.
113 : : */
114 : :
115 : : /* This is a bit pointless now, but let's keep it anyway... */
116 : 52 : function _init() {
117 [ + - ]: 1 : if (_inited)
118 : 0 : return;
119 : :
120 : 1 : _inited = true;
121 : : }
122 : :
123 : 1 : function setFrameTicker(ticker) {
124 : 1 : _ticker = ticker;
125 : : }
126 : :
127 : 2 : function _startEngine() {
128 [ + - ]: 2 : if (_engineExists)
129 : 0 : return;
130 : :
131 : 2 : _engineExists = true;
132 : 2 : _tweenList = [];
133 : :
134 [ + - ]: 2 : if (!_ticker)
135 : 0 : throw new Error('Must call setFrameTicker()');
136 : :
137 : 4 : _prepareFrameId = _ticker.connect('prepare-frame',
138 : 2 : _onEnterFrame);
139 : 2 : _ticker.start();
140 : : }
141 : :
142 : 1 : function _stopEngine() {
143 [ + - ]: 1 : if (!_engineExists)
144 : 0 : return;
145 : :
146 : 1 : _engineExists = false;
147 : 1 : _tweenList = false;
148 : :
149 : 1 : _ticker.disconnect(_prepareFrameId);
150 : 1 : _prepareFrameId = 0;
151 : 1 : _ticker.stop();
152 : : }
153 : :
154 : 471 : function _getCurrentTweeningTime() {
155 : 471 : return _ticker.getTime();
156 : : }
157 : :
158 : 73 : function _removeTweenByIndex(i) {
159 : 73 : _tweenList[i] = null;
160 : :
161 : 73 : var finalRemoval = arguments[1];
162 : :
163 [ + + ][ + + ]: 73 : if (finalRemoval != undefined && finalRemoval)
164 : 32 : _tweenList.splice(i, 1);
165 : :
166 : 73 : return true;
167 : : }
168 : :
169 : 4 : function _resumeTweenByIndex(i) {
170 : 4 : var tweening = _tweenList[i];
171 : :
172 [ - + ][ + - ]: 4 : if (tweening == null || !tweening.isPaused)
173 : 0 : return false;
174 : :
175 : 4 : var currentTime = _getCurrentTweeningTime(tweening);
176 : :
177 : 4 : tweening.timeStart += currentTime - tweening.timePaused;
178 : 4 : tweening.timeComplete += currentTime - tweening.timePaused;
179 : 4 : tweening.timePaused = undefined;
180 : 4 : tweening.isPaused = false;
181 : :
182 : 4 : return true;
183 : : }
184 : :
185 : : /* FIXME: any way to get the function name from the fn itself? */
186 : 466 : function _callOnFunction(fn, fnname, scope, fallbackScope, params) {
187 [ + + ]: 466 : if (fn) {
188 [ + - ]: 75 : var eventScope = scope ? scope : fallbackScope;
189 : 75 : try {
190 : 75 : fn.apply(eventScope, params);
191 : 0 : } catch (e) {
192 : 0 : logError(e, `Error calling ${fnname}`);
193 : : }
194 : : }
195 : : }
196 : :
197 : 472 : function _updateTweenByIndex(i) {
198 : 472 : var tweening = _tweenList[i];
199 : :
200 [ + + ][ + + ]: 472 : if (tweening == null || !tweening.scope)
201 : 9 : return false;
202 : :
203 : 463 : var currentTime = _getCurrentTweeningTime(tweening);
204 : :
205 [ + + ]: 463 : if (currentTime < tweening.timeStart)
206 : 52 : return true; // Hasn't started, so return true
207 : :
208 : 411 : var scope = tweening.scope;
209 : : var t, b, c, d, nv;
210 : :
211 : 411 : var isOver = false;
212 : :
213 [ + + ]: 411 : if (tweening.isCaller) {
214 : 5 : do {
215 : 14 : t = (tweening.timeComplete - tweening.timeStart) / tweening.count *
216 : 14 : (tweening.timesCalled + 1);
217 : 14 : b = tweening.timeStart;
218 : 14 : c = tweening.timeComplete - tweening.timeStart;
219 : 14 : d = tweening.timeComplete - tweening.timeStart;
220 : 14 : nv = tweening.transition(t, b, c, d);
221 : :
222 [ + + ]: 14 : if (currentTime >= nv) {
223 : 20 : _callOnFunction(tweening.onUpdate, 'onUpdate', tweening.onUpdateScope,
224 : 10 : scope, tweening.onUpdateParams);
225 : :
226 : 10 : tweening.timesCalled++;
227 [ + + ]: 10 : if (tweening.timesCalled >= tweening.count) {
228 : 1 : isOver = true;
229 : : break;
230 : : }
231 : :
232 [ + - ]: 9 : if (tweening.waitFrames)
233 : : break;
234 : : }
235 [ + + ]: 13 : } while (currentTime >= nv);
236 : : } else {
237 : : var mustUpdate, name;
238 : :
239 [ + + ]: 406 : if (currentTime >= tweening.timeComplete) {
240 : 22 : isOver = true;
241 : 22 : mustUpdate = true;
242 : : } else {
243 [ - + ]: 384 : mustUpdate = tweening.skipUpdates < 1 ||
244 [ + - ]: 384 : !tweening.skipUpdates ||
245 : 0 : tweening.updatesSkipped >= tweening.skipUpdates;
246 : : }
247 : :
248 [ + + ]: 406 : if (!tweening.hasStarted) {
249 : 48 : _callOnFunction(tweening.onStart, 'onStart', tweening.onStartScope,
250 : 24 : scope, tweening.onStartParams);
251 : :
252 [ + + ]: 59 : for (name in tweening.properties) {
253 : : var pv;
254 : :
255 [ + + ]: 35 : if (tweening.properties[name].isSpecialProperty) {
256 : : // It's a special property, tunnel via the special property function
257 [ + - ]: 1 : if (_specialPropertyList[name].preProcess != undefined)
258 : 0 : tweening.properties[name].valueComplete = _specialPropertyList[name].preProcess(scope, _specialPropertyList[name].parameters, tweening.properties[name].originalValueComplete, tweening.properties[name].extra);
259 : 1 : pv = _specialPropertyList[name].getValue(scope, _specialPropertyList[name].parameters, tweening.properties[name].extra);
260 : : } else {
261 : : // Directly read property
262 : 34 : pv = scope[name];
263 : : }
264 [ + - ]: 35 : tweening.properties[name].valueStart = isNaN(pv) ? tweening.properties[name].valueComplete : pv;
265 : : }
266 : :
267 : 24 : mustUpdate = true;
268 : 24 : tweening.hasStarted = true;
269 : : }
270 : :
271 [ - + ]: 406 : if (mustUpdate) {
272 [ + + ]: 1166 : for (name in tweening.properties) {
273 : 760 : var property = tweening.properties[name];
274 : :
275 [ + + ]: 760 : if (isOver) {
276 : : // Tweening time has finished, just set it to the final value
277 : 33 : nv = property.valueComplete;
278 [ + + ]: 727 : } else if (property.hasModifier) {
279 : : // Modified
280 : 49 : t = currentTime - tweening.timeStart;
281 : 49 : d = tweening.timeComplete - tweening.timeStart;
282 : 49 : nv = tweening.transition(t, 0, 1, d, tweening.transitionParams);
283 : 49 : nv = property.modifierFunction(property.valueStart, property.valueComplete, nv, property.modifierParameters);
284 : : } else {
285 : : // Normal update
286 : 678 : t = currentTime - tweening.timeStart;
287 : 678 : b = property.valueStart;
288 : 678 : c = property.valueComplete - property.valueStart;
289 : 678 : d = tweening.timeComplete - tweening.timeStart;
290 : 678 : nv = tweening.transition(t, b, c, d, tweening.transitionParams);
291 : : }
292 : :
293 [ + - ]: 760 : if (tweening.rounded)
294 : 0 : nv = Math.round(nv);
295 : :
296 [ + + ][ + + ]: 760 : if (tweening.min !== undefined && nv < tweening.min)
297 : 50 : nv = tweening.min;
298 [ + + ][ + + ]: 760 : if (tweening.max !== undefined && nv > tweening.max)
299 : 16 : nv = tweening.max;
300 : :
301 [ + + ]: 760 : if (property.isSpecialProperty) {
302 : : // It's a special property, tunnel via the special property method
303 : 50 : _specialPropertyList[name].setValue(scope, nv, _specialPropertyList[name].parameters, tweening.properties[name].extra);
304 : : } else {
305 : : // Directly set property
306 : 710 : scope[name] = nv;
307 : : }
308 : : }
309 : :
310 : 406 : tweening.updatesSkipped = 0;
311 : :
312 : 812 : _callOnFunction(tweening.onUpdate, 'onUpdate', tweening.onUpdateScope,
313 : 406 : scope, tweening.onUpdateParams);
314 : : } else {
315 : 0 : tweening.updatesSkipped++;
316 : : }
317 : : }
318 : :
319 [ + + ]: 411 : if (isOver) {
320 : 46 : _callOnFunction(tweening.onComplete, 'onComplete', tweening.onCompleteScope,
321 : 23 : scope, tweening.onCompleteParams);
322 : : }
323 : :
324 : 411 : return !isOver;
325 : : }
326 : :
327 : 308 : function _updateTweens() {
328 [ + + ]: 308 : if (_tweenList.length == 0)
329 : 1 : return false;
330 : :
331 [ + + ]: 803 : for (let i = 0; i < _tweenList.length; i++) {
332 [ + + ][ + + ]: 496 : if (_tweenList[i] == undefined || !_tweenList[i].isPaused) {
333 [ + + ]: 471 : if (!_updateTweenByIndex(i))
334 : 31 : _removeTweenByIndex(i);
335 : :
336 [ + + ]: 471 : if (_tweenList[i] == null) {
337 : 32 : _removeTweenByIndex(i, true);
338 : 32 : i--;
339 : : }
340 : : }
341 : : }
342 : :
343 : 307 : return true;
344 : : }
345 : :
346 : : /* Ran once every 'frame'. It's the main engine, updates all existing tweenings */
347 : 308 : function _onEnterFrame() {
348 [ + + ]: 308 : if (!_updateTweens())
349 : 1 : _stopEngine();
350 : :
351 : 308 : return true;
352 : : }
353 : :
354 : 1 : var restrictedWords = {
355 : 1 : time: true,
356 : 1 : delay: true,
357 : 1 : userFrames: true,
358 : 1 : skipUpdates: true,
359 : 1 : transition: true,
360 : 1 : transitionParams: true,
361 : 1 : onStart: true,
362 : 1 : onUpdate: true,
363 : 1 : onComplete: true,
364 : 1 : onOverwrite: true,
365 : 1 : onError: true,
366 : 1 : rounded: true,
367 : 1 : min: true,
368 : 1 : max: true,
369 : 1 : onStartParams: true,
370 : 1 : onUpdateParams: true,
371 : 1 : onCompleteParams: true,
372 : 1 : onOverwriteParams: true,
373 : 1 : onStartScope: true,
374 : 1 : onUpdateScope: true,
375 : 1 : onCompleteScope: true,
376 : 1 : onOverwriteScope: true,
377 : 1 : onErrorScope: true,
378 : : };
379 : :
380 : 32 : function _constructPropertyList(obj) {
381 : 32 : var properties = {};
382 : 32 : var modifiedProperties = {};
383 : :
384 [ + + ]: 144 : for (let istr in obj) {
385 [ + + ]: 112 : if (restrictedWords[istr])
386 : : continue;
387 : :
388 [ + + ]: 50 : if (_specialPropertySplitterList[istr] != undefined) {
389 : : // Special property splitter
390 : 1 : var splitProperties = _specialPropertySplitterList[istr].splitValues(obj[istr], _specialPropertySplitterList[istr].parameters);
391 [ + + ]: 3 : for (let i = 0; i < splitProperties.length; i++) {
392 [ + - ]: 2 : if (_specialPropertySplitterList[splitProperties[i].name] != undefined) {
393 : 0 : var splitProperties2 = _specialPropertySplitterList[splitProperties[i].name].splitValues(splitProperties[i].value, _specialPropertySplitterList[splitProperties[i].name].parameters);
394 [ # # ]: 0 : for (let j = 0; j < splitProperties2.length; j++) {
395 : 0 : properties[splitProperties2[j].name] = {
396 : 0 : valueStart: undefined,
397 : 0 : valueComplete: splitProperties2[j].value,
398 : 0 : arrayIndex: splitProperties2[j].arrayIndex,
399 : 0 : isSpecialProperty: false,
400 : : };
401 : : }
402 : : } else {
403 : 2 : properties[splitProperties[i].name] = {
404 : 2 : valueStart: undefined,
405 : 2 : valueComplete: splitProperties[i].value,
406 : 2 : arrayIndex: splitProperties[i].arrayIndex,
407 : 2 : isSpecialProperty: false,
408 : : };
409 : : }
410 : : }
411 [ + + ]: 49 : } else if (_specialPropertyModifierList[istr] != undefined) {
412 : : // Special property modifier
413 : 1 : let tempModifiedProperties = _specialPropertyModifierList[istr].modifyValues(obj[istr]);
414 [ + + ]: 2 : for (let i = 0; i < tempModifiedProperties.length; i++) {
415 : 1 : modifiedProperties[tempModifiedProperties[i].name] = {
416 : 1 : modifierParameters: tempModifiedProperties[i].parameters,
417 : 1 : modifierFunction: _specialPropertyModifierList[istr].getValue,
418 : : };
419 : : }
420 : : } else {
421 : 48 : properties[istr] = {
422 : 48 : valueStart: undefined,
423 : 48 : valueComplete: obj[istr],
424 : : };
425 : : }
426 : : }
427 : :
428 : : // Adds the modifiers to the list of properties
429 [ + + ]: 33 : for (let istr in modifiedProperties) {
430 [ - + ]: 1 : if (properties[istr]) {
431 : 1 : properties[istr].modifierParameters = modifiedProperties[istr].modifierParameters;
432 : 1 : properties[istr].modifierFunction = modifiedProperties[istr].modifierFunction;
433 : : }
434 : : }
435 : :
436 : 32 : return properties;
437 : : }
438 : :
439 : 50 : function PropertyInfo(valueStart, valueComplete, originalValueComplete,
440 : : arrayIndex, extra, isSpecialProperty,
441 : : modifierFunction, modifierParameters) {
442 : 100 : this._init(valueStart, valueComplete, originalValueComplete,
443 : 50 : arrayIndex, extra, isSpecialProperty,
444 : 50 : modifierFunction, modifierParameters);
445 : : }
446 : :
447 : 1 : PropertyInfo.prototype = {
448 : 1 : _init(valueStart, valueComplete, originalValueComplete,
449 : : arrayIndex, extra, isSpecialProperty,
450 : : modifierFunction, modifierParameters) {
451 : 50 : this.valueStart = valueStart;
452 : 50 : this.valueComplete = valueComplete;
453 : 50 : this.originalValueComplete = originalValueComplete;
454 : 50 : this.arrayIndex = arrayIndex;
455 : 50 : this.extra = extra;
456 : 50 : this.isSpecialProperty = isSpecialProperty;
457 : 50 : this.hasModifier = Boolean(modifierFunction);
458 : 50 : this.modifierFunction = modifierFunction;
459 : 50 : this.modifierParameters = modifierParameters;
460 : : },
461 : : };
462 : :
463 : 33 : function _addTweenOrCaller(target, tweeningParameters, isCaller) {
464 [ + - ]: 33 : if (!target)
465 : 0 : return false;
466 : :
467 : : var scopes; // List of objects to tween
468 [ + - ]: 33 : if (Array.isArray(target)) {
469 : : // The first argument is an array
470 : 0 : scopes = target.concat(); // XXX: To copy the array I guess
471 : : } else {
472 : : // The first argument(s) is(are) object(s)
473 : 33 : scopes = new Array(target);
474 : : }
475 : :
476 : : var obj, istr;
477 : :
478 [ + + ]: 33 : if (isCaller) {
479 : 1 : obj = tweeningParameters;
480 : : } else {
481 : 32 : obj = TweenList.makePropertiesChain(tweeningParameters);
482 : :
483 : 32 : var properties = _constructPropertyList(obj);
484 : :
485 : : // Verifies whether the properties exist or not, for warning messages
486 [ + + ]: 82 : for (istr in properties) {
487 [ + + ]: 50 : if (_specialPropertyList[istr] != undefined) {
488 : 1 : properties[istr].isSpecialProperty = true;
489 : : } else {
490 [ + + ]: 98 : for (var i = 0; i < scopes.length; i++) {
491 [ + - ]: 49 : if (scopes[i][istr] == undefined) {
492 : 0 : log(`The property ${istr} doesn't seem to be a ` +
493 : 0 : `normal object property of ${scopes[i]} or a ` +
494 : 0 : 'registered special property');
495 : : }
496 : : }
497 : : }
498 : : }
499 : : }
500 : :
501 : : // Creates the main engine if it isn't active
502 [ + + ]: 33 : if (!_inited)
503 : 1 : _init();
504 [ + + ]: 33 : if (!_engineExists)
505 : 2 : _startEngine();
506 : :
507 : : // Creates a "safer", more strict tweening object
508 [ + + ]: 33 : var time = obj.time || 0;
509 [ + + ]: 33 : var delay = obj.delay || 0;
510 : :
511 : : var transition;
512 : :
513 : : // FIXME: Tweener allows you to use functions with an all lower-case name
514 [ + + ]: 33 : if (typeof obj.transition == 'string')
515 : 7 : transition = imports.tweener.equations[obj.transition];
516 : : else
517 : 26 : transition = obj.transition;
518 : :
519 [ + + ]: 33 : if (!transition)
520 : 26 : transition = imports.tweener.equations['easeOutExpo'];
521 : :
522 : : var tween;
523 : :
524 [ + + ]: 66 : for (let i = 0; i < scopes.length; i++) {
525 [ + + ]: 33 : if (!isCaller) {
526 : : // Make a copy of the properties
527 : 32 : var copyProperties = {};
528 [ + + ]: 82 : for (istr in properties) {
529 : 100 : copyProperties[istr] = new PropertyInfo(properties[istr].valueStart,
530 : 50 : properties[istr].valueComplete,
531 : 50 : properties[istr].valueComplete,
532 [ - + ]: 50 : properties[istr].arrayIndex || 0,
533 : 50 : {},
534 [ + + ]: 50 : properties[istr].isSpecialProperty || false,
535 [ + + ]: 50 : properties[istr].modifierFunction || null,
536 [ - + ]: 50 : properties[istr].modifierParameters || null);
537 : : }
538 : : }
539 : :
540 : 66 : tween = new TweenList.TweenList(scopes[i],
541 : 33 : _ticker.getTime() + delay * 1000 / _timeScale,
542 : 33 : _ticker.getTime() + (delay * 1000 + time * 1000) / _timeScale,
543 : 33 : false,
544 : 33 : transition,
545 [ - + ]: 33 : obj.transitionParams || null);
546 : :
547 [ + + ]: 33 : tween.properties = isCaller ? null : copyProperties;
548 : 33 : tween.onStart = obj.onStart;
549 : 33 : tween.onUpdate = obj.onUpdate;
550 : 33 : tween.onComplete = obj.onComplete;
551 : 33 : tween.onOverwrite = obj.onOverwrite;
552 : 33 : tween.onError = obj.onError;
553 : 33 : tween.onStartParams = obj.onStartParams;
554 : 33 : tween.onUpdateParams = obj.onUpdateParams;
555 : 33 : tween.onCompleteParams = obj.onCompleteParams;
556 : 33 : tween.onOverwriteParams = obj.onOverwriteParams;
557 : 33 : tween.onStartScope = obj.onStartScope;
558 : 33 : tween.onUpdateScope = obj.onUpdateScope;
559 : 33 : tween.onCompleteScope = obj.onCompleteScope;
560 : 33 : tween.onOverwriteScope = obj.onOverwriteScope;
561 : 33 : tween.onErrorScope = obj.onErrorScope;
562 : 33 : tween.rounded = obj.rounded;
563 : 33 : tween.min = obj.min;
564 : 33 : tween.max = obj.max;
565 : 33 : tween.skipUpdates = obj.skipUpdates;
566 : 33 : tween.isCaller = isCaller;
567 : :
568 [ + + ]: 33 : if (isCaller) {
569 : 1 : tween.count = obj.count;
570 : 1 : tween.waitFrames = obj.waitFrames;
571 : : }
572 : :
573 [ + + ]: 33 : if (!isCaller) {
574 : : // Remove other tweenings that occur at the same time
575 : 32 : removeTweensByTime(tween.scope, tween.properties, tween.timeStart, tween.timeComplete);
576 : : }
577 : :
578 : : // And finally adds it to the list
579 : 33 : _tweenList.push(tween);
580 : :
581 : : // Immediate update and removal if it's an immediate tween
582 : : // If not deleted, it executes at the end of this frame execution
583 [ + + ][ + + ]: 33 : if (time == 0 && delay == 0) {
584 : 1 : var myT = _tweenList.length - 1;
585 : 1 : _updateTweenByIndex(myT);
586 : 1 : _removeTweenByIndex(myT);
587 : : }
588 : : }
589 : :
590 : 33 : return true;
591 : : }
592 : :
593 : 32 : function addTween(target, tweeningParameters) {
594 : 32 : return _addTweenOrCaller(target, tweeningParameters, false);
595 : : }
596 : :
597 : 1 : function addCaller(target, tweeningParameters) {
598 : 1 : return _addTweenOrCaller(target, tweeningParameters, true);
599 : : }
600 : :
601 : 21 : function _getNumberOfProperties(object) {
602 : 21 : var totalProperties = 0;
603 : :
604 : : // the following line is disabled because eslint was picking up the following error: the variable name is defined but never used, however since it is required to search the object it is used and we'll allow the line to be ignored to get rid of the error message
605 : : /* eslint-disable-next-line */
606 [ + + ]: 39 : for (let name in object) {
607 : 18 : totalProperties++;
608 : : }
609 : :
610 : :
611 : 21 : return totalProperties;
612 : : }
613 : :
614 : 32 : function removeTweensByTime(scope, properties, timeStart, timeComplete) {
615 : 32 : var removed = false;
616 : : var removedLocally;
617 : : var name;
618 : :
619 [ + + ]: 77 : for (let i = 0; i < _tweenList.length; i++) {
620 : 45 : removedLocally = false;
621 : :
622 [ + + ]: 45 : if (_tweenList[i] &&
623 [ + + ]: 42 : scope == _tweenList[i].scope &&
624 [ - + ]: 13 : timeComplete > _tweenList[i].timeStart &&
625 [ + + ]: 13 : timeStart < _tweenList[i].timeComplete) {
626 [ + + ]: 30 : for (name in _tweenList[i].properties) {
627 [ + + ]: 18 : if (properties[name]) {
628 [ + + ]: 9 : if (!removedLocally) {
629 : 6 : _callOnFunction(_tweenList[i].onOverwrite, 'onOverwrite', _tweenList[i].onOverwriteScope,
630 : 3 : _tweenList[i].scope, _tweenList[i].onOverwriteParams);
631 : : }
632 : :
633 : 9 : _tweenList[i].properties[name] = undefined;
634 : 9 : delete _tweenList[i].properties[name];
635 : 9 : removedLocally = true;
636 : 9 : removed = true;
637 : : }
638 : : }
639 : :
640 [ + + ]: 12 : if (removedLocally &&
641 [ + + ]: 3 : _getNumberOfProperties(_tweenList[i].properties) == 0)
642 : 3 : _removeTweenByIndex(i);
643 : : }
644 : : }
645 : :
646 : 32 : return removed;
647 : : }
648 : :
649 : 5 : function _pauseTweenByIndex(i) {
650 : 5 : var tweening = _tweenList[i];
651 : :
652 [ - + ][ + + ]: 5 : if (tweening == null || tweening.isPaused)
653 : 1 : return false;
654 : :
655 : 4 : tweening.timePaused = _getCurrentTweeningTime(tweening);
656 : 4 : tweening.isPaused = true;
657 : :
658 : 4 : return true;
659 : : }
660 : :
661 : 0 : function _splitTweens(tween, properties) {
662 : 0 : var originalTween = _tweenList[tween];
663 : 0 : var newTween = originalTween.clone();
664 : : var name;
665 : :
666 [ # # ]: 0 : for (let i = 0; i < properties.length; i++) {
667 : 0 : name = properties[i];
668 [ # # ]: 0 : if (originalTween.properties[name]) {
669 : 0 : originalTween.properties[name] = undefined;
670 : 0 : delete originalTween.properties[name];
671 : : }
672 : : }
673 : :
674 : 0 : var found = false;
675 [ # # ]: 0 : for (name in newTween.properties) {
676 : 0 : found = false;
677 [ # # ]: 0 : for (let i = 0; i < properties.length; i++) {
678 [ # # ]: 0 : if (properties[i] == name) {
679 : 0 : found = true;
680 : : break;
681 : : }
682 : : }
683 : :
684 [ # # ]: 0 : if (!found) {
685 : 0 : newTween.properties[name] = undefined;
686 : 0 : delete newTween.properties[name];
687 : : }
688 : : }
689 : :
690 : 0 : _tweenList.push(newTween);
691 : 0 : return _tweenList.length - 1;
692 : : }
693 : :
694 : 6 : function _affectTweens(affectFunction, scope, properties) {
695 : 6 : var affected = false;
696 : :
697 [ + - ]: 6 : if (!_tweenList)
698 : 0 : return false;
699 : :
700 [ + + ]: 31 : for (let i = 0; i < _tweenList.length; i++) {
701 [ - + ][ + + ]: 25 : if (!_tweenList[i] || _tweenList[i].scope != scope)
702 : : continue;
703 : :
704 [ + + ]: 11 : if (properties.length == 0) {
705 : : // Can check everything
706 : 1 : affectFunction(i);
707 : 1 : affected = true;
708 : : } else {
709 : : // Must check whether this tween must have specific properties affected
710 : 10 : var affectedProperties = [];
711 [ + + ]: 27 : for (let j = 0; j < properties.length; j++) {
712 [ + + ]: 17 : if (_tweenList[i].properties[properties[j]])
713 : 6 : affectedProperties.push(properties[j]);
714 : : }
715 : :
716 [ + + ]: 10 : if (affectedProperties.length > 0) {
717 : 6 : var objectProperties = _getNumberOfProperties(_tweenList[i].properties);
718 [ - + ]: 6 : if (objectProperties == affectedProperties.length) {
719 : : // The list of properties is the same as all properties, so affect it all
720 : 6 : affectFunction(i);
721 : 6 : affected = true;
722 : : } else {
723 : : // The properties are mixed, so split the tween and affect only certain specific
724 : : // properties
725 : 0 : var splicedTweenIndex = _splitTweens(i, affectedProperties);
726 : 0 : affectFunction(splicedTweenIndex);
727 : 0 : affected = true;
728 : : }
729 : : }
730 : : }
731 : : }
732 : :
733 : 6 : return affected;
734 : : }
735 : :
736 : 7 : function _isInArray(string, array) {
737 : 7 : var l = array.length;
738 : :
739 [ + + ]: 9 : for (let i = 0; i < l; i++) {
740 [ + - ]: 2 : if (array[i] == string)
741 : 0 : return true;
742 : : }
743 : :
744 : 7 : return false;
745 : 0 : }
746 : :
747 : 6 : function _affectTweensWithFunction(func, args) {
748 : 6 : var properties = [];
749 : 6 : var scope = args[0];
750 : 6 : var affected = false;
751 : : var scopes;
752 : :
753 [ + - ]: 6 : if (Array.isArray(scope))
754 : 0 : scopes = scope.concat();
755 : : else
756 : 6 : scopes = new Array(scope);
757 : :
758 [ + + ]: 13 : for (let i = 1; args[i] != undefined; i++) {
759 [ - + ][ - + ]: 7 : if (typeof args[i] == 'string' && !_isInArray(args[i], properties)) {
760 [ + - ]: 7 : if (_specialPropertySplitterList[args[i]]) {
761 : : // special property, get splitter array first
762 : 0 : var sps = _specialPropertySplitterList[arguments[i]];
763 : 0 : var specialProps = sps.splitValues(scope, null);
764 [ # # ]: 0 : for (let j = 0; j < specialProps.length; j++)
765 : 0 : properties.push(specialProps[j].name);
766 : : } else {
767 : 7 : properties.push(args[i]);
768 : : }
769 : : }
770 : : }
771 : :
772 : : // the return now value means: "affect at least one tween"
773 [ + + ]: 12 : for (let i = 0; i < scopes.length; i++)
774 [ - + ]: 6 : affected = affected || _affectTweens(func, scopes[i], properties);
775 : :
776 : 6 : return affected;
777 : : }
778 : :
779 : 1 : function resumeTweens() {
780 : 1 : return _affectTweensWithFunction(_resumeTweenByIndex, arguments);
781 : : }
782 : :
783 : 3 : function pauseTweens() {
784 : 3 : return _affectTweensWithFunction(_pauseTweenByIndex, arguments);
785 : : }
786 : :
787 : 2 : function removeTweens() {
788 : 2 : return _affectTweensWithFunction(_removeTweenByIndex, arguments);
789 : : }
790 : :
791 : 3 : function _mapOverTweens(func) {
792 : 3 : var rv = false;
793 : :
794 [ + - ]: 3 : if (_tweenList == null)
795 : 0 : return false;
796 : :
797 [ + + ]: 11 : for (let i = 0; i < _tweenList.length; i++) {
798 [ + + ]: 8 : if (func(i))
799 : 7 : rv = true;
800 : : }
801 : :
802 : 3 : return rv;
803 : : }
804 : :
805 : 1 : function pauseAllTweens() {
806 : 1 : return _mapOverTweens(_pauseTweenByIndex);
807 : : }
808 : :
809 : 1 : function resumeAllTweens() {
810 : 1 : return _mapOverTweens(_resumeTweenByIndex);
811 : : }
812 : :
813 : 1 : function removeAllTweens() {
814 : 1 : return _mapOverTweens(_removeTweenByIndex);
815 : : }
816 : :
817 : 6 : function getTweenCount(scope) {
818 [ + - ]: 6 : if (!_tweenList)
819 : 0 : return 0;
820 : :
821 : 6 : var c = 0;
822 : :
823 [ + + ]: 20 : for (let i = 0; i < _tweenList.length; i++) {
824 [ + + ][ + + ]: 14 : if (_tweenList[i] && _tweenList[i].scope == scope)
825 : 12 : c += _getNumberOfProperties(_tweenList[i].properties);
826 : : }
827 : :
828 : 6 : return c;
829 : : }
830 : :
831 : 1 : function registerSpecialProperty(name, getFunction, setFunction,
832 : : parameters, preProcessFunction) {
833 : 1 : _specialPropertyList[name] = {
834 : 1 : getValue: getFunction,
835 : 1 : setValue: setFunction,
836 : 1 : parameters,
837 : 1 : preProcess: preProcessFunction,
838 : : };
839 : : }
840 : :
841 : 1 : function registerSpecialPropertyModifier(name, modifyFunction, getFunction) {
842 : 1 : _specialPropertyModifierList[name] = {
843 : 1 : modifyValues: modifyFunction,
844 : 1 : getValue: getFunction,
845 : : };
846 : : }
847 : :
848 : 1 : function registerSpecialPropertySplitter(name, splitFunction, parameters) {
849 : 1 : _specialPropertySplitterList[name] = {
850 : 1 : splitValues: splitFunction,
851 : 1 : parameters,
852 : : };
853 : : }
854 : :
855 : 0 : function setTimeScale(scale) {
856 : 0 : _timeScale = scale;
857 : : }
858 : :
859 : 0 : function getTimeScale() {
860 : 0 : return _timeScale;
861 : : }
|