Merge branch '24_source_text'
diff --git a/extension/jsx/utils/textHelper.jsx b/extension/jsx/utils/textHelper.jsx
index f1cdffa..c5afbe8 100644
--- a/extension/jsx/utils/textHelper.jsx
+++ b/extension/jsx/utils/textHelper.jsx
@@ -23,49 +23,70 @@
         }
     }
     
-    function exportTextDocumentData(layerInfo, ob) {
-        
-        var textDocument = layerInfo.property("Source Text").value;
-        if (textDocument.boxText) {
-            ob.sz = textDocument.boxTextSize;
-            ob.ps = textDocument.boxTextPos;
+    function exportTextDocumentData(layerInfo, data, frameRate) {
+
+        var sourceTextProp = layerInfo.property("Source Text");
+        bm_expressionHelper.checkExpression(sourceTextProp, data);
+        var arr = [];
+        data.k = arr;
+        var numKeys = sourceTextProp.numKeys;
+        var j, jLen = numKeys ? numKeys : 1;
+        if(jLen === 0){
+            jLen = 1;
         }
-        var i, len;
-        ob.s = textDocument.fontSize;
-        ob.f = textDocument.font;
-        bm_sourceHelper.addFont(textDocument.font, textDocument.fontFamily, textDocument.fontStyle);
-        if(textDocument.allCaps){
-            ob.t = textDocument.text.toUpperCase();
-        } else {
-            ob.t = textDocument.text;
-        }
-        len = ob.t.length;
-        bm_textShapeHelper.addTextLayer(layerInfo);
-        ob.j = getJustification(textDocument.justification);
-        ob.tr = textDocument.tracking;
-        if(textDocument.baselineLocs && textDocument.baselineLocs.length > 5){
-            ob.lh = textDocument.baselineLocs[5] - textDocument.baselineLocs[1];
-        } else {
-            ob.lh = ob.s*1.2;
-        }
-        if (textDocument.applyFill) {
-            len = textDocument.fillColor.length;
-            ob.fc = [];
-            for (i = 0; i < len; i += 1) {
-                ob.fc[i] = Math.round(100*textDocument.fillColor[i])/100;
+        for(j=0;j<jLen;j+=1){
+            var ob = {};
+            var textDocument, time;
+            if(numKeys === 0){
+                time = 0;
+                textDocument = sourceTextProp.value;
+            } else {
+                time = sourceTextProp.keyTime(j + 1);
+                textDocument = sourceTextProp.keyValue(j + 1);
             }
-        }
-        if (textDocument.applyStroke) {
-            len = textDocument.strokeColor.length;
-            ob.sc = [];
-            for (i = 0; i < len; i += 1) {
-                ob.sc[i] = Math.round(100*textDocument.strokeColor[i])/100;
+            if (textDocument.boxText) {
+                ob.sz = textDocument.boxTextSize;
+                ob.ps = textDocument.boxTextPos;
             }
-            ob.sw = textDocument.strokeWidth;
+            var i, len;
+            ob.s = textDocument.fontSize;
+            ob.f = textDocument.font;
+            bm_sourceHelper.addFont(textDocument.font, textDocument.fontFamily, textDocument.fontStyle);
+            if(textDocument.allCaps){
+                ob.t = textDocument.text.toUpperCase();
+            } else {
+                ob.t = textDocument.text;
+            }
+            len = ob.t.length;
+            ob.j = getJustification(textDocument.justification);
+            ob.tr = textDocument.tracking;
+            if(textDocument.baselineLocs && textDocument.baselineLocs.length > 5){
+                ob.lh = textDocument.baselineLocs[5] - textDocument.baselineLocs[1];
+            } else {
+                ob.lh = ob.s*1.2;
+            }
             if (textDocument.applyFill) {
-                ob.of = textDocument.strokeOverFill;
+                len = textDocument.fillColor.length;
+                ob.fc = [];
+                for (i = 0; i < len; i += 1) {
+                    ob.fc[i] = Math.round(100*textDocument.fillColor[i])/100;
+                }
             }
+            if (textDocument.applyStroke) {
+                len = textDocument.strokeColor.length;
+                ob.sc = [];
+                for (i = 0; i < len; i += 1) {
+                    ob.sc[i] = Math.round(100*textDocument.strokeColor[i])/100;
+                }
+                ob.sw = textDocument.strokeWidth;
+                if (textDocument.applyFill) {
+                    ob.of = textDocument.strokeOverFill;
+                }
+            }
+            arr.push({s:ob,t:time*frameRate});
         }
+        bm_textShapeHelper.addTextLayer(layerInfo);
+
     }
     
     function exportTextPathData(pathOptions, ob, masksProperties, frameRate) {
@@ -103,7 +124,7 @@
             p: {},
             m: {}
         };
-        exportTextDocumentData(layerInfo, layerOb.t.d);
+        exportTextDocumentData(layerInfo, layerOb.t.d, frameRate);
         var textProperty = layerInfo.property("Text");
         
         var i, len = textProperty.numProperties;
diff --git a/extension/jsx/utils/textShapeHelper.jsx b/extension/jsx/utils/textShapeHelper.jsx
index 48be63d..1d27bb4 100644
--- a/extension/jsx/utils/textShapeHelper.jsx
+++ b/extension/jsx/utils/textShapeHelper.jsx
@@ -94,7 +94,7 @@
         resetProp(textLayer.transform.rotation, 0);
     }
     
-    function createNewChar(layerInfo, ch, charData) {
+    function createNewChar(layerInfo, originalTextDocument, ch, charData) {
             //"allCaps","applyFill","applyStroke","baselineLocs","baselineShift","boxText","boxTextPos","boxTextSize","fauxBold","fauxItalic","fillColor","font","fontFamily","fontLocation","fontSize","fontStyle","horizontalScale","justification","pointText","resetCharStyle","resetParagraphStyle","smallCaps","strokeColor","strokeOverFill","strokeWidth","subscript","superscript","text","tracking","tsume","verticalScale"
         if (ch.charCodeAt(0) === 13) {
             return;
@@ -103,7 +103,6 @@
         var l, lLen;
         var cmdID = bm_projectManager.getCommandID('shapesFromText');
         layerInfo.copyToComp(comp);
-        var originalTextDocument = layerInfo.property('Source Text').value;
         //var dupl = comp.layers[1];
         //var dupl = comp.layers.addText();
         //removeLayerAnimators(dupl);
@@ -162,40 +161,54 @@
     function exportChars(fonts) {
         
         comp.openInViewer();
-        var layerCollection = comp.layers;
         var i, len = layers.length, layerInfo;
+        var k, kLen;
         for (i = 0; i < len; i += 1) {
             layerInfo = layers[i];
             var textProp = layerInfo.property("Source Text");
-            var textDocument = textProp.value;
-            var font = textDocument.font;
-            var fontFamily = textDocument.fontFamily;
-            var fontStyle = textDocument.fontStyle;
-            var fontSize = textDocument.fontSize;
-            var text = textDocument.allCaps ? textDocument.text.toUpperCase() : textDocument.text;
-            var j, jLen = text.length;
-            
-            if (currentFont !== font) {
-                currentFont = font;
-                createNewChar(layerInfo, '[]', {});
+            kLen = textProp.numKeys;
+            var keysFlag = true;
+            if(kLen === 0){
+                kLen = 1;
+                keysFlag = false;
             }
-            var l, lLen;
-            for (j = 0; j < jLen; j += 1) {
-                var ch = text.substr(j, 1);
-                var charData = addChar(ch, fontSize, font, fontStyle);
-                if (charData !== false) {
-                    createNewChar(layerInfo, ch, charData);
-                    l = 0;
-                    lLen = fonts.list.length;
-                    while (l < lLen) {
-                        if (fonts.list[l].fName === charData.font) {
-                            charData.fFamily = fonts.list[l].fFamily;
-                            break;
+            var textDocument;
+            for(k=0;k<kLen;k+=1){
+                if(!keysFlag){
+                    textDocument = textProp.value;
+                } else {
+                    textDocument = textProp.keyValue(k + 1);
+                }
+                var font = textDocument.font;
+                var fontFamily = textDocument.fontFamily;
+                var fontStyle = textDocument.fontStyle;
+                var fontSize = textDocument.fontSize;
+                var text = textDocument.allCaps ? textDocument.text.toUpperCase() : textDocument.text;
+                var j, jLen = text.length;
+
+                if (currentFont !== font) {
+                    currentFont = font;
+                    createNewChar(layerInfo, textDocument, '[]', {});
+                }
+                var l, lLen;
+                for (j = 0; j < jLen; j += 1) {
+                    var ch = text.substr(j, 1);
+                    var charData = addChar(ch, fontSize, font, fontStyle);
+                    if (charData !== false) {
+                        createNewChar(layerInfo, textDocument, ch, charData);
+                        l = 0;
+                        lLen = fonts.list.length;
+                        while (l < lLen) {
+                            if (fonts.list[l].fName === charData.font) {
+                                charData.fFamily = fonts.list[l].fFamily;
+                                break;
+                            }
+                            l += 1;
                         }
-                        l += 1;
                     }
                 }
             }
+
         }
         
         bm_renderManager.setChars(chars);
diff --git a/player/index.html b/player/index.html
index 325ce15..0fb968d 100644
--- a/player/index.html
+++ b/player/index.html
@@ -11,9 +11,9 @@
         }
 
         #bodymovin{
-            background-color:#000;
-            width:100%;
-            height:100%;
+            background-color:#fff;
+            width:970px;
+            height:250px;
             /*width:800px;
             height:500px;*/
             display:block;
@@ -103,11 +103,11 @@
     var animData = {
         container: document.getElementById('bodymovin'),
         renderer: 'svg',
-        loop: false,
+        loop: true,
         autoplay: true,
         autoloadSegments: true,
         rendererSettings: {
-            progressiveLoad:true
+            progressiveLoad:false
         },
         path: 'exports/render/data.json'
     };
diff --git a/player/js/elements/TextElement.js b/player/js/elements/TextElement.js
index 647b1f3..a47cdf5 100644
--- a/player/js/elements/TextElement.js
+++ b/player/js/elements/TextElement.js
@@ -2,9 +2,10 @@
 }
 ITextElement.prototype.init = function(){
     this._parent.init.call(this);
+
     this.lettersChangedFlag = false;
+    this.currentTextDocumentData = {};
     var data = this.data;
-    this.renderedLetters = Array.apply(null,{length:data.t.d.l ? data.t.d.l.length : 0});
     this.viewData = {
         m:{
             a: PropertyFactory.getProp(this,data.t.m.a,1,0,this.dynamicProperties)
@@ -87,6 +88,25 @@
         this.maskPath = false;
     }
 };
+ITextElement.prototype.prepareFrame = function(num) {
+    var i = 0, len = this.data.t.d.k.length;
+    var textDocumentData = this.data.t.d.k[i].s;
+    i += 1;
+    while(i<len){
+        if(this.data.t.d.k[i].t > num){
+            break;
+        }
+        textDocumentData = this.data.t.d.k[i].s;
+        i += 1;
+    }
+    this.lettersChangedFlag = false;
+    if(textDocumentData !== this.currentTextDocumentData){
+        this.currentTextDocumentData = textDocumentData;
+        this.lettersChangedFlag = true;
+        this.buildNewText();
+    }
+    this._parent.prepareFrame.call(this, num);
+}
 
 ITextElement.prototype.createPathShape = function(matrixHelper, shapes) {
     var j,jLen = shapes.length;
@@ -114,7 +134,7 @@
     var data = this.data;
     var xPos,yPos;
     var i, len;
-    var documentData = data.t.d;
+    var documentData = this.currentTextDocumentData;
     var letters = documentData.l;
     if(this.maskPath) {
         var mask = this.viewData.p.m;
@@ -191,12 +211,11 @@
     len = letters.length;
     xPos = 0;
     yPos = 0;
-    var yOff = data.t.d.s*1.2*.714;
+    var yOff = documentData.s*1.2*.714;
     var firstLine = true;
     var renderedData = this.viewData, animatorProps, animatorSelector;
     var j, jLen;
     var lettersValue = Array.apply(null,{length:len}), letterValue;
-    this.lettersChangedFlag = false;
 
     jLen = renderedData.a.length;
     var lastLetter;
@@ -329,17 +348,17 @@
             }
             lineLength += letters[i].l/2;
             if(documentData.strokeWidthAnim) {
-                sw = data.t.d.sw || 0;
+                sw = documentData.sw || 0;
             }
             if(documentData.strokeColorAnim) {
-                if(data.t.d.sc){
-                    sc = [data.t.d.sc[0], data.t.d.sc[1], data.t.d.sc[2]];
+                if(documentData.sc){
+                    sc = [documentData.sc[0], documentData.sc[1], documentData.sc[2]];
                 }else{
                     sc = [0,0,0];
                 }
             }
             if(documentData.fillColorAnim) {
-                fc = [data.t.d.fc[0], data.t.d.fc[1], data.t.d.fc[2]];
+                fc = [documentData.fc[0], documentData.fc[1], documentData.fc[2]];
             }
             for(j=0;j<jLen;j+=1){
                 animatorProps = renderedData.a[j].a;
@@ -504,7 +523,7 @@
                 currentLength -= renderedData.m.a.v[0]*letters[i].an/200;
                 if(letters[i+1] && ind !== letters[i+1].ind){
                     currentLength += letters[i].an / 2;
-                    currentLength += documentData.tr/1000*data.t.d.s;
+                    currentLength += documentData.tr/1000*documentData.s;
                 }
             }else{
 
@@ -524,7 +543,7 @@
                 }
                 matrixHelper.translate(offf,0,0);
                 matrixHelper.translate(renderedData.m.a.v[0]*letters[i].an/200,renderedData.m.a.v[1]*yOff/100,0);
-                xPos += letters[i].l + documentData.tr/1000*data.t.d.s;
+                xPos += letters[i].l + documentData.tr/1000*documentData.s;
             }
             if(renderType === 'html'){
                 letterM = matrixHelper.toCSS();
@@ -550,7 +569,6 @@
                     letterValue = lastLetter;
                 }
             }
-
             this.renderedLetters[i] = letterValue;
         }
     }
diff --git a/player/js/elements/canvasElements/CVTextElement.js b/player/js/elements/canvasElements/CVTextElement.js
index 7a4947a..bdcf4fa 100644
--- a/player/js/elements/canvasElements/CVTextElement.js
+++ b/player/js/elements/canvasElements/CVTextElement.js
@@ -22,6 +22,7 @@
 CVTextElement.prototype.init = ITextElement.prototype.init;
 CVTextElement.prototype.getMeasures = ITextElement.prototype.getMeasures;
 CVTextElement.prototype.getMult = ITextElement.prototype.getMult;
+CVTextElement.prototype.prepareFrame = ITextElement.prototype.prepareFrame;
 
 CVTextElement.prototype.tHelper = document.createElement('canvas').getContext('2d');
 
@@ -29,7 +30,12 @@
 
     this._parent.createElements.call(this);
     //console.log('this.data: ',this.data);
-    var documentData = this.data.t.d;
+
+};
+
+CVTextElement.prototype.buildNewText = function(){
+    var documentData = this.currentTextDocumentData;
+    this.renderedLetters = Array.apply(null,{length:this.currentTextDocumentData.l ? this.currentTextDocumentData.l.length : 0});
 
     var hasFill = false;
     if(documentData.fc) {
@@ -57,6 +63,7 @@
     if (singleShape) {
         var xPos = 0, yPos = 0, lineWidths = documentData.lineWidths, boxWidth = documentData.boxWidth, firstLine = true;
     }
+    var cnt = 0;
     for (i = 0;i < len ;i += 1) {
         charData = this.globalData.fontManager.getCharData(documentData.t.charAt(i), fontData.fStyle, this.globalData.fontManager.getFontByName(documentData.f).fFamily);
         var shapeData;
@@ -111,9 +118,14 @@
         if(singleShape){
             xPos += letters[i].l;
         }
-        this.textSpans.push({elem: commands});
+        if(this.textSpans[cnt]){
+            this.textSpans[cnt].elem = commands;
+        } else {
+            this.textSpans[cnt] = {elem: commands};
+        }
+        cnt +=1;
     }
-};
+}
 
 CVTextElement.prototype.renderFrame = function(parentMatrix){
     if(this._parent.renderFrame.call(this, parentMatrix)===false){
@@ -136,7 +148,7 @@
     var  i,len, j, jLen, k, kLen;
     var renderedLetters = this.renderedLetters;
 
-    var letters = this.data.t.d.l;
+    var letters = this.currentTextDocumentData.l;
 
     len = letters.length;
     var renderedLetter;
diff --git a/player/js/elements/htmlElements/HShapeElement.js b/player/js/elements/htmlElements/HShapeElement.js
index 7fad36d..3f5fcce 100644
--- a/player/js/elements/htmlElements/HShapeElement.js
+++ b/player/js/elements/htmlElements/HShapeElement.js
@@ -75,15 +75,18 @@
 
     if(this.isVisible && (this.elemMdf || this.firstFrame)){
         var boundingBox = this.shapeCont.getBBox();
+        var changed = false;
         if(this.currentBBox.w !== boundingBox.width){
             this.currentBBox.w = boundingBox.width;
             this.shapeCont.setAttribute('width',boundingBox.width);
+            changed = true;
         }
         if(this.currentBBox.h !== boundingBox.height){
             this.currentBBox.h = boundingBox.height;
             this.shapeCont.setAttribute('height',boundingBox.height);
+            changed = true;
         }
-        if(this.currentBBox.w !== boundingBox.width || this.currentBBox.h !== boundingBox.height  || this.currentBBox.x !== boundingBox.x  || this.currentBBox.y !== boundingBox.y){
+        if(changed  || this.currentBBox.x !== boundingBox.x  || this.currentBBox.y !== boundingBox.y){
             this.currentBBox.w = boundingBox.width;
             this.currentBBox.h = boundingBox.height;
             this.currentBBox.x = boundingBox.x;
diff --git a/player/js/elements/htmlElements/HTextElement.js b/player/js/elements/htmlElements/HTextElement.js
index f249baf..d215057 100644
--- a/player/js/elements/htmlElements/HTextElement.js
+++ b/player/js/elements/htmlElements/HTextElement.js
@@ -17,10 +17,10 @@
 HTextElement.prototype.init = ITextElement.prototype.init;
 HTextElement.prototype.getMeasures = ITextElement.prototype.getMeasures;
 HTextElement.prototype.createPathShape = ITextElement.prototype.createPathShape;
+HTextElement.prototype.prepareFrame = ITextElement.prototype.prepareFrame;
 
 HTextElement.prototype.createElements = function(){
     this.isMasked = this.checkMasks();
-    var documentData = this.data.t.d;
     var parent = document.createElement('div');
     styleDiv(parent);
     this.layerElement = parent;
@@ -43,6 +43,12 @@
     }
     this.baseElement = parent;
 
+
+};
+
+HTextElement.prototype.buildNewText = function(){
+    var documentData = this.currentTextDocumentData;
+    this.renderedLetters = Array.apply(null,{length:this.currentTextDocumentData.l ? this.currentTextDocumentData.l.length : 0});
     if(documentData.fc) {
         this.innerElem.style.color = this.innerElem.style.fill = 'rgb(' + Math.round(documentData.fc[0]*255) + ',' + Math.round(documentData.fc[1]*255) + ',' + Math.round(documentData.fc[2]*255) + ')';
         ////this.innerElem.setAttribute('fill', 'rgb(' + documentData.fc[0] + ',' + documentData.fc[1] + ',' + documentData.fc[2] + ')');
@@ -75,34 +81,48 @@
     }
     var i, len;
 
-
-
     var letters = documentData.l;
     len = letters.length;
     var tSpan,tParent,tCont;
     var matrixHelper = this.mHelper;
     var shapes, shapeStr = '';
+    var cnt = 0;
     for (i = 0;i < len ;i += 1) {
         if(this.globalData.fontManager.chars){
-            tSpan = document.createElementNS(svgNS,'path');
-            if(!this.isMasked){
-                tParent = document.createElement('div');
-                tCont = document.createElementNS(svgNS,'svg');
-                tCont.appendChild(tSpan);
-                styleDiv(tParent);
+            if(!this.textPaths[cnt]){
+                tSpan = document.createElementNS(svgNS,'path');
+                tSpan.setAttribute('stroke-linecap', 'butt');
+                tSpan.setAttribute('stroke-linejoin','round');
+                tSpan.setAttribute('stroke-miterlimit','4');
+            } else {
+                tSpan = this.textPaths[cnt];
             }
-            tSpan.setAttribute('stroke-linecap', 'butt');
-            tSpan.setAttribute('stroke-linejoin','round');
-            tSpan.setAttribute('stroke-miterlimit','4');
+            if(!this.isMasked){
+                if(this.textSpans[cnt]){
+                    tParent = this.textSpans[cnt];
+                    tCont = tParent.children()[0];
+                } else {
+
+                    tParent = document.createElement('div');
+                    tCont = document.createElementNS(svgNS,'svg');
+                    tCont.appendChild(tSpan);
+                    styleDiv(tParent);
+                }
+            }
         }else{
             if(!this.isMasked){
-                tParent = document.createElement('span');
-                styleDiv(tParent);
-                tSpan = document.createElement('span');
-                styleDiv(tSpan);
-                tParent.appendChild(tSpan);
+                if(this.textSpans[cnt]){
+                    tParent = this.textSpans[cnt];
+                    tSpan = this.textPaths[cnt];
+                } else {
+                    tParent = document.createElement('span');
+                    styleDiv(tParent);
+                    tSpan = document.createElement('span');
+                    styleDiv(tSpan);
+                    tParent.appendChild(tSpan);
+                }
             } else {
-                tSpan = document.createElementNS(svgNS,'text');
+                tSpan = this.textPaths[cnt] ? this.textPaths[cnt] : document.createElementNS(svgNS,'text');
             }
         }
         //tSpan.setAttribute('visibility', 'hidden');
@@ -135,16 +155,6 @@
                     letters[i].yOffset = boundingBox.y;
                     tParent.appendChild(tCont);
 
-
-                    /*var rBound = Math.ceil(shapeData.bounds.r*scale);
-                    var tBound = Math.floor(shapeData.bounds.t*scale);
-                    var lBound = Math.floor(shapeData.bounds.l*scale);
-                    var bBound = Math.ceil(shapeData.bounds.b*scale);
-                    tCont.setAttribute('width',rBound - lBound);
-                    tCont.setAttribute('height',bBound - tBound);
-                    tCont.setAttribute('viewBox',lBound+' '+tBound+' '+(rBound - lBound)+' '+(bBound - tBound));
-                    tCont.style.transform = tCont.style.webkitTransform = 'translate('+lBound+'px,'+ tBound+'px)';
-                    letters[i].yOffset = tBound;*/
                 } else{
                     tCont.setAttribute('width',1);
                     tCont.setAttribute('height',1);
@@ -165,13 +175,18 @@
         }
         //
         if(!this.isMasked){
-            this.textSpans.push(tParent);
+            this.textSpans[cnt] = tParent;
         }else{
-            this.textSpans.push(tSpan);
+            this.textSpans[cnt] = tSpan;
         }
-        this.textPaths.push(tSpan);
+        this.textPaths[cnt] = tSpan;
+        cnt += 1;
     }
-};
+    while(cnt < this.textSpans.length){
+        this.textSpans[cnt].style.display = 'none';
+        cnt += 1;
+    }
+}
 
 HTextElement.prototype.hide = SVGTextElement.prototype.hide;
 
@@ -185,10 +200,11 @@
     if(this.hidden){
         this.hidden = false;
         this.innerElem.style.display = 'block';
+        this.layerElement.style.display = 'block';
     }
 
     if(this.data.singleShape){
-        if(!this.firstFrame){
+        if(!this.firstFrame && !this.lettersChangedFlag){
             return;
         } else {
             // Todo Benchmark if using this is better than getBBox
@@ -206,7 +222,7 @@
     var  i,len;
     var renderedLetters = this.renderedLetters;
 
-    var letters = this.data.t.d.l;
+    var letters = this.currentTextDocumentData.l;
 
     len = letters.length;
     var renderedLetter;
diff --git a/player/js/elements/svgElements/SVGTextElement.js b/player/js/elements/svgElements/SVGTextElement.js
index 4f3a47e..e63c50c 100644
--- a/player/js/elements/svgElements/SVGTextElement.js
+++ b/player/js/elements/svgElements/SVGTextElement.js
@@ -8,12 +8,26 @@
 SVGTextElement.prototype.init = ITextElement.prototype.init;
 SVGTextElement.prototype.createPathShape = ITextElement.prototype.createPathShape;
 SVGTextElement.prototype.getMeasures = ITextElement.prototype.getMeasures;
+SVGTextElement.prototype.prepareFrame = ITextElement.prototype.prepareFrame;
 
 SVGTextElement.prototype.createElements = function(){
 
     this._parent.createElements.call(this);
-    var documentData = this.data.t.d;
 
+
+    if(this.data.ln){
+        this.layerElement.setAttribute('id',this.data.ln);
+    }
+    if(this.data.cl){
+        this.layerElement.setAttribute('class',this.data.cl);
+    }
+};
+
+SVGTextElement.prototype.buildNewText = function(){
+    var i, len;
+
+    var documentData = this.currentTextDocumentData;
+    this.renderedLetters = Array.apply(null,{length:this.currentTextDocumentData.l ? this.currentTextDocumentData.l.length : 0});
     if(documentData.fc) {
         this.layerElement.setAttribute('fill', 'rgb(' + Math.round(documentData.fc[0]*255) + ',' + Math.round(documentData.fc[1]*255) + ',' + Math.round(documentData.fc[2]*255) + ')');
     }else{
@@ -33,7 +47,6 @@
         this.layerElement.setAttribute('font-style', fStyle);
         this.layerElement.setAttribute('font-weight', fWeight);
     }
-    var i, len;
 
 
 
@@ -48,14 +61,16 @@
     if (singleShape) {
         var xPos = 0, yPos = 0, lineWidths = documentData.lineWidths, boxWidth = documentData.boxWidth, firstLine = true;
     }
+    var cnt = 0;
     for (i = 0;i < len ;i += 1) {
         if(this.globalData.fontManager.chars){
             if(!singleShape || i === 0){
-                tSpan = document.createElementNS(svgNS,'path');
+                tSpan = this.textSpans[cnt] ? this.textSpans[cnt] : document.createElementNS(svgNS,'path');
             }
         }else{
-            tSpan = document.createElementNS(svgNS,'text');
+            tSpan = this.textSpans[cnt] ? this.textSpans[cnt] : document.createElementNS(svgNS,'text');
         }
+        tSpan.style.display = 'inherit';
         tSpan.setAttribute('stroke-linecap', 'butt');
         tSpan.setAttribute('stroke-linejoin','round');
         tSpan.setAttribute('stroke-miterlimit','4');
@@ -118,19 +133,18 @@
             xPos += letters[i].l;
         }
         //
-        this.textSpans.push(tSpan);
+        this.textSpans[cnt] = tSpan;
+        cnt += 1;
     }
-    if(this.data.ln){
-        this.layerElement.setAttribute('id',this.data.ln);
-    }
-    if(this.data.cl){
-        this.layerElement.setAttribute('class',this.data.cl);
+    while(cnt < this.textSpans.length){
+        this.textSpans[cnt].style.display = 'none';
+        cnt += 1;
     }
     if(singleShape && this.globalData.fontManager.chars){
         tSpan.setAttribute('d',shapeStr);
         this.layerElement.appendChild(tSpan);
     }
-};
+}
 
 SVGTextElement.prototype.hide = function(){
     if(!this.hidden){
@@ -154,7 +168,6 @@
     if(this.data.singleShape){
         return;
     }
-
     this.getMeasures();
     if(!this.lettersChangedFlag){
         return;
@@ -162,7 +175,7 @@
     var  i,len;
     var renderedLetters = this.renderedLetters;
 
-    var letters = this.data.t.d.l;
+    var letters = this.currentTextDocumentData.l;
 
     len = letters.length;
     var renderedLetter;
diff --git a/player/js/utils/DataManager.js b/player/js/utils/DataManager.js
index f60d44c..07e0b6e 100644
--- a/player/js/utils/DataManager.js
+++ b/player/js/utils/DataManager.js
@@ -125,6 +125,46 @@
         }
     }
 
+    var checkText = (function(){
+        var minimumVersion = [4,4,14];
+
+        function updateTextLayer(textLayer){
+            var documentData = textLayer.t.d;
+            textLayer.t.d = {
+                k: [
+                    {
+                        s:documentData,
+                        t:0
+                    }
+                ]
+            }
+        }
+
+        function iterateLayers(layers){
+            var i, len = layers.length;
+            for(i=0;i<len;i+=1){
+                if(layers[i].ty === 5){
+                    updateTextLayer(layers[i]);
+                }
+            }
+        }
+
+        return function (animationData){
+            if(checkVersion(minimumVersion,animationData.v)){
+                iterateLayers(animationData.layers);
+                if(animationData.assets){
+                    var i, len = animationData.assets.length;
+                    for(i=0;i<len;i+=1){
+                        if(animationData.assets[i].layers){
+                            iterateLayers(animationData.assets[i].layers);
+
+                        }
+                    }
+                }
+            }
+        }
+    }())
+
     var checkColors = (function(){
         var minimumVersion = [4,1,9];
 
@@ -342,206 +382,213 @@
             return;
         }
         checkColors(animationData);
+        checkText(animationData);
         completeLayers(animationData.layers, animationData.assets, fontManager);
         animationData.__complete = true;
         //blitAnimation(animationData, animationData.assets, fontManager);
     }
 
     function completeText(data, fontManager){
-        var letters = [];
-        var documentData = data.t.d;
-        var i, len;
-        var newLineFlag, index = 0, val;
-        var anchorGrouping = data.t.m.g;
-        var currentSize = 0, currentPos = 0, currentLine = 0, lineWidths = [];
-        var lineWidth = 0;
-        var maxLineWidth = 0;
-        var j, jLen;
-        var fontData = fontManager.getFontByName(documentData.f);
-        var charData, cLength = 0;
-        var styles = fontData.fStyle.split(' ');
+        var letters;
+        var keys = data.t.d.k;
+        var k, kLen = keys.length;
+        for(k=0;k<kLen;k+=1){
+            var documentData = data.t.d.k[k].s;
+            letters = [];
+            var i, len;
+            var newLineFlag, index = 0, val;
+            var anchorGrouping = data.t.m.g;
+            var currentSize = 0, currentPos = 0, currentLine = 0, lineWidths = [];
+            var lineWidth = 0;
+            var maxLineWidth = 0;
+            var j, jLen;
+            var fontData = fontManager.getFontByName(documentData.f);
+            var charData, cLength = 0;
+            var styles = fontData.fStyle.split(' ');
 
-        var fWeight = 'normal', fStyle = 'normal';
-        len = styles.length;
-        for(i=0;i<len;i+=1){
-            if (styles[i].toLowerCase() === 'italic') {
-                fStyle = 'italic';
-            }else if (styles[i].toLowerCase() === 'bold') {
-                fWeight = '700';
-            } else if (styles[i].toLowerCase() === 'black') {
-                fWeight = '900';
-            } else if (styles[i].toLowerCase() === 'medium') {
-                fWeight = '500';
-            } else if (styles[i].toLowerCase() === 'regular' || styles[i].toLowerCase() === 'normal') {
-                fWeight = '400';
-            } else if (styles[i].toLowerCase() === 'light' || styles[i].toLowerCase() === 'thin') {
-                fWeight = '200';
-            }
-        }
-        documentData.fWeight = fWeight;
-        documentData.fStyle = fStyle;
-        len = documentData.t.length;
-        if(documentData.sz){
-            var boxWidth = documentData.sz[0];
-            var lastSpaceIndex = -1;
+            var fWeight = 'normal', fStyle = 'normal';
+            len = styles.length;
             for(i=0;i<len;i+=1){
+                if (styles[i].toLowerCase() === 'italic') {
+                    fStyle = 'italic';
+                }else if (styles[i].toLowerCase() === 'bold') {
+                    fWeight = '700';
+                } else if (styles[i].toLowerCase() === 'black') {
+                    fWeight = '900';
+                } else if (styles[i].toLowerCase() === 'medium') {
+                    fWeight = '500';
+                } else if (styles[i].toLowerCase() === 'regular' || styles[i].toLowerCase() === 'normal') {
+                    fWeight = '400';
+                } else if (styles[i].toLowerCase() === 'light' || styles[i].toLowerCase() === 'thin') {
+                    fWeight = '200';
+                }
+            }
+            documentData.fWeight = fWeight;
+            documentData.fStyle = fStyle;
+            len = documentData.t.length;
+            if(documentData.sz){
+                var boxWidth = documentData.sz[0];
+                var lastSpaceIndex = -1;
+                for(i=0;i<len;i+=1){
+                    newLineFlag = false;
+                    if(documentData.t.charAt(i) === ' '){
+                        lastSpaceIndex = i;
+                    }else if(documentData.t.charCodeAt(i) === 13){
+                        lineWidth = 0;
+                        newLineFlag = true;
+                    }
+                    if(fontManager.chars){
+                        charData = fontManager.getCharData(documentData.t.charAt(i), fontData.fStyle, fontData.fFamily);
+                        cLength = newLineFlag ? 0 : charData.w*documentData.s/100;
+                    }else{
+                        //tCanvasHelper.font = documentData.s + 'px '+ fontData.fFamily;
+                        cLength = fontManager.measureText(documentData.t.charAt(i), documentData.f, documentData.s);
+                    }
+                    if(lineWidth + cLength > boxWidth){
+                        if(lastSpaceIndex === -1){
+                            //i -= 1;
+                            documentData.t = documentData.t.substr(0,i) + "\r" + documentData.t.substr(i);
+                            len += 1;
+                        } else {
+                            i = lastSpaceIndex;
+                            documentData.t = documentData.t.substr(0,i) + "\r" + documentData.t.substr(i+1);
+                        }
+                        lastSpaceIndex = -1;
+                        lineWidth = 0;
+                    }else {
+                        lineWidth += cLength;
+                    }
+                }
+                len = documentData.t.length;
+            }
+            lineWidth = 0;
+            cLength = 0;
+            for (i = 0;i < len ;i += 1) {
                 newLineFlag = false;
                 if(documentData.t.charAt(i) === ' '){
-                    lastSpaceIndex = i;
+                    val = '\u00A0';
                 }else if(documentData.t.charCodeAt(i) === 13){
+                    lineWidths.push(lineWidth);
+                    maxLineWidth = lineWidth > maxLineWidth ? lineWidth : maxLineWidth;
                     lineWidth = 0;
+                    val = '';
                     newLineFlag = true;
+                    currentLine += 1;
+                }else{
+                    val = documentData.t.charAt(i);
                 }
                 if(fontManager.chars){
-                    charData = fontManager.getCharData(documentData.t.charAt(i), fontData.fStyle, fontData.fFamily);
+                    charData = fontManager.getCharData(documentData.t.charAt(i), fontData.fStyle, fontManager.getFontByName(documentData.f).fFamily);
                     cLength = newLineFlag ? 0 : charData.w*documentData.s/100;
                 }else{
-                    //tCanvasHelper.font = documentData.s + 'px '+ fontData.fFamily;
-                    cLength = fontManager.measureText(documentData.t.charAt(i), documentData.f, documentData.s);
+                    //var charWidth = fontManager.measureText(val, documentData.f, documentData.s);
+                    //tCanvasHelper.font = documentData.s + 'px '+ fontManager.getFontByName(documentData.f).fFamily;
+                    cLength = fontManager.measureText(val, documentData.f, documentData.s);
                 }
-                if(lineWidth + cLength > boxWidth){
-                    if(lastSpaceIndex === -1){
-                       //i -= 1;
-                        documentData.t = documentData.t.substr(0,i) + "\r" + documentData.t.substr(i);
-                        len += 1;
-                    } else {
-                        i = lastSpaceIndex;
-                        documentData.t = documentData.t.substr(0,i) + "\r" + documentData.t.substr(i+1);
-                    }
-                    lastSpaceIndex = -1;
-                    lineWidth = 0;
-                }else {
-                    lineWidth += cLength;
-                }
-            }
-            len = documentData.t.length;
-        }
-        lineWidth = 0;
-        cLength = 0;
-        for (i = 0;i < len ;i += 1) {
-            newLineFlag = false;
-            if(documentData.t.charAt(i) === ' '){
-                val = '\u00A0';
-            }else if(documentData.t.charCodeAt(i) === 13){
-                lineWidths.push(lineWidth);
-                maxLineWidth = lineWidth > maxLineWidth ? lineWidth : maxLineWidth;
-                lineWidth = 0;
-                val = '';
-                newLineFlag = true;
-                currentLine += 1;
-            }else{
-                val = documentData.t.charAt(i);
-            }
-            if(fontManager.chars){
-                charData = fontManager.getCharData(documentData.t.charAt(i), fontData.fStyle, fontManager.getFontByName(documentData.f).fFamily);
-                cLength = newLineFlag ? 0 : charData.w*documentData.s/100;
-            }else{
-                //var charWidth = fontManager.measureText(val, documentData.f, documentData.s);
-                //tCanvasHelper.font = documentData.s + 'px '+ fontManager.getFontByName(documentData.f).fFamily;
-                cLength = fontManager.measureText(val, documentData.f, documentData.s);
-            }
 
-            //
-            lineWidth += cLength;
-            letters.push({l:cLength,an:cLength,add:currentSize,n:newLineFlag, anIndexes:[], val: val, line: currentLine});
-            if(anchorGrouping == 2){
-                currentSize += cLength;
-                if(val == '' || val == '\u00A0' || i == len - 1){
-                    if(val == '' || val == '\u00A0'){
-                        currentSize -= cLength;
+                //
+                lineWidth += cLength;
+                letters.push({l:cLength,an:cLength,add:currentSize,n:newLineFlag, anIndexes:[], val: val, line: currentLine});
+                if(anchorGrouping == 2){
+                    currentSize += cLength;
+                    if(val == '' || val == '\u00A0' || i == len - 1){
+                        if(val == '' || val == '\u00A0'){
+                            currentSize -= cLength;
+                        }
+                        while(currentPos<=i){
+                            letters[currentPos].an = currentSize;
+                            letters[currentPos].ind = index;
+                            letters[currentPos].extra = cLength;
+                            currentPos += 1;
+                        }
+                        index += 1;
+                        currentSize = 0;
                     }
-                    while(currentPos<=i){
-                        letters[currentPos].an = currentSize;
-                        letters[currentPos].ind = index;
-                        letters[currentPos].extra = cLength;
-                        currentPos += 1;
+                }else if(anchorGrouping == 3){
+                    currentSize += cLength;
+                    if(val == '' || i == len - 1){
+                        if(val == ''){
+                            currentSize -= cLength;
+                        }
+                        while(currentPos<=i){
+                            letters[currentPos].an = currentSize;
+                            letters[currentPos].ind = index;
+                            letters[currentPos].extra = cLength;
+                            currentPos += 1;
+                        }
+                        currentSize = 0;
+                        index += 1;
                     }
-                    index += 1;
-                    currentSize = 0;
-                }
-            }else if(anchorGrouping == 3){
-                currentSize += cLength;
-                if(val == '' || i == len - 1){
-                    if(val == ''){
-                        currentSize -= cLength;
-                    }
-                    while(currentPos<=i){
-                        letters[currentPos].an = currentSize;
-                        letters[currentPos].ind = index;
-                        letters[currentPos].extra = cLength;
-                        currentPos += 1;
-                    }
-                    currentSize = 0;
+                }else{
+                    letters[index].ind = index;
+                    letters[index].extra = 0;
                     index += 1;
                 }
+            }
+            documentData.l = letters;
+            maxLineWidth = lineWidth > maxLineWidth ? lineWidth : maxLineWidth;
+            lineWidths.push(lineWidth);
+            if(documentData.sz){
+                documentData.boxWidth = documentData.sz[0];
+                documentData.justifyOffset = 0;
             }else{
-                letters[index].ind = index;
-                letters[index].extra = 0;
-                index += 1;
+                documentData.boxWidth = maxLineWidth;
+                switch(documentData.j){
+                    case 1:
+                        documentData.justifyOffset = - documentData.boxWidth;
+                        break;
+                    case 2:
+                        documentData.justifyOffset = - documentData.boxWidth/2;
+                        break;
+                    default:
+                        documentData.justifyOffset = 0;
+                }
             }
-        }
-        documentData.l = letters;
-        maxLineWidth = lineWidth > maxLineWidth ? lineWidth : maxLineWidth;
-        lineWidths.push(lineWidth);
-        if(documentData.sz){
-            documentData.boxWidth = documentData.sz[0];
-            data.t.d.justifyOffset = 0;
-        }else{
-            documentData.boxWidth = maxLineWidth;
-            switch(documentData.j){
-                case 1:
-                    data.t.d.justifyOffset = - documentData.boxWidth;
-                    break;
-                case 2:
-                    data.t.d.justifyOffset = - documentData.boxWidth/2;
-                    break;
-                default:
-                    data.t.d.justifyOffset = 0;
-            }
-        }
-        documentData.lineWidths = lineWidths;
+            documentData.lineWidths = lineWidths;
 
-        var animators = data.t.a;
-        jLen = animators.length;
-        var based, ind, indexes = [];
-        for(j=0;j<jLen;j+=1){
-            if(animators[j].a.sc){
-                documentData.strokeColorAnim = true;
-            }
-            if(animators[j].a.sw){
-                documentData.strokeWidthAnim = true;
-            }
-            if(animators[j].a.fc || animators[j].a.fh || animators[j].a.fs || animators[j].a.fb){
-                documentData.fillColorAnim = true;
-            }
-            ind = 0;
-            based = animators[j].s.b;
-            for(i=0;i<len;i+=1){
-                letters[i].anIndexes[j] = ind;
-                if((based == 1 && letters[i].val != '') || (based == 2 && letters[i].val != '' && letters[i].val != '\u00A0') || (based == 3 && (letters[i].n || letters[i].val == '\u00A0' || i == len - 1)) || (based == 4 && (letters[i].n || i == len - 1))){
-                    if(animators[j].s.rn === 1){
-                        indexes.push(ind);
+            var animators = data.t.a;
+            jLen = animators.length;
+            var based, ind, indexes = [];
+            for(j=0;j<jLen;j+=1){
+                if(animators[j].a.sc){
+                    documentData.strokeColorAnim = true;
+                }
+                if(animators[j].a.sw){
+                    documentData.strokeWidthAnim = true;
+                }
+                if(animators[j].a.fc || animators[j].a.fh || animators[j].a.fs || animators[j].a.fb){
+                    documentData.fillColorAnim = true;
+                }
+                ind = 0;
+                based = animators[j].s.b;
+                for(i=0;i<len;i+=1){
+                    letters[i].anIndexes[j] = ind;
+                    if((based == 1 && letters[i].val != '') || (based == 2 && letters[i].val != '' && letters[i].val != '\u00A0') || (based == 3 && (letters[i].n || letters[i].val == '\u00A0' || i == len - 1)) || (based == 4 && (letters[i].n || i == len - 1))){
+                        if(animators[j].s.rn === 1){
+                            indexes.push(ind);
+                        }
+                        ind += 1;
                     }
-                    ind += 1;
+                }
+                data.t.a[j].s.totalChars = ind;
+                var currentInd = -1, newInd;
+                if(animators[j].s.rn === 1){
+                    for(i = 0; i < len; i += 1){
+                        if(currentInd != letters[i].anIndexes[j]){
+                            currentInd = letters[i].anIndexes[j];
+                            newInd = indexes.splice(Math.floor(Math.random()*indexes.length),1)[0];
+                        }
+                        letters[i].anIndexes[j] = newInd;
+                    }
                 }
             }
-            data.t.a[j].s.totalChars = ind;
-            var currentInd = -1, newInd;
-            if(animators[j].s.rn === 1){
-                for(i = 0; i < len; i += 1){
-                    if(currentInd != letters[i].anIndexes[j]){
-                        currentInd = letters[i].anIndexes[j];
-                        newInd = indexes.splice(Math.floor(Math.random()*indexes.length),1)[0];
-                    }
-                    letters[i].anIndexes[j] = newInd;
-                }
+            if(jLen === 0 && !('m' in data.t.p)){
+                data.singleShape = true;
             }
+            documentData.yOffset = documentData.lh || documentData.s*1.2;
+            documentData.ascent = fontData.ascent*documentData.s/100;
         }
-        if(jLen === 0 && !('m' in data.t.p)){
-            data.singleShape = true;
-        }
-        documentData.yOffset = documentData.lh || documentData.s*1.2;
-        documentData.ascent = fontData.ascent*documentData.s/100;
+
     }
 
     var moduleOb = {};
diff --git a/player/js/utils/PropertyFactory.js b/player/js/utils/PropertyFactory.js
index 33a6d82..52894a0 100644
--- a/player/js/utils/PropertyFactory.js
+++ b/player/js/utils/PropertyFactory.js
@@ -758,23 +758,35 @@
                     if(mult<.5){
                         mult *= 2;
                     }else{
-                        mult = 1 - mult;
+                        mult = 1 - 2*(mult-0.5);
                     }
                 }
+                mult = easer(mult);
             }else if(type == 5){
                 if(e === s){
                     mult = ind >= e ? 0 : 1;
                 }else{
                     var tot = e - s;
-
-                    mult = -4/(tot*tot)*(ind*ind)+(4/tot)*ind;
+                    /*ind += 0.5;
+                    mult = -4/(tot*tot)*(ind*ind)+(4/tot)*ind;*/
+                    ind = min(max(0,ind+0.5-s),e-s);
+                    var x = -tot/2+ind;
+                    var a = tot/2;
+                    mult = Math.sqrt(1 - (x*x)/(a*a));
                 }
+                mult = easer(mult);
             }else if(type == 6){
                 if(e === s){
                     mult = ind >= e ? 0 : 1;
                 }else{
-                    mult = (1+(Math.cos(Math.PI+Math.PI*2*(ind-s)/(e-s))+0))/2;
+                    ind = min(max(0,ind+0.5-s),e-s);
+                    mult = (1+(Math.cos((Math.PI+Math.PI*2*(ind)/(e-s)))))/2;
+                    /*
+                     ind = Math.min(Math.max(s,ind),e-1);
+                     mult = (1+(Math.cos((Math.PI+Math.PI*2*(ind-s)/(e-1-s)))))/2;
+                     mult = Math.max(mult,(1/(e-1-s))/(e-1-s));*/
                 }
+                mult = easer(mult);
             }else {
                 if(ind >= floor(s)){
                     if(ind-s < 0){
@@ -783,6 +795,7 @@
                         mult = max(0,min(e-ind,1));
                     }
                 }
+                mult = easer(mult);
             }
             return mult*this.a.v;
         }
diff --git a/player/js/utils/common.js b/player/js/utils/common.js
index 90741a7..1a7090b 100644
--- a/player/js/utils/common.js
+++ b/player/js/utils/common.js
@@ -1,4 +1,4 @@
-var subframeEnabled = true;
+var subframeEnabled = false;
 var expressionsPlugin;
 var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
 var cachedColors = {};
diff --git a/player/js/utils/expressions/ShapeInterface.js b/player/js/utils/expressions/ShapeInterface.js
index f237fe7..17f0093 100644
--- a/player/js/utils/expressions/ShapeInterface.js
+++ b/player/js/utils/expressions/ShapeInterface.js
@@ -605,7 +605,6 @@
             prop.r.setGroupProperty(_propertyGroup);
 
             function interfaceFunction(value){
-                console.log(value);
                 if(shape.p.ix === value){
                     return interfaceFunction.position;
                 }