13257c12220c411cc9db564e6bf02baba4c1bc061caryclarkvar animationState = {};
23257c12220c411cc9db564e6bf02baba4c1bc061caryclarkanimationState.reset = function (engine) {
33257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if ('string' === typeof engine) {
43257c12220c411cc9db564e6bf02baba4c1bc061caryclark        this.defaultEngine = engine;
53257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
63257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.defaults = {};
73257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.displayList = [];
83257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.displayDict = {};
93257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.start = null;
103257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.time = 0;
113257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.timeline = [];
123257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.timelineIndex = 0;
133257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.requestID = null;
143257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.paused = false;
153257c12220c411cc9db564e6bf02baba4c1bc061caryclark    this.displayEngine = 'undefined' === typeof engine ? this.defaultEngine : engine;
163257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
173257c12220c411cc9db564e6bf02baba4c1bc061caryclark
183257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction addActions(frame, timeline) {
193257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var keyframe = keyframes[frame];
203257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var len = keyframe.length;
213257c12220c411cc9db564e6bf02baba4c1bc061caryclark    for (var i = 0; i < len; ++i) {
223257c12220c411cc9db564e6bf02baba4c1bc061caryclark        var action = keyframe[i];
233257c12220c411cc9db564e6bf02baba4c1bc061caryclark        loopOver(action, timeline);
243257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
253257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
263257c12220c411cc9db564e6bf02baba4c1bc061caryclark
273257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction animateList(now) {
283257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (animationState.paused) {
293257c12220c411cc9db564e6bf02baba4c1bc061caryclark        return;
303257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
313257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (animationState.start == null) {
323257c12220c411cc9db564e6bf02baba4c1bc061caryclark        animationState.start = now - animationState.time;
333257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
343257c12220c411cc9db564e6bf02baba4c1bc061caryclark    animationState.time = now - animationState.start;
353257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var stillAnimating = false;
363257c12220c411cc9db564e6bf02baba4c1bc061caryclark    for (var index = animationState.timelineIndex; index < animationState.timeline.length; ++index) {
373257c12220c411cc9db564e6bf02baba4c1bc061caryclark        var animation = animationState.timeline[index];
383257c12220c411cc9db564e6bf02baba4c1bc061caryclark        if (animation.time > animationState.time) {
393257c12220c411cc9db564e6bf02baba4c1bc061caryclark            stillAnimating = true;
403257c12220c411cc9db564e6bf02baba4c1bc061caryclark            break;
413257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
423257c12220c411cc9db564e6bf02baba4c1bc061caryclark        if (animation.time + animation.duration < animationState.time) {
433257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (animation.finalized) {
443257c12220c411cc9db564e6bf02baba4c1bc061caryclark                continue;
453257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
463257c12220c411cc9db564e6bf02baba4c1bc061caryclark            animation.finalized = true;
473257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
483257c12220c411cc9db564e6bf02baba4c1bc061caryclark        stillAnimating = true;
493257c12220c411cc9db564e6bf02baba4c1bc061caryclark        var actions = animation.actions;
503257c12220c411cc9db564e6bf02baba4c1bc061caryclark        for (var aIndex = 0; aIndex < actions.length; ++aIndex) {
513257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var action = actions[aIndex];
523257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var hasDraw = 'draw' in action;
533257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var hasRef = 'ref' in action;
543257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var displayIndex;
553257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (hasDraw) {
563257c12220c411cc9db564e6bf02baba4c1bc061caryclark                var ref = hasRef ? action.ref : "anonymous_" + index + "_" + aIndex;
573257c12220c411cc9db564e6bf02baba4c1bc061caryclark                assert('string' == typeof(ref));
583257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if (ref in animationState.displayDict) {
593257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    displayIndex = animationState.displayDict[ref];
603257c12220c411cc9db564e6bf02baba4c1bc061caryclark                } else {
613257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    assert('string' == typeof(action.draw));
623257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    var draw = (new Function("return " + action.draw))();
633257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    assert('object' == typeof(draw));
643257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    var paint;
653257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    if ('paint' in action) {
663257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        assert('string' == typeof(action.paint));
673257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        paint = (new Function("return " + action.paint))();
683257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        assert('object' == typeof(paint) && !isArray(paint));
693257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    } else {
703257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        paint = animationState.defaults.paint;
713257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
723257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    displayIndex = animationState.displayList.length;
733257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    animationState.displayList.push( { "ref":ref, "draw":draw, "paint":paint,
743257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        "drawSpec":action.draw, "paintSpec":action.paint,
753257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        "drawCopied":false, "paintCopied":false,
763257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        "drawDirty":true, "paintDirty":true, "once":false } );
773257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    animationState.displayDict[ref] = displayIndex;
783257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
793257c12220c411cc9db564e6bf02baba4c1bc061caryclark            } else if (hasRef) {
803257c12220c411cc9db564e6bf02baba4c1bc061caryclark                assert('string' == typeof(action.ref));
813257c12220c411cc9db564e6bf02baba4c1bc061caryclark                displayIndex = animationState.displayDict[action.ref];
823257c12220c411cc9db564e6bf02baba4c1bc061caryclark            } else {
833257c12220c411cc9db564e6bf02baba4c1bc061caryclark                assert(actions.length == 1);
843257c12220c411cc9db564e6bf02baba4c1bc061caryclark                for (var prop in action) {
853257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    if ('paint' == prop) {
863257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        assert('string' == typeof(action[prop]));
873257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        var obj = (new Function("return " + action[prop]))();
883257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        assert('object' == typeof(obj) && !isArray(obj));
893257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        animationState.defaults[prop] = obj;
903257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    } else {
913257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        animationState.defaults[prop] = action[prop];
923257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
933257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
943257c12220c411cc9db564e6bf02baba4c1bc061caryclark                continue;
953257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
963257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var targetSpec = 'target' in action ? action.target : animationState.defaults.target;
973257c12220c411cc9db564e6bf02baba4c1bc061caryclark            assert(targetSpec);
983257c12220c411cc9db564e6bf02baba4c1bc061caryclark            assert('string' == typeof(targetSpec));
993257c12220c411cc9db564e6bf02baba4c1bc061caryclark            assert(displayIndex < animationState.displayList.length);
1003257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var display = animationState.displayList[displayIndex];
1013257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var modDraw = targetSpec.startsWith('draw');
1023257c12220c411cc9db564e6bf02baba4c1bc061caryclark            assert(modDraw || targetSpec.startsWith('paint'));
1033257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var modType = modDraw ? "draw" : "paint";
1043257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var copied = modDraw ? display.drawCopied : action.paintCopied;
1053257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (!copied) {
1063257c12220c411cc9db564e6bf02baba4c1bc061caryclark                var copy;
1073257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if (!modDraw || display.drawSpec.startsWith("text")) {
1083257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    copy = {};
1093257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    var original = modDraw ? display.draw : display.paint;
1103257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    for (var p in original) {
1113257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        copy[p] = original[p];
1123257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
1133257c12220c411cc9db564e6bf02baba4c1bc061caryclark                } else if (display.drawSpec.startsWith("paths")) {
1143257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    copy = [];
1153257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    for (var i = 0; i < display.draw.length; ++i) {
1163257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        var curves = display.draw[i];
1173257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        var curve = Object.keys(curves)[0];
1183257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        copy[i] = {};
1193257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        copy[i][curve] = curves[curve].slice(0);  // clone the array of curves
1203257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
1213257c12220c411cc9db564e6bf02baba4c1bc061caryclark                } else {
1223257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    assert(display.drawSpec.startsWith("pictures"));
1233257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    copy = [];
1243257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    for (var i = 0; i < display.draw.length; ++i) {
1253257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        var entry = display.draw[i];
1263257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        copy[i] = { "draw":entry.draw, "paint":entry.paint };
1273257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
1283257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
1293257c12220c411cc9db564e6bf02baba4c1bc061caryclark                display[modType] = copy;
1303257c12220c411cc9db564e6bf02baba4c1bc061caryclark                display[modType + "Copied"] = true;
1313257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
1323257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var targetField, targetObject, fieldOffset;
1333257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (targetSpec.endsWith("]")) {
1343257c12220c411cc9db564e6bf02baba4c1bc061caryclark                fieldOffset = targetSpec.lastIndexOf("[");
1353257c12220c411cc9db564e6bf02baba4c1bc061caryclark                assert(fieldOffset >= 0);
1363257c12220c411cc9db564e6bf02baba4c1bc061caryclark                targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length - 1);
1373257c12220c411cc9db564e6bf02baba4c1bc061caryclark                var arrayIndex = +targetField;
1383257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if (!isNaN(arrayIndex) && targetField.length > 0) {
1393257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    targetField = arrayIndex;
1403257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
1413257c12220c411cc9db564e6bf02baba4c1bc061caryclark
1423257c12220c411cc9db564e6bf02baba4c1bc061caryclark            } else {
1433257c12220c411cc9db564e6bf02baba4c1bc061caryclark                fieldOffset = targetSpec.lastIndexOf(".");
1443257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if (fieldOffset >= 0) {
1453257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length);
1463257c12220c411cc9db564e6bf02baba4c1bc061caryclark                } else {
1473257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    targetObject = display;
1483257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    targetField = targetSpec;
1493257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
1503257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
1513257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (fieldOffset >= 0) {
1523257c12220c411cc9db564e6bf02baba4c1bc061caryclark                var sub = targetSpec.substring(0, fieldOffset);
1533257c12220c411cc9db564e6bf02baba4c1bc061caryclark                targetObject = (new Function('display', "return display." + sub))(display);
1543257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
1553257c12220c411cc9db564e6bf02baba4c1bc061caryclark            assert(null != targetObject[targetField]);
1563257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (!('start' in action) || action.start < animation.time) {
1573257c12220c411cc9db564e6bf02baba4c1bc061caryclark                for (var p in animationState.defaults) {
1583257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    if ('draw' == p || 'paint' == p || 'ref' == p) {
1593257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        continue;
1603257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
1613257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    assert('range' == p || 'target' == p || 'formula' == p || 'params' == p);
1623257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    if (!(p in action)) {
1633257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        action[p] = animationState.defaults[p];
1643257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
1653257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
1663257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if ('number' == typeof(action.formula)) {
1673257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    targetObject[targetField] = action.formula;
1683257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    action.once = true;
1693257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
1703257c12220c411cc9db564e6bf02baba4c1bc061caryclark                action.start = animation.time;
1713257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
1723257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (action.once) {
1733257c12220c411cc9db564e6bf02baba4c1bc061caryclark                continue;
1743257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
1753257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var value = Math.min(1, (animationState.time - animation.time) / animation.duration);
1763257c12220c411cc9db564e6bf02baba4c1bc061caryclark            var scaled = action.range[0] + (action.range[1] - action.range[0]) * value;
1773257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if ('params' in action) {
1783257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if (!('func' in action)) {
1793257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    if (isArray(action.params)) {
1803257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        action.funcParams = [];
1813257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        var len = action.params.length;
1823257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        for (var i = 0; i < len; ++i) {
1833257c12220c411cc9db564e6bf02baba4c1bc061caryclark                            action.funcParams[i] = 'target' == action.params[i]
1843257c12220c411cc9db564e6bf02baba4c1bc061caryclark                                ? targetObject[targetField]
1853257c12220c411cc9db564e6bf02baba4c1bc061caryclark                                : (new Function("return " + action.params[i]))();
1863257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        }
1873257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    } else {
1883257c12220c411cc9db564e6bf02baba4c1bc061caryclark                        action.funcParams = 'target' == action.params
1893257c12220c411cc9db564e6bf02baba4c1bc061caryclark                                ? targetObject[targetField]
1903257c12220c411cc9db564e6bf02baba4c1bc061caryclark                                : (new Function("return " + action.params))();
1913257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    }
1923257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    assert('formula' in action && 'string' == typeof(action.formula));
1933257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    // evaluate inline function to get value
1943257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    action.func = new Function('value', 'params', "return " + action.formula);
1953257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
1963257c12220c411cc9db564e6bf02baba4c1bc061caryclark                scaled = action.func(scaled, action.funcParams);
1973257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
1983257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (targetObject[targetField] != scaled) {
1993257c12220c411cc9db564e6bf02baba4c1bc061caryclark                if (modDraw) {
2003257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    display.drawDirty = true;
2013257c12220c411cc9db564e6bf02baba4c1bc061caryclark                } else {
2023257c12220c411cc9db564e6bf02baba4c1bc061caryclark                    display.paintDirty = true;
2033257c12220c411cc9db564e6bf02baba4c1bc061caryclark                }
2043257c12220c411cc9db564e6bf02baba4c1bc061caryclark                targetObject[targetField] = scaled;
2053257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
2063257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
2073257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2083257c12220c411cc9db564e6bf02baba4c1bc061caryclark    displayBackend(animationState.displayEngine, animationState.displayList);
2093257c12220c411cc9db564e6bf02baba4c1bc061caryclark
2103257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (stillAnimating) {
2113257c12220c411cc9db564e6bf02baba4c1bc061caryclark        animationState.requestID = requestAnimationFrame(animateList);
2123257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2133257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
2143257c12220c411cc9db564e6bf02baba4c1bc061caryclark
2153257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction flattenPaint(paint) {
2163257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (!paint.paint) {
2173257c12220c411cc9db564e6bf02baba4c1bc061caryclark        return;
2183257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2193257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var parent = paints[paint.paint];
2203257c12220c411cc9db564e6bf02baba4c1bc061caryclark    flattenPaint(parent);
2213257c12220c411cc9db564e6bf02baba4c1bc061caryclark    for (var prop in parent) {
2223257c12220c411cc9db564e6bf02baba4c1bc061caryclark        if (!(prop in paint)) {
2233257c12220c411cc9db564e6bf02baba4c1bc061caryclark            paint[prop] = parent[prop];
2243257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
2253257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2263257c12220c411cc9db564e6bf02baba4c1bc061caryclark    paint.paint = null;
2273257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
2283257c12220c411cc9db564e6bf02baba4c1bc061caryclark
2293257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction init(engine, keyframe) {
2303257c12220c411cc9db564e6bf02baba4c1bc061caryclark    animationState.reset(engine);
2313257c12220c411cc9db564e6bf02baba4c1bc061caryclark    setupPaint();
2323257c12220c411cc9db564e6bf02baba4c1bc061caryclark    setupBackend(animationState.displayEngine);
2333257c12220c411cc9db564e6bf02baba4c1bc061caryclark    keyframeInit(keyframe);
2343257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
2353257c12220c411cc9db564e6bf02baba4c1bc061caryclark
2363257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction keyframeInit(frame) {
2373257c12220c411cc9db564e6bf02baba4c1bc061caryclark    animationState.reset();
2383257c12220c411cc9db564e6bf02baba4c1bc061caryclark    addActions("_default", animationState.timeline);
2393257c12220c411cc9db564e6bf02baba4c1bc061caryclark    addActions(frame, animationState.timeline);
2403257c12220c411cc9db564e6bf02baba4c1bc061caryclark    for (var index = 0; index < animationState.timeline.length; ++index) {
2413257c12220c411cc9db564e6bf02baba4c1bc061caryclark        animationState.timeline[index].position = index;
2423257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2433257c12220c411cc9db564e6bf02baba4c1bc061caryclark    animationState.timeline.sort(function(a, b) {
2443257c12220c411cc9db564e6bf02baba4c1bc061caryclark        if (a.time == b.time) {
2453257c12220c411cc9db564e6bf02baba4c1bc061caryclark            return a.position - b.position;
2463257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
2473257c12220c411cc9db564e6bf02baba4c1bc061caryclark        return a.time - b.time;
2483257c12220c411cc9db564e6bf02baba4c1bc061caryclark    });
2493257c12220c411cc9db564e6bf02baba4c1bc061caryclark    keyframeBackendInit(animationState.displayEngine, animationState.displayList,
2503257c12220c411cc9db564e6bf02baba4c1bc061caryclark            keyframes[frame][0]);
2513257c12220c411cc9db564e6bf02baba4c1bc061caryclark    animationState.requestID = requestAnimationFrame(animateList);
2523257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
2533257c12220c411cc9db564e6bf02baba4c1bc061caryclark
2543257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction loopAddProp(action, propName) {
2553257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var funcStr = "";
2563257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var prop = action[propName];
2573257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if ('draw' != propName && isArray(prop)) {
2583257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += '[';
2593257c12220c411cc9db564e6bf02baba4c1bc061caryclark        for (var index = 0; index < prop.length; ++index) {
2603257c12220c411cc9db564e6bf02baba4c1bc061caryclark            funcStr += loopAddProp(prop, index);
2613257c12220c411cc9db564e6bf02baba4c1bc061caryclark            if (index + 1 < prop.length) {
2623257c12220c411cc9db564e6bf02baba4c1bc061caryclark                funcStr += ", ";
2633257c12220c411cc9db564e6bf02baba4c1bc061caryclark            }
2643257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
2653257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += ']';
2663257c12220c411cc9db564e6bf02baba4c1bc061caryclark        return funcStr;
2673257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2683257c12220c411cc9db564e6bf02baba4c1bc061caryclark    assert("object" != typeof(prop));
2693257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var useString = "string" == typeof(prop) && isAlpha(prop.charCodeAt(0));
2703257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (useString) {
2713257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "'";
2723257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2733257c12220c411cc9db564e6bf02baba4c1bc061caryclark    funcStr += prop;
2743257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (useString) {
2753257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "'";
2763257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2773257c12220c411cc9db564e6bf02baba4c1bc061caryclark    return funcStr;
2783257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
2793257c12220c411cc9db564e6bf02baba4c1bc061caryclark
2803257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction loopOver(rec, timeline) {
2813257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var funcStr = "";
2823257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (rec.for) {
2833257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "for (" + rec.for[0] + "; " + rec.for[1] + "; " + rec.for[2] + ") {\n";
2843257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
2853257c12220c411cc9db564e6bf02baba4c1bc061caryclark    funcStr += "    var time = " + ('time' in rec ? rec.time : 0) + ";\n";
2863257c12220c411cc9db564e6bf02baba4c1bc061caryclark    funcStr += "    var duration = " + ('duration' in rec ? rec.duration : 0) + ";\n";
2873257c12220c411cc9db564e6bf02baba4c1bc061caryclark    funcStr += "    var actions = [];\n";
2883257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var len = rec.actions.length;
2893257c12220c411cc9db564e6bf02baba4c1bc061caryclark    for (var i = 0; i < len; ++i) {
2903257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "    var action" + i + " = {\n";
2913257c12220c411cc9db564e6bf02baba4c1bc061caryclark        var action = rec.actions[i];
2923257c12220c411cc9db564e6bf02baba4c1bc061caryclark        for (var p in action) {
2933257c12220c411cc9db564e6bf02baba4c1bc061caryclark            funcStr += "        '" + p + "':";
2943257c12220c411cc9db564e6bf02baba4c1bc061caryclark            funcStr += loopAddProp(action, p);
2953257c12220c411cc9db564e6bf02baba4c1bc061caryclark            funcStr += ",\n";
2963257c12220c411cc9db564e6bf02baba4c1bc061caryclark        }
2973257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr = funcStr.substring(0, funcStr.length - 2);
2983257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "\n    };\n";
2993257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "    actions.push(action" + i + ");\n";
3003257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
3013257c12220c411cc9db564e6bf02baba4c1bc061caryclark    funcStr += "    timeline.push( { 'time':time, 'duration':duration, 'actions':actions,"
3023257c12220c411cc9db564e6bf02baba4c1bc061caryclark            + "'finalized':false } );\n";
3033257c12220c411cc9db564e6bf02baba4c1bc061caryclark    if (rec.for) {
3043257c12220c411cc9db564e6bf02baba4c1bc061caryclark        funcStr += "}\n";
3053257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
3063257c12220c411cc9db564e6bf02baba4c1bc061caryclark    var func = new Function('rec', 'timeline', funcStr);
3073257c12220c411cc9db564e6bf02baba4c1bc061caryclark    func(rec, timeline);
3083257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
3093257c12220c411cc9db564e6bf02baba4c1bc061caryclark
3103257c12220c411cc9db564e6bf02baba4c1bc061caryclarkfunction setupPaint() {
3113257c12220c411cc9db564e6bf02baba4c1bc061caryclark    for (var prop in paints) {
3123257c12220c411cc9db564e6bf02baba4c1bc061caryclark        flattenPaint(paints[prop]);
3133257c12220c411cc9db564e6bf02baba4c1bc061caryclark    }
3143257c12220c411cc9db564e6bf02baba4c1bc061caryclark}
315