bodymovin
Version: 
After Effects plugin for exporting animations to SVG + JavaScript or canvas + JavaScript
1,205 lines (1,131 loc) • 124 kB
JSX
var Gtlym = {};var bodymovinWindow = this;
(function(){
/****** INIT JSON PARSER ******/
if (typeof JSON !== 'object') {
    JSON = {};
}
(function () {
    'use strict';
    function f(n) {
        return n < 10 ? '0' + n : n;
    }
    if (typeof Date.prototype.toJSON !== 'function') {
        Date.prototype.toJSON = function () {
            return isFinite(this.valueOf())
                ? this.getUTCFullYear()     + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate())      + 'T' +
                f(this.getUTCHours())     + ':' +
                f(this.getUTCMinutes())   + ':' +
                f(this.getUTCSeconds())   + 'Z'
                : null;
        };
        String.prototype.toJSON      =
            Number.prototype.toJSON  =
                Boolean.prototype.toJSON = function () {
                    return this.valueOf();
                };
    }
    var cx,
        escapable,
        gap,
        indent,
        meta,
        rep;
    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }
    function str(key, holder) {
        var i,
            k,
            v,
            length,
            mind = gap,
            partial,
            value = holder[key];
        if (value && typeof value === 'object' &&
            typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
            case 'string':
                return quote(value);
            case 'number':
                return isFinite(value) ? String(value) : 'null';
            case 'boolean':
            case 'null':
                return String(value);
            case 'object':
                if (!value) {
                    return 'null';
                }
                gap += indent;
                partial = [];
                if (Object.prototype.toString.apply(value) === '[object Array]') {
                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }
                    v = partial.length === 0
                        ? '[]'
                        : gap
                        ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
                        : '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }
                if (rep && typeof rep === 'object') {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        if (typeof rep[i] === 'string') {
                            k = rep[i];
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                }
                v = partial.length === 0
                    ? '{}'
                    : gap
                    ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
                    : '{' + partial.join(',') + '}';
                gap = mind;
                return v;
        }
    }
    if (typeof JSON.stringify !== 'function') {
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
        meta = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
            } else if (typeof space === 'string') {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            return str('', {'': value});
        };
    }
    if (typeof JSON.parse !== 'function') {
        cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
        JSON.parse = function (text, reviver) {
            var j;
            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }
            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/
                .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                    .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                    .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                j = eval('(' + text + ')');
                return typeof reviver === 'function'
                    ? walk({'': j}, '')
                    : j;
            }
            throw new SyntaxError('JSON.parse');
        };
    }
}());
/****** END JSON PARSER ******/
/****** INIT ARRAY POLYFILLS ******/
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(callback, thisArg) {
        var T, k;
        if (this == null) {
            throw new TypeError(' this is null or not defined');
        }
        var O = Object(this);
        var len = O.length >>> 0;
        if (typeof callback !== "function") {
            throw new TypeError(callback + ' is not a function');
        }
        if (arguments.length > 1) {
            T = thisArg;
        }
        k = 0;
        while (k < len) {
            var kValue;
            if (k in O) {
                kValue = O[k];
                callback.call(T, kValue, k, O);
            }
            k++;
        }
    };
}
if (!Array.prototype.map) {
    Array.prototype.map = function(callback, thisArg) {
        var T, A, k;
        if (this == null) {
            throw new TypeError(' this is null or not defined');
        }
        var O = Object(this);
        var len = O.length >>> 0;
        if (typeof callback !== 'function') {
            throw new TypeError(callback + ' is not a function');
        }
        if (arguments.length > 1) {
            T = thisArg;
        }
        A = new Array(len);
        k = 0;
        while (k < len) {
            var kValue, mappedValue;
            if (k in O) {
                kValue = O[k];
                mappedValue = callback.call(T, kValue, k, O);
                A[k] = mappedValue;
            }
            k++;
        }
        return A;
    };
}
/****** END ARRAY POLYFILLS ******/
var console = {
    log : function(){
        $.writeln.call($,arguments);
    }
};
/****** INIT Var Declarations ******/
var helperSolidComp;
var helperFootage;
//Solid for anchor point fix on shape layers
var helperSolid;
//Destination export folder
var exportFolder;
//Interval objects container
//var Gtlym = {};
Gtlym.CALL = {};
//Render cancelled flag
var renderCancelled = false;
//modules
var LayerConverter;
var rqManager;
var extrasInstance;
var AsyncManager;
var DOMAnimationManager;
var CompConverter;
var ShapesParser;
var EffectsParser;
var UI;
/****** INIT Assets Manager ******/
(function(){
    var ob = {};
    var sourceAssets = [];
    var sourceExportData = [];
    function reset(){
        sourceAssets = [];
        sourceExportData = [];
    }
    function associateLayerToSource(layer, source){
        var i=0, len = sourceAssets.length;
        while(i<len){
            if(sourceAssets[i].s === source){
                sourceAssets[i].l.push(layer);
                break;
            }
            i+=1;
        }
    }
    function exportFileFromLayer(layer, filesDirectory){
        var i = 0, len = sourceAssets.length;
        var j, jLen, found = false;
        while(i<len){
            j = 0;
            jLen = sourceAssets[i].l.length;
            while(j<jLen){
                if(sourceAssets[i].l[j] === layer){
                    found = true;
                    if(sourceAssets[i].exported === false){
                        var imageName = 'imagen_'+i;
                        var imageExtension = 'png';
                        var destinationFile = new File(filesDirectory.fullName+'/'+imageName+'.'+imageExtension);
                        sourceAssets[i].f.copy(destinationFile);
                        sourceAssets[i].exported = true;
                        sourceAssets[i].path = 'files/'+imageName+'.'+imageExtension;
                    }
                }
                j+=1;
            }
            if(found === true){
                return i;
            }
            i+=1;
        }
    }
    function createAssetsDataForExport(){
        sourceAssets.forEach(function(item){
            if(item.exported === true){
                sourceExportData.push({path:item.path});
            }
        })
    }
    function createLayerSource(file, layer, source){
        sourceAssets.push({s:source,f:file,l:[layer], exported:false});
    }
    function getAssetsData(){
        return sourceExportData;
    }
    ob.getAssetsData = getAssetsData;
    ob.reset = reset;
    ob.associateLayerToSource = associateLayerToSource;
    ob.createLayerSource = createLayerSource;
    ob.createAssetsDataForExport = createAssetsDataForExport;
    ob.exportFileFromLayer = exportFileFromLayer;
    AssetsManager = ob;
}());
/****** END Assets Manager ******/
/**** Async Manager ****/
(function(){
    var ob = {};
    var asyncCount = 0;
    var callback;
    var asyncElements = [];
    function executeCall(item){
        item.call();
    }
    function executeAsyncCalls(){
        var executingElements = asyncElements.splice(0,asyncElements.length);
        asyncElements.length = 0;
        executingElements.forEach(executeCall);
        asyncCount -= 1;
        if(asyncCount == 0){
            callback.apply();
        }
    }
    function addAsyncCall(fn){
        asyncElements.push(fn);
        if(asyncElements.length == 1){
            asyncCount += 1;
            //Todo Create async call
            extrasInstance.setTimeout(executeAsyncCalls,1);
        }
    }
    function addAsyncCounter(){
        asyncCount += 1;
    }
    function removeAsyncCounter(){
        asyncCount -= 1;
        if(asyncCount == 0){
            callback.apply();
        }
    }
    function getAsyncCounter(){
        return asyncCount;
    }
    function setCallBack(cb){
        callback = cb;
    }
    ob.addAsyncCall = addAsyncCall;
    ob.addAsyncCount = addAsyncCounter;
    ob.removeAsyncCounter = removeAsyncCounter;
    ob.getAsyncCounter = getAsyncCounter;
    ob.setCallBack = setCallBack;
    AsyncManager = ob;
}());
/**** END Async Manager ****/
/****** INIT DOMAnimationMAnager ******/
(function(){
    var frameRate = 0;
    var totalFrames = 0;
    var firstFrame = 0;
    var currentRenderFrame = 0;
    var currentTime = 0;
    var imageCount = 0;
    var zCount = 0;
    var isRenderReady = false;
    var mainComp;
    var mainLayers = [];
    var filesDirectory;
    var callback;
    var pendingLayers = [];
    var totalLayers = 0;
    var exportedComps = [];
    function getCompositionAnimationData(compo, compositionData,fDirectory){
        exportedComps = [];
        mainComp = compo;
        frameRate = mainComp.frameRate;
        currentRenderFrame = 0;
        imageCount = 0;
        zCount = 0;
        mainLayers = [];
        totalFrames = mainComp.workAreaDuration*mainComp.frameRate;
        firstFrame = mainComp.workAreaStart*mainComp.frameRate;
        //totalFrames = 1;
        var animationOb = {};
        compositionData.animation = animationOb;
        compositionData.assets = AssetsManager.getAssetsData();
        compositionData.v = '2.0.6';
        animationOb.layers = mainLayers;
        animationOb.totalFrames = totalFrames;
        animationOb.frameRate = frameRate;
        animationOb.ff = mainComp.workAreaStart;
        animationOb.compWidth = mainComp.width;
        animationOb.compHeight = mainComp.height;
        filesDirectory = fDirectory;
        iterateComposition();
    }
    function getMaskMode (num){
        switch(num){
            case MaskMode.NONE:
                return 'n';
            case MaskMode.ADD:
                return 'a';
            case MaskMode.SUBTRACT:
                return 's';
            case MaskMode.INTERSECT:
                return 'i';
            case MaskMode.LIGHTEN:
                return 'l';
            case MaskMode.DARKEN:
                return 'd';
            case MaskMode.DIFFERENCE:
                return 'f';
        }
    }
    function addMasksToLayer(layerInfo,layerOb,time){
        layerOb.mk = [];
        var i, len = layerInfo.mask.numProperties, maskShape, maskElement;
        for(i=0;i<len;i++){
            maskElement = layerInfo.mask(i+1);
            maskShape = layerInfo.mask(i+1).property('maskShape').valueAtTime(time,false);
            layerOb.mk.push({v:extrasInstance.roundNumber(maskShape.vertices,3), i:extrasInstance.roundNumber(maskShape.inTangents,3), o:extrasInstance.roundNumber(maskShape.outTangents,3), t:extrasInstance.roundNumber(maskElement.property('Mask Opacity').valueAtTime(time,false)/100,3)});
        }
    }
    function setMasks(masks,layerOb){
        layerOb.masksProperties = [];
        var i, len = masks.numProperties, maskShape, maskElement;
        for(i=0;i<len;i++){
            maskElement = masks(i+1);
            maskShape = maskElement.property('maskShape').value;
            var shapeData = {
                cl:maskShape.closed,
                inv:maskElement.inverted,
                mode:getMaskMode(maskElement.maskMode)
            };
            extrasInstance.convertToBezierValues(maskElement.property('maskShape'), frameRate, shapeData,'pt');
            extrasInstance.convertToBezierValues(maskElement.property('Mask Opacity'), frameRate, shapeData,'o');
            layerOb.masksProperties.push(shapeData);
        }
    }
    function addStillAsset(layerOb,layerInfo){
        layerOb.assetId = AssetsManager.exportFileFromLayer(layerInfo,filesDirectory);
    }
    function removeExtraData(layersData){
        var i, len = layersData.length,j, jLen, shapes;
        for(i = 0;i<len;i++){
            var layerOb = layersData[i];
            if(layerOb.enabled == false){
                layersData.splice(i,1);
                i -= 1;
                len -= 1;
                continue;
            }
            layerOb.lastData = null ;
            delete layerOb.lastData;
            if(layerOb.type == 'ShapeLayer'){
                shapes = layerOb.shapes;
                jLen = shapes.length;
                for(j=0;j<jLen;j++){
                    shapes[j].lastData = null;
                    delete shapes[j].lastData;
                }
            }
            if(layerOb.type == 'PreCompLayer' && layerOb.layers){
                removeExtraData(layerOb.layers);
            }
            EffectsParser.saveEffectData(layerOb);
        }
    }
    function processFinalData(layersData){
        var i, len = layersData.length;
        for(i = 0;i<len;i++){
            var layerOb = layersData[i];
            if(layerOb.type == 'ShapeLayer'){
                layerOb.rectData.w = extrasInstance.roundNumber(layerOb.rectData.r - layerOb.rectData.l,3);
                layerOb.rectData.h = extrasInstance.roundNumber(layerOb.rectData.b - layerOb.rectData.t,3);
            }
            if(layerOb.type == 'PreCompLayer' && layerOb.layers){
                processFinalData(layerOb.layers);
            }
        }
    }
    function buildTextData(textDocument){
        var textDataOb = {};
        textDataOb.font = textDocument.font;
        textDataOb.fontSize = textDocument.fontSize;
        textDataOb.fillColor = extrasInstance.arrayRgbToHex(textDocument.fillColor);
        textDataOb.text = textDocument.text;
        var justification = '';
        switch(textDocument.justification){
            case ParagraphJustification.LEFT_JUSTIFY:
                justification = 'left';
                break;
            case ParagraphJustification.RIGHT_JUSTIFY:
                justification = 'right';
                break;
            case ParagraphJustification.CENTER_JUSTIFY:
                justification = 'center';
                break;
            case ParagraphJustification.FULL_JUSTIFY_LASTLINE_LEFT:
            case ParagraphJustification.FULL_JUSTIFY_LASTLINE_RIGHT:
            case ParagraphJustification.FULL_JUSTIFY_LASTLINE_CENTER:
            case ParagraphJustification.FULL_JUSTIFY_LASTLINE_FULL:
                justification = 'justify';
                break;
            default:
                justification = 'left';
                break;
        }
        textDataOb.justification = justification;
        return textDataOb;
    }
    function analyzeNextLayer(){
        if(pendingLayers.length == 0){
            renderNextFrame();
        }else{
            var pendingItem = pendingLayers.pop();
            UI.setProgress(pendingLayers.length/totalLayers);
            var layerOb = pendingItem.lOb;
            var layerInfo = pendingItem.lInfo;
            var frameRate = pendingItem.frameRate;
            var lType = extrasInstance.layerType(layerInfo);
            if(lType == 'AudioLayer' || lType == 'CameraLayer' || (layerInfo.enabled == false && !layerInfo.isTrackMatte)){
                //TODO add audios
                layerOb.enabled = false;
                extrasInstance.setTimeout(analyzeNextLayer,100);
                return;
            }else if(lType == 'TextLayer'){
                var textProp = layerInfo.property("Source Text");
                var textDocument = textProp.value;
                layerOb.textData = buildTextData(textDocument);
                var r = layerInfo.sourceRectAtTime(0, false);
                layerOb.textData.xOffset = r.left;
                layerOb.textData.yOffset = r.top;
                layerOb.textData.width = r.width;
                layerOb.textData.height = r.height;
            }
            //EffectsParser.createEffects(layerInfo,layerOb);
            if(layerInfo.mask.numProperties>0){
                setMasks(layerInfo.mask,layerOb);
                layerOb.hasMask = true;
            }
            layerOb.type = lType;
            if(lType == 'ShapeLayer'){
                ShapesParser.createShapes(layerInfo,layerOb, frameRate);
                layerOb.rectData = {l:0,t:0,b:0,r:0,w:0,h:0};
            }
            if(layerInfo.parent != null){
                layerOb.parent = layerInfo.parent.index - 1;
            }
            layerOb.layerName = layerInfo.name;
            layerOb.threeD = layerInfo.threeDLayer;
            layerOb.an = {};
            if(lType=='PreCompLayer'){
                layerOb.width = layerInfo.source.width;
                layerOb.height = layerInfo.source.height;
            }else if(lType == 'StillLayer'){
                addStillAsset(layerOb,layerInfo);
                layerOb.width = layerInfo.source.width;
                layerOb.height = layerInfo.source.height;
            }else if(lType == 'SolidLayer'){
                layerOb.width = layerInfo.source.width;
                layerOb.height = layerInfo.source.height;
                layerOb.color = extrasInstance.arrayRgbToHex(layerInfo.source.mainSource.color);
            }else if(lType == 'ShapeLayer'){
                layerOb.width = layerInfo.width;
                layerOb.height = layerInfo.height;
            }
            layerOb.inPoint = layerInfo.inPoint*frameRate;
            layerOb.outPoint = layerInfo.outPoint*frameRate;
            layerOb.startTime = layerInfo.startTime*frameRate;
            layerOb.lastData = {};
            layerOb.ks = {};
            if(layerInfo.transform.opacity.numKeys>1){
                extrasInstance.convertToBezierValues(layerInfo.transform.opacity, frameRate, layerOb.ks,'o');
            }else{
                layerOb.ks.o = extrasInstance.roundNumber(layerInfo.transform.opacity.valueAtTime(0,false),3);
            }
            if(layerInfo.transform.rotation.numKeys>1){
                extrasInstance.convertToBezierValues(layerInfo.transform.rotation, frameRate, layerOb.ks,'r');
            }else{
                layerOb.ks.r = extrasInstance.roundNumber(layerInfo.transform.rotation.valueAtTime(0,false),3);
            }
            if(layerInfo.transform.position.dimensionsSeparated){
                layerOb.ks.p = {s:true};
                extrasInstance.convertToBezierValues(layerInfo.transform['X Position'], frameRate, layerOb.ks.p,'x');
                extrasInstance.convertToBezierValues(layerInfo.transform['Y Position'], frameRate, layerOb.ks.p,'y');
            }else{
                if(layerInfo.transform.position.numKeys>1){
                    extrasInstance.convertToBezierValues(layerInfo.transform.position, frameRate, layerOb.ks,'p');
                }else{
                    layerOb.ks.p = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(0,false),3);
                }
            }
            if(layerInfo.transform['Anchor Point'].numKeys>1){
                if(lType == 'ShapeLayer'){
                    prepareHelperSolid(layerInfo.transform['Anchor Point'],frameRate,layerOb.ks,'a');
                }else{
                    extrasInstance.convertToBezierValues(layerInfo.transform['Anchor Point'], frameRate, layerOb.ks,'a');
                }
            }else{
                layerOb.ks.a = extrasInstance.roundNumber(layerInfo.transform['Anchor Point'].valueAtTime(0,false),3);
            }
            if(layerInfo.transform['Scale'].numKeys>1){
                extrasInstance.convertToBezierValues(layerInfo.transform['Scale'], frameRate, layerOb.ks,'s');
            }else{
                layerOb.ks.s = extrasInstance.roundNumber(layerInfo.transform['Scale'].valueAtTime(0,false),3);
            }
            if(layerInfo.canSetTimeRemapEnabled && layerInfo.timeRemapEnabled){
                extrasInstance.convertToBezierValues(layerInfo['Time Remap'], frameRate, layerOb,'tm');
            }
            checkLayerReady();
        }
    }
    function checkLayerReady(){
        if(renderCancelled){
            callback.apply();
        }else{
            extrasInstance.setTimeout(analyzeNextLayer,100);
        }
    }
    function prepareHelperSolid(property,frameRate,ob,param){
        var currentKeyframe = 1;
        var helperPosition = helperSolid.transform["Anchor Point"];
        var jLen = helperPosition.numKeys;
        while(jLen > 0){
            helperPosition.removeKey(1);
            jLen -= 1;
        }
        helperSolidComp.frameRate = frameRate;
        jLen = property.numKeys;
        var keyIn, keyOut;
        var keyInHelper, keyOutHelper;
        var propertyValueDelta, helperValueDelta;
        function adjustNextHelperSpeed(){
            var j;
            for(j = 0; j<jLen; j+=1){
                keyIn = property.keyInTemporalEase(j+1)[0];
                keyOut = property.keyOutTemporalEase(j+1)[0];
                keyInHelper = new KeyframeEase(keyIn.speed, keyIn.influence);
                keyOutHelper = new KeyframeEase(keyOut.speed, keyOut.influence);
                helperPosition.addKey(property.keyTime(j+1));
                helperPosition.setValueAtTime(property.keyTime(j+1),property.valueAtTime(property.keyTime(j+1),true));
                helperPosition.setTemporalEaseAtKey(j+1, [keyInHelper], [keyOutHelper]);
            }
            correctNextKey();
        }
        function correctNextKey(){
            var i= 0, len = 20;
            if(currentKeyframe !== jLen + 1){
                keyIn = property.keyInTemporalEase(currentKeyframe)[0];
                keyOut = property.keyOutTemporalEase(currentKeyframe)[0];
                propertyValueDelta = property.valueAtTime(property.keyTime(currentKeyframe)+0.01,false);
                keyOutHelper = helperPosition.keyOutTemporalEase(currentKeyframe);
                keyInHelper = helperPosition.keyInTemporalEase(currentKeyframe);
                var flag = true;
                var currentSpeed, deltaSpeed = 10, dir = 0;
                var helpValue,helpValue2;
                 if(currentKeyframe != 1){
                     helpValue = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe),false);
                     helpValue2 = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe-1),false);
                    propertyValueDelta = property.valueAtTime(property.keyTime(currentKeyframe)-0.01,false);
                    helperValueDelta = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe)-0.01,false);
                    currentSpeed = keyInHelper[0].speed;
                    deltaSpeed = Math.abs(keyInHelper[0].speed);
                    if(Math.abs(helperValueDelta[0]) > Math.abs(propertyValueDelta[0]) || Math.abs(helperValueDelta[1]) > Math.abs(propertyValueDelta[1])){
                        dir = 1;
                    }else{
                        dir = -1;
                    }
                    while(flag){
                        helpValue = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe),false);
                        helpValue2 = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe - 1),false);
                        helperValueDelta = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe)-0.01,false);
                        if(Math.abs(helperValueDelta[0]-propertyValueDelta[0]) < 0.001 && Math.abs(helperValueDelta[1]-propertyValueDelta[1]) < 0.001){
                            flag = false;
                        }else{
                            if(Math.abs(helperValueDelta[0]) > Math.abs(propertyValueDelta[0]) || Math.abs(helperValueDelta[1]) > Math.abs(propertyValueDelta[1])){
                                if(dir == 1){
                                    deltaSpeed /= 2;
                                }
                                dir = -1;
                                currentSpeed += deltaSpeed;
                                keyInHelper[0].speed = currentSpeed;
                                helperPosition.setTemporalEaseAtKey(currentKeyframe, keyInHelper, keyOutHelper);
                            }else{
                                if(dir == -1){
                                    deltaSpeed /= 2;
                                }
                                dir = 1;
                                currentSpeed -= deltaSpeed;
                                keyInHelper[0].speed = currentSpeed;
                                helperPosition.setTemporalEaseAtKey(currentKeyframe, keyInHelper, keyOutHelper);
                            }
                        }
                        i += 1;
                        if(i == len){
                            keyInHelper[0].speed = keyIn.speed;
                            helperPosition.setTemporalEaseAtKey(currentKeyframe, keyInHelper, keyOutHelper);
                            flag = false;
                        }
                    }
                }
                if(currentKeyframe != jLen){
                    i = 0;
                    flag = true;
                    propertyValueDelta = property.valueAtTime(property.keyTime(currentKeyframe)+0.01,false);
                    helperValueDelta = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe)+0.01,false);
                    helpValue = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe),false);
                    currentSpeed = keyOutHelper[0].speed;
                    deltaSpeed = Math.abs(keyOutHelper[0].speed);
                    if(Math.abs(helperValueDelta[0]) > Math.abs(propertyValueDelta[0]) || Math.abs(helperValueDelta[1]) > Math.abs(propertyValueDelta[1])){
                        dir = -1;
                    }else{
                        dir = 1;
                    }
                    while(flag){
                        helpValue = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe),false);
                        helperValueDelta = helperPosition.valueAtTime(helperPosition.keyTime(currentKeyframe)+0.01,false);
                        if(Math.abs(helperValueDelta[0]-propertyValueDelta[0]) < 0.001 && Math.abs(helperValueDelta[1]-propertyValueDelta[1]) < 0.001){
                            flag = false;
                        }else{
                            if(Math.abs(helperValueDelta[0]) > Math.abs(propertyValueDelta[0]) || Math.abs(helperValueDelta[1]) > Math.abs(propertyValueDelta[1]) ){
                                if(dir == -1){
                                    deltaSpeed /= 2;
                                }
                                dir = 1;
                                currentSpeed -= deltaSpeed;
                                keyOutHelper[0].speed = currentSpeed;
                                helperPosition.setTemporalEaseAtKey(currentKeyframe, keyInHelper, keyOutHelper);
                            }else{
                                if(dir == 1){
                                    deltaSpeed /= 2;
                                }
                                dir = -1;
                                currentSpeed += deltaSpeed;
                                keyOutHelper[0].speed = currentSpeed;
                                helperPosition.setTemporalEaseAtKey(currentKeyframe, keyInHelper, keyOutHelper);
                            }
                        }
                        i += 1;
                        if(i == len){
                            keyOutHelper[0].speed = keyOut.speed;
                            helperPosition.setTemporalEaseAtKey(currentKeyframe, keyInHelper, keyOutHelper);
                            flag = false;
                        }
                    }
                }
                currentKeyframe += 1;
                correctNextKey();
            }else{
                extrasInstance.convertToBezierValues(helperPosition, frameRate, ob,param);
            }
        }
        adjustNextHelperSpeed();
    }
    function createLayers(compo, layersData, frameRate){
        var i, len = compo.layers.length;
        var pendingType;
        for(i = 0;i<len;i++){
            var layerOb = {};
            layerOb.ind = i;
            var layerInfo = compo.layers[i+1];
            var lType = extrasInstance.layerType(layerInfo);
            //$.writeln('layerInfo.isTrackMatte: ',layerInfo.isTrackMatte);
            //$.writeln('layerInfo.hasTrackMatte: ',layerInfo.hasTrackMatte);
            layersData.push(layerOb);
            if(lType == 'AudioLayer' || lType == 'CameraLayer' || (layerInfo.enabled == false && !layerInfo.isTrackMatte)){
                //TODO add audios
                layerOb.enabled = false;
                continue;
            }
            if(layerInfo.hasTrackMatte){
                switch(layerInfo.trackMatteType){
                    case TrackMatteType.ALPHA:
                        pendingType = 1;
                        break;
                    case TrackMatteType.ALPHA_INVERTED:
                        pendingType = 2;
                        break;
                    case TrackMatteType.LUMA:
                        pendingType = 3;
                        break;
                    case TrackMatteType.LUMA_INVERTED :
                        pendingType = 4;
                        break;
                }
                layerOb.tt = pendingType;
            }else if(layerInfo.isTrackMatte){
                layerOb.td = 1;
            }
            pendingLayers.push({lInfo:layerInfo,lOb:layerOb,frameRate:frameRate});
            if(lType=='PreCompLayer'){
                var j = 0, jLen = exportedComps.length, isRendered = false;
                while(j<jLen){
                    if(exportedComps[j].lInfo.source == layerInfo.source){
                        isRendered = true;
                        break;
                    }
                    j+=1;
                }
                if(isRendered){
                    if(!exportedComps[j].lOb.compId){
                        exportedComps[j].lOb.compId = extrasInstance.getRandomName(7);
                    }
                    layerOb.refId = exportedComps[j].lOb.compId;
                }else{
                    layerOb.layers = [];
                    createLayers(layerInfo.source,layerOb.layers,layerInfo.source.frameRate);
                    exportedComps.push({
                        lInfo: layerInfo,
                        lOb: layerOb
                    })
                }
            }
        }
    }
    function getParentSize (name,layers){
        var i=0, len = layers.length;
        while(i<len){
            if(layers[i].layerName == name){
                return {width:layers[i].width,height:layers[i].height};
            }
            i++;
        }
        return {width:0,height:0};
    }
    function traverseAnimation(compo,layersData, frameNum, time){
        var i, len = compo.layers.length;
        for(i = 0;i<len;i++){
            var layerInfo = compo.layers[i+1];
            var lType = extrasInstance.layerType(layerInfo);
            if(lType == 'AudioLayer' || lType == 'CameraLayer' || (layerInfo.enabled == false && !layerInfo.isTrackMatte)){
                //TODO add audios
                continue;
            }
            var layerOb = layersData[i];
            var animData = {};
            if(layerOb.hasMask){
                addMasksToLayer(layerInfo,animData,time);
            }
            animData.tr = {};
            animData.tr.p = [];
            animData.tr.a = [];
            animData.tr.r = [];
            animData.tr.s = [];
            animData.tr.o = {};
            if(layerOb.parent != null){
                var parentSize = getParentSize(layerOb.parent,layersData);
                animData.tr.p[0] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[0],3);
                animData.tr.p[1] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[1],3);
            }else{
                animData.tr.p[0] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[0],3);
                animData.tr.p[1] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[1],3);
            }
            if(layerOb.threeD){
                animData.tr.p[2] = extrasInstance.roundNumber(-layerInfo.transform.position.valueAtTime(time,false)[2],3);
            }else{
                animData.tr.p[2] = -zCount;
                zCount++;
            }
            if(lType=='ShapeLayer'){
                var r = layerInfo.sourceRectAtTime(frameNum, false);
                layerOb.rectData.l = extrasInstance.roundNumber(Math.min(r.left,layerOb.rectData.l),3);
                layerOb.rectData.t = extrasInstance.roundNumber(Math.min(r.top,layerOb.rectData.t),3);
                layerOb.rectData.r = extrasInstance.roundNumber(Math.max(r.left+r.width,layerOb.rectData.r),3);
                layerOb.rectData.b = extrasInstance.roundNumber(Math.max(r.top+r.height,layerOb.rectData.b),3);
            }
            animData.tr.a[0] = extrasInstance.roundNumber(layerInfo.transform['Anchor Point'].valueAtTime(time,false)[0],3);
            animData.tr.a[1] = extrasInstance.roundNumber(layerInfo.transform['Anchor Point'].valueAtTime(time,false)[1],3);
            animData.tr.a[2] = extrasInstance.roundNumber(-layerInfo.transform['Anchor Point'].valueAtTime(time,false)[2],3);
            animData.tr.s = extrasInstance.roundNumber([(layerInfo.transform['Scale'].valueAtTime(time,false)[0]/100),(layerInfo.transform['Scale'].valueAtTime(time,false)[1]/100),(layerInfo.transform['Scale'].valueAtTime(time,false)[2]/100)],3);
            if(layerOb.threeD){
                animData.tr.r[0] = extrasInstance.roundNumber((layerInfo.transform['X Rotation'].valueAtTime(time,false)+layerInfo.transform['Orientation'].valueAtTime(time,false)[0])*Math.PI/180,3);
                animData.tr.r[1] = extrasInstance.roundNumber(-(layerInfo.transform['Y Rotation'].valueAtTime(time,false)+layerInfo.transform['Orientation'].valueAtTime(time,false)[1])*Math.PI/180,3);
                animData.tr.r[2] = extrasInstance.roundNumber((layerInfo.transform['Rotation'].valueAtTime(time,false)+layerInfo.transform['Orientation'].valueAtTime(time,false)[2])*Math.PI/180,3);
            }else{
                animData.tr.r[0] = 0;
                animData.tr.r[1] = 0;
                animData.tr.r[2] = extrasInstance.roundNumber(layerInfo.transform['Rotation'].valueAtTime(time,false)*Math.PI/180,3);
            }
            animData.tr.o = extrasInstance.roundNumber(layerInfo.transform['Opacity'].valueAtTime(time,false)/100,3);
            if(lType == 'ShapeLayer'){
                ShapesParser.addFrameData(layerInfo,layerOb, frameNum, time);
            }
            if(lType == 'PreCompLayer'){
                var compoInTime = -layerInfo.startTime;
                traverseAnimation(layerInfo.source,layerOb.layers, frameNum, time+compoInTime);
            }
            //THIS IS REPLACED WITH THE KEYFRAMES. LEAVE THIS FOR NOW.
            /*if(layerOb.lastData.an == null || extrasInstance.compareObjects(animData,layerOb.lastData.an)==false){
             layerOb.an[frameNum] = animData;
             layerOb.lastData.an = animData;
             }*/
            EffectsParser.renderFrame(layerOb,frameNum);
        }
    }
    function iterateComposition(){
        createLayers(mainComp, mainLayers, mainComp.frameRate);
        // TO TRAVERSE LAYER BY LAYER. NEEDED FOR TIME REMAP?
        /*renderCompo(mainComp, mainLayers);
         AssetsManager.createAssetsDataForExport();
         removeExtraData(mainLayers);
         processFinalData(mainLayers);
         callback.apply();*/
        // END TO TRAVERSE LAYER BY LAYER. NEEDED FOR TIME REMAP?
        totalLayers = pendingLayers.length;
        extrasInstance.setTimeout(analyzeNextLayer,100);
    }
    function iterateLayer(layerInfo, layerOb,frameRate){
        var duration =layerInfo.duration;
        layerOb.st = layerInfo.startTime;
        var frameNum = 0;
        var time = layerInfo.startTime;
        var lType = extrasInstance.layerType(layerInfo);
        if(lType == 'AudioLayer' || lType == 'CameraLayer' || (layerInfo.enabled == false && !layerInfo.isTrackMatte)){
            //TODO add audios
            return;
        }
        while(frameNum < duration*frameRate){
            var layerOb = layersData[i];
            var animData = {};
            if(layerOb.hasMask){
                addMasksToLayer(layerInfo,animData,time);
            }
            animData.tr = {};
            animData.tr.p = [];
            animData.tr.a = [];
            animData.tr.r = [];
            animData.tr.s = [];
            animData.tr.o = {};
            if(layerOb.parent != null){
                var parentSize = getParentSize(layerOb.parent,layersData);
                animData.tr.p[0] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[0],3);
                animData.tr.p[1] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[1],3);
            }else{
                animData.tr.p[0] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[0],3);
                animData.tr.p[1] = extrasInstance.roundNumber(layerInfo.transform.position.valueAtTime(time,false)[1],3);
            }
            if(layerOb.threeD){
                animData.tr.p[2] = extrasInstance.roundNumber(-layerInfo.transform.position.valueAtTime(time,false)[2],3);
            }else{
                animData.tr.p[2] = -zCount;
                zCount++;
            }
            if(lType=='ShapeLayer'){
                var r = layerInfo.sourceRectAtTime(frameNum, false);
                layerOb.rectData.l = extrasInstance.roundNumber(Math.min(r.left,layerOb.rectData.l),3);
                layerOb.rectData.t = extrasInstance.roundNumber(Math.min(r.top,layerOb.rectData.t),3);
                layerOb.rectData.r = extrasInstance.roundNumber(Math.max(r.left+r.width,layerOb.rectData.r),3);
                layerOb.rectData.b = extrasInstance.roundNumber(Math.max(r.top+r.height,layerOb.rectData.b),3);
            }
            animData.tr.a[0] = extrasInstance.roundNumber(layerInfo.transform['Anchor Point'].valueAtTime(time,false)[0],3);
            animData.tr.a[1] = extrasInstance.roundNumber(layerInfo.transform['Anchor Point'].valueAtTime(time,false)[1],3);
            animData.tr.a[2] = extrasInstance.roundNumber(-layerInfo.transform['Anchor Point'].valueAtTime(time,false)[2],3);
            animData.tr.s = extrasInstance.roundNumber([(layerInfo.transform['Scale'].valueAtTime(time,false)[0]/100),(layerInfo.transform['Scale'].valueAtTime(time,false)[1]/100),(layerInfo.transform['Scale'].valueAtTime(time,false)[2]/100)],3);
            if(layerOb.threeD){
                animData.tr.r[0] = extrasInstance.roundNumber((layerInfo.transform['X Rotation'].valueAtTime(time,false)+layerInfo.transform['Orientation'].valueAtTime(time,false)[0])*Math.PI/180,3);
                animData.tr.r[1] = extrasInstance.roundNumber(-(layerInfo.transform['Y Rotation'].valueAtTime(time,false)+layerInfo.transform['Orientation'].valueAtTime(time,false)[1])*Math.PI/180,3);
                animData.tr.r[2] = extrasInstance.roundNumber((layerInfo.transform['Rotation'].valueAtTime(time,false)+layerInfo.transform['Orientation'].valueAtTime(time,false)[2])*Math.PI/180,3);
            }else{
                animData.tr.r[0] = 0;
                animData.tr.r[1] = 0;
                animData.tr.r[2] = extrasInstance.roundNumber(layerInfo.transform['Rotation'].valueAtTime(time,false)*Math.PI/180,3);
            }
            animData.tr.o = extrasInstance.roundNumber(layerInfo.transform['Opacity'].valueAtTime(time,false)/100,3);
            if(lType == 'ShapeLayer'){
                ShapesParser.addFrameData(layerInfo,layerOb, frameNum, time);
            }
            //THIS IS REPLACED WITH THE KEYFRAMES. BUT SHOULD BE USED FOR EXPRESSION LAYERS.
            if(layerOb.lastData.an == null || extrasInstance.compareObjects(animData,layerOb.lastData.an)==false){
                layerOb.an[frameNum] = animData;
                layerOb.lastData.an = animData;
            }
            //END FOR EXPRESSION LAYERS
            EffectsParser.renderFrame(layerOb,frameNum);
            frameNum += 1;
            time += 1/frameRate;
        }
        //traverseAnimation(layerInfo.source,layerOb.layers, frameNum, time+compoInTime);
        if(lType == 'PreCompLayer'){
            var i, len = layerInfo.source.layers.length;
            for(i = 0;i<len;i++){
                iterateLayer(layerInfo.source.layers[i+1],layerOb.layers[i],layerInfo.source.frameRate);
            }
        }
    }
    function renderCompo(compo, mainLayers){
        //var duration = compo.duration;
        var i, len = compo.layers.length;
        for(i = 0;i<len;i++){
            iterateLayer(compo.layers[i+1],mainLayers[i],compo.frameRate);
        }
    }
    function renderNextFrame(){
        /*if(currentRenderFrame < totalFrames && renderCancelled === false){
            renderFrame();
            currentRenderFrame +=1;
            renderNextFrame();
            //extrasInstance.setTimeout(renderNextFrame,50);
        }else{
            isRenderReady = true;
            checkRenderReady();
        }*/
        isRenderReady = true;
        checkRenderReady();
    }
    function checkRenderReady(){
        if(AsyncManager.getAsyncCounter() == 0 && isRenderReady == true){
            AssetsManager.createAssetsDataForExport();
            removeExtraData(mainLayers);
            processFinalData(mainLayers);
            callback.apply();
        }
    }
    function renderFrame(){
        currentTime = (currentRenderFrame+firstFrame)/frameRate;
        zCount = 0;
        traverseAnimation(mainComp,mainLayers, currentRenderFrame,currentTime);
    }
    function setCallback(cb){
        callback = cb;
    }
    AsyncManager.setCallBack(checkRenderReady);
    var ob = {};
        ob.getCompositionAnimationData = getCompositionAnimationData;
        ob.setCallback = setCallback;
    DOMAnimationManager = ob;
}());
/****** END DOMAnimationMAnager ******/
/****** INIT Effects Parser ******/
(function(){
    var ob = {};
    var registeredEffects = {};
    function createEffects(layerInfo,layerOb){
        if(layerInfo.effect.numProperties>0){
            layerOb.eff = [];
            var i, len = layerInfo.effect.numProperties, name;
            for(i=0;i<len;i++){
                name = layerInfo.effect(i+1).name;
                if(registeredEffects[name] != null){
                    layerOb.eff.push({parser: registeredEffects[name], id:registeredEffects[name].registerElement(layerInfo.effect(i+1))});
                }
            }
        }
    }
    function renderFrame(layerOb,frameNum){
        if(layerOb.eff){
            layerOb.eff.forEach(function(item){
                item.parser.renderFrame(frameNum);
            });
        }
    }
    function saveEffectData(layerOb){
        if(layerOb.eff){
            layerOb.eff = layerOb.eff.map(function(item){
                return item.parser.getData(item.id);
            });
        }
    }
    function registerEffect(name,object){
        registeredEffects[name] = object;
    }
    ob.registerEffect = registerEffect;
    ob.createEffects = createEffects;
    ob.renderFrame = renderFrame;
    ob.saveEffectData = saveEffectData;
    EffectsParser = ob;
}());
/****** END Effects Parser ******/
/****** INIT Effects Stroke Parser ******/
(function(){
    var ob = {};
    var registeredElements = [];
    var lastValues = {};
    function renderFrame(frameNum, id){
        var effectData = registeredElements[id];
        var effectInfo = effectData.elem;
        for(var s in effectData.animated){
            var propertyValue = getPropertyValue(effectInfo[s].value,getPropertyType(s));
            if(lastValues[s] == null || !extrasInstance.compareObjects(propertyValue,lastValues[s])){
                effectData.animated[s][frameNum] = propertyValue;
                lastValues[s] = propertyValue;
            }
        }
    }
    function getPropertyValue(value,type){
        switch(type)
        {
            case 'color':
                return extrasInstance.arrayRgbToHex(value);
                break;
            default:
                return value;
                break;
        }
    }
    function getPropertyType(propertyName){
        var i = 0;len = animatableProperties.length;
        while(i<len){
            if(animatableProperties[i].name == propert