Merge branch 'master' of github.com:bodymovin/bodymovin
diff --git a/player/index.html b/player/index.html
index d08e5cd..040b15e 100644
--- a/player/index.html
+++ b/player/index.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
+    <meta charset="UTF-8">
     <style>
         body, html{
             background-color:#000;
@@ -190,14 +191,15 @@
     var animData = {
         container: elem,
         renderer: 'svg',
-        loop: false,
+        loop: true,
         autoplay: true,
         rendererSettings: {
             progressiveLoad:false,
         },
-        path: 'exports/render/pinkbackground.json'
+        path: 'exports/render/data.json'
     };
     anim = lottie.loadAnimation(animData);
+    //anim.setSubframe(false);
 </script>
 </body>
 </html>
diff --git a/player/js/animation/AnimationItem.js b/player/js/animation/AnimationItem.js
index 2515eac..8c21164 100644
--- a/player/js/animation/AnimationItem.js
+++ b/player/js/animation/AnimationItem.js
@@ -582,6 +582,10 @@
     return this.assets;
 };
 
+AnimationItem.prototype.getDuration = function (isFrame) {
+    return isFrame ? this.totalFrames : this.totalFrames / this.frameRate;
+};
+
 AnimationItem.prototype.trigger = function(name){
     if(this._cbs && this._cbs[name]){
         switch(name){
diff --git a/player/js/elements/TextElement.js b/player/js/elements/TextElement.js
index 73d8b7e..aeb01d9 100644
--- a/player/js/elements/TextElement.js
+++ b/player/js/elements/TextElement.js
@@ -5,8 +5,8 @@
     this.lettersChangedFlag = true;
     this.initFrame();
     this.initBaseData(data, globalData, comp);
-    this.textAnimator = new TextAnimatorProperty(data.t, this.renderType, this);
     this.textProperty = new TextProperty(this, data.t, this.dynamicProperties);
+    this.textAnimator = new TextAnimatorProperty(data.t, this.renderType, this);
     this.initTransform(data, globalData, comp);
     this.initHierarchy();
     this.initRenderable();
@@ -25,6 +25,7 @@
     if(this.textProperty._mdf || this.textProperty._isFirstFrame) {
         this.buildNewText();
         this.textProperty._isFirstFrame = false;
+        this.textProperty._mdf = false;
     }
 };
 
@@ -41,20 +42,14 @@
 
 ITextElement.prototype.updateDocumentData = function(newData, index) {
     this.textProperty.updateDocumentData(newData, index);
-    this.buildNewText();
-    this.renderInnerContent();
 };
 
 ITextElement.prototype.canResizeFont = function(_canResize) {
     this.textProperty.canResizeFont(_canResize);
-    this.buildNewText();
-    this.renderInnerContent();
 };
 
 ITextElement.prototype.setMinimumFontSize = function(_fontSize) {
     this.textProperty.setMinimumFontSize(_fontSize);
-    this.buildNewText();
-    this.renderInnerContent();
 };
 
 ITextElement.prototype.applyTextPropertiesToMatrix = function(documentData, matrixHelper, lineNumber, xPos, yPos) {
@@ -73,6 +68,7 @@
     matrixHelper.translate(xPos, yPos, 0);
 };
 
+
 ITextElement.prototype.buildColor = function(colorData) {
     return 'rgb(' + Math.round(colorData[0]*255) + ',' + Math.round(colorData[1]*255) + ',' + Math.round(colorData[2]*255) + ')';
 };
diff --git a/player/js/elements/htmlElements/HCameraElement.js b/player/js/elements/htmlElements/HCameraElement.js
index 6f977ac..daa7378 100644
--- a/player/js/elements/htmlElements/HCameraElement.js
+++ b/player/js/elements/htmlElements/HCameraElement.js
@@ -37,9 +37,11 @@
     for(i=0;i<len;i+=1){
         //[perspectiveElem,container]
         comp = this.comp.threeDElements[i];
-        comp.perspectiveElem.style.perspective = comp.perspectiveElem.style.webkitPerspective = this.pe.v+'px';
-        comp.container.style.transformOrigin = comp.container.style.mozTransformOrigin = comp.container.style.webkitTransformOrigin = "0px 0px 0px";
-        comp.perspectiveElem.style.transform = comp.perspectiveElem.style.webkitTransform = 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)';
+        if(comp.type === '3d') {
+            comp.perspectiveElem.style.perspective = comp.perspectiveElem.style.webkitPerspective = this.pe.v+'px';
+            comp.container.style.transformOrigin = comp.container.style.mozTransformOrigin = comp.container.style.webkitTransformOrigin = "0px 0px 0px";
+            comp.perspectiveElem.style.transform = comp.perspectiveElem.style.webkitTransform = 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)';
+        }
     }
 };
 
@@ -107,7 +109,9 @@
             var comp;
             for(i=0;i<len;i+=1){
                 comp = this.comp.threeDElements[i];
-                comp.container.style.transform = comp.container.style.webkitTransform = this.mat.toCSS();
+                if(comp.type === '3d') {
+                    comp.container.style.transform = comp.container.style.webkitTransform = this.mat.toCSS();
+                }
             }
             this.mat.clone(this._prevMat);
         }
diff --git a/player/js/elements/htmlElements/HImageElement.js b/player/js/elements/htmlElements/HImageElement.js
index 191f854..ecb0dd9 100644
--- a/player/js/elements/htmlElements/HImageElement.js
+++ b/player/js/elements/htmlElements/HImageElement.js
@@ -23,6 +23,6 @@
     }
     img.src = assetPath;
     if(this.data.ln){
-        this.innerElem.setAttribute('id',this.data.ln);
+        this.baseElement.setAttribute('id',this.data.ln);
     }
 };
\ No newline at end of file
diff --git a/player/js/elements/htmlElements/HTextElement.js b/player/js/elements/htmlElements/HTextElement.js
index f2b0e93..552ca88 100644
--- a/player/js/elements/htmlElements/HTextElement.js
+++ b/player/js/elements/htmlElements/HTextElement.js
@@ -36,7 +36,7 @@
 
 HTextElement.prototype.buildNewText = function(){
     var documentData = this.textProperty.currentData;
-    this.renderedLetters = createSizedArray(this.textProperty.currentData.l ? this.textProperty.currentData.l.length : 0);
+    this.renderedLetters = createSizedArray(documentData.l ? documentData.l.length : 0);
     var innerElemStyle = this.innerElem.style;
     innerElemStyle.color = innerElemStyle.fill = documentData.fc ? this.buildColor(documentData.fc) : 'rgba(0,0,0,0)';
     if(documentData.sc){
@@ -131,12 +131,12 @@
                     tCont.style.transform = tCont.style.webkitTransform = 'translate(' + (boundingBox.x-1) + 'px,' + (boundingBox.y-1) + 'px)';
 
                     letters[i].yOffset = boundingBox.y-1;
-                    tParent.appendChild(tCont);
 
                 } else{
                     tCont.setAttribute('width',1);
                     tCont.setAttribute('height',1);
                 }
+                    tParent.appendChild(tCont);
             }else{
                 this.innerElem.appendChild(tSpan);
             }
diff --git a/player/js/renderers/HybridRenderer.js b/player/js/renderers/HybridRenderer.js
index 57ab388..c8da087 100644
--- a/player/js/renderers/HybridRenderer.js
+++ b/player/js/renderers/HybridRenderer.js
@@ -38,25 +38,30 @@
     }
     var layer = this.layers[pos];
     if(!layer.ddd || !this.supports3d){
-        var i = 0;
-        var nextDOMElement, nextLayer, tmpDOMElement;
-        while(i<pos){
-            if(this.elements[i] && this.elements[i]!== true && this.elements[i].getBaseElement){
-                nextLayer = this.elements[i];
-                tmpDOMElement = this.layers[i].ddd ? this.getThreeDContainerByPos(i) : nextLayer.getBaseElement();
-                nextDOMElement = tmpDOMElement || nextDOMElement;
-            }
-            i += 1;
-        }
-        if(nextDOMElement){
-            if(!layer.ddd || !this.supports3d){
-                this.layerElement.insertBefore(newDOMElement, nextDOMElement);
-            }
+        if(this.threeDElements) {
+            this.addTo3dContainer(newDOMElement,pos);
         } else {
-            if(!layer.ddd || !this.supports3d){
-                this.layerElement.appendChild(newDOMElement);
+            var i = 0;
+            var nextDOMElement, nextLayer, tmpDOMElement;
+            while(i<pos){
+                if(this.elements[i] && this.elements[i]!== true && this.elements[i].getBaseElement){
+                    nextLayer = this.elements[i];
+                    tmpDOMElement = this.layers[i].ddd ? this.getThreeDContainerByPos(i) : nextLayer.getBaseElement();
+                    nextDOMElement = tmpDOMElement || nextDOMElement;
+                }
+                i += 1;
+            }
+            if(nextDOMElement){
+                if(!layer.ddd || !this.supports3d){
+                    this.layerElement.insertBefore(newDOMElement, nextDOMElement);
+                }
+            } else {
+                if(!layer.ddd || !this.supports3d){
+                    this.layerElement.appendChild(newDOMElement);
+                }
             }
         }
+        
     } else {
         this.addTo3dContainer(newDOMElement,pos);
     }
@@ -115,22 +120,26 @@
     }
 };
 
-HybridRenderer.prototype.createThreeDContainer = function(pos){
+HybridRenderer.prototype.createThreeDContainer = function(pos, type){
     var perspectiveElem = createTag('div');
     styleDiv(perspectiveElem);
-    perspectiveElem.style.width = this.globalData.compSize.w+'px';
-    perspectiveElem.style.height = this.globalData.compSize.h+'px';
-    perspectiveElem.style.transformOrigin = perspectiveElem.style.mozTransformOrigin = perspectiveElem.style.webkitTransformOrigin = "50% 50%";
     var container = createTag('div');
     styleDiv(container);
-    container.style.transform = container.style.webkitTransform = 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)';
+    if(type === '3d') {
+        perspectiveElem.style.width = this.globalData.compSize.w+'px';
+        perspectiveElem.style.height = this.globalData.compSize.h+'px';
+        perspectiveElem.style.transformOrigin = perspectiveElem.style.mozTransformOrigin = perspectiveElem.style.webkitTransformOrigin = "50% 50%";
+        container.style.transform = container.style.webkitTransform = 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)';
+    }
+    
     perspectiveElem.appendChild(container);
-    this.resizerElem.appendChild(perspectiveElem);
+    //this.resizerElem.appendChild(perspectiveElem);
     var threeDContainerData = {
         container:container,
         perspectiveElem:perspectiveElem,
         startPos: pos,
-        endPos: pos
+        endPos: pos,
+        type: type
     };
     this.threeDElements.push(threeDContainerData);
     return threeDContainerData;
@@ -139,16 +148,26 @@
 HybridRenderer.prototype.build3dContainers = function(){
     var i, len = this.layers.length;
     var lastThreeDContainerData;
+    var currentContainer = '';
     for(i=0;i<len;i+=1){
         if(this.layers[i].ddd && this.layers[i].ty !== 3){
-            if(!lastThreeDContainerData){
-                lastThreeDContainerData = this.createThreeDContainer(i);
+            if(currentContainer !== '3d'){
+                currentContainer = '3d';
+                lastThreeDContainerData = this.createThreeDContainer(i,'3d');
             }
             lastThreeDContainerData.endPos = Math.max(lastThreeDContainerData.endPos,i);
         } else {
-            lastThreeDContainerData = null;
+            if(currentContainer !== '2d'){
+                currentContainer = '2d';
+                lastThreeDContainerData = this.createThreeDContainer(i,'2d');
+            }
+            lastThreeDContainerData.endPos = Math.max(lastThreeDContainerData.endPos,i);
         }
     }
+    len = this.threeDElements.length;
+    for(i = len - 1; i >= 0; i --) {
+        this.resizerElem.appendChild(this.threeDElements[i].perspectiveElem);
+    }
 };
 
 HybridRenderer.prototype.addTo3dContainer = function(elem,pos){
diff --git a/player/js/utils/FontManager.js b/player/js/utils/FontManager.js
index 0ec5443..0106ef8 100644
--- a/player/js/utils/FontManager.js
+++ b/player/js/utils/FontManager.js
@@ -94,7 +94,10 @@
     function createHelper(def, fontData){
         var tHelper = createNS('text');
         tHelper.style.fontSize = '100px';
-        tHelper.style.fontFamily = fontData.fFamily;
+        //tHelper.style.fontFamily = fontData.fFamily;
+        tHelper.setAttribute('font-family', fontData.fFamily);
+        tHelper.setAttribute('font-style', fontData.fStyle);
+        tHelper.setAttribute('font-weight', fontData.fWeight);
         tHelper.textContent = '1';
         if(fontData.fClass){
             tHelper.style.fontFamily = 'inherit';
@@ -104,8 +107,9 @@
         }
         def.appendChild(tHelper);
         var tCanvasHelper = createTag('canvas').getContext('2d');
-        tCanvasHelper.font = '100px '+ fontData.fFamily;
-        return tCanvasHelper;
+        tCanvasHelper.font = fontData.fWeight + ' ' + fontData.fStyle + ' 100px '+ fontData.fFamily;
+        //tCanvasHelper.font = ' 100px '+ fontData.fFamily;
+        return tHelper;
     }
 
     function addFonts(fontData, defs){
@@ -139,7 +143,8 @@
                 l.type = "text/css";
                 l.rel = "stylesheet";
                 l.href = fontArr[i].fPath;
-                defs.appendChild(l);
+                //defs.appendChild(l);
+                document.body.appendChild(l);
             } else if(fontArr[i].fOrigin === 't' || fontArr[i].origin === 2){
                 //<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet' type='text/css'>
                 var sc = createTag('script');
@@ -147,11 +152,12 @@
                 defs.appendChild(sc);
             }
             fontArr[i].helper = createHelper(defs,fontArr[i]);
+            fontArr[i].cache = {};
             this.fonts.push(fontArr[i]);
         }
         //On some cases the font even if it is loaded, it won't load correctly when measuring text on canvas.
         //Adding this timeout seems to fix it
-        setTimeout(function() {
+       setTimeout(function() {
             checkLoadedFonts.bind(this)();
         }.bind(this), 100);
     }
@@ -195,12 +201,25 @@
         return emptyChar;
     }
 
-    function measureText(char, fontName, size){
+    function measureText(char, fontName, size) {
         var fontData = this.getFontByName(fontName);
-        var tHelper = fontData.helper;
-        //tHelper.textContent = char;
-        return tHelper.measureText(char).width*size/100;
-        //return tHelper.getComputedTextLength()*size/100;
+        var index = char.charCodeAt(0);
+        if(!fontData.cache[index + 1]) {
+            var tHelper = fontData.helper;
+            //Canvas version
+            //fontData.cache[index] = tHelper.measureText(char).width / 100;
+            //SVG version
+            //console.log(tHelper.getBBox().width)
+            /*tHelper.textContent = '|' + char + '|';
+            var doubleSize = tHelper.getComputedTextLength();
+            tHelper.textContent = '||';
+            var singleSize = tHelper.getComputedTextLength();
+            fontData.cache[index + 1] = (doubleSize - singleSize)/100;*/
+           
+            tHelper.textContent = char;
+            fontData.cache[index + 1] = (tHelper.getComputedTextLength())/100;
+        }
+        return fontData.cache[index + 1] * size;
     }
 
     function getFontByName(name){
@@ -211,7 +230,7 @@
             }
             i += 1;
         }
-        return 'sans-serif';
+        return this.fonts[0];
     }
 
     function getCombinedCharacterCodes() {
diff --git a/player/js/utils/expressions/ExpressionTextPropertyDecorator.js b/player/js/utils/expressions/ExpressionTextPropertyDecorator.js
index 99d2aa6..8b9ffcc 100644
--- a/player/js/utils/expressions/ExpressionTextPropertyDecorator.js
+++ b/player/js/utils/expressions/ExpressionTextPropertyDecorator.js
@@ -2,28 +2,24 @@
 
     function searchExpressions(){
         if(this.data.d.x){
-            this.comp = this.elem.comp;
-            if(this.getValue) {
-                this.getPreValue = this.getValue;
-            }
             this.calculateExpression = ExpressionManager.initiateExpression.bind(this)(this.elem,this.data.d,this);
-            this.getValue = this.getExpressionValue;
+            this.addEffect(this.getExpressionValue.bind(this));
             return true;
         }
-        return false;
+    }
+
+    TextProperty.prototype.getExpressionValue = function(currentValue) {
+        var newValue = this.calculateExpression(currentValue.t);
+        currentValue.t = newValue;
+        return currentValue;
     }
 
     TextProperty.prototype.searchProperty = function(){
-        this.kf = this.searchExpressions() || this.data.d.k.length > 1;
-        return this.kf;
-    };
 
-    TextProperty.prototype.getExpressionValue = function(num){
-        this.calculateExpression();
-        if(this._mdf) {
-            this.currentData.t = this.v.toString();
-            this.completeTextData(this.currentData);
-        }
+        var isKeyframed = this.searchKeyframes();
+        var hasExpressions = this.searchExpressions();
+        this.kf = isKeyframed || hasExpressions;
+        return this.kf;
     };
 
     TextProperty.prototype.searchExpressions = searchExpressions;
diff --git a/player/js/utils/expressions/TextInterface.js b/player/js/utils/expressions/TextInterface.js
index 632ff7a..5f04bab 100644
--- a/player/js/utils/expressions/TextInterface.js
+++ b/player/js/utils/expressions/TextInterface.js
@@ -6,7 +6,7 @@
         Object.defineProperty(_thisLayerFunction, "sourceText", {
             get: function(){
                 var stringValue = elem.textProperty.currentData.t;
-                if(elem.textProperty.currentData.t !== _prevValue) {
+                if(stringValue !== _prevValue) {
                     elem.textProperty.currentData.t = _prevValue;
                     _sourceText = new String(stringValue);
                     //If stringValue is an empty string, eval returns undefined, so it has to be returned as a String primitive
diff --git a/player/js/utils/text/TextAnimatorProperty.js b/player/js/utils/text/TextAnimatorProperty.js
index b4dff75..7c0818e 100644
--- a/player/js/utils/text/TextAnimatorProperty.js
+++ b/player/js/utils/text/TextAnimatorProperty.js
@@ -480,6 +480,7 @@
                             matrixHelper.translate(animatorProps.p.v[0] * mult[0], animatorProps.p.v[1] * mult[1], -animatorProps.p.v[2] * mult[2]);
                         } else {
                             matrixHelper.translate(animatorProps.p.v[0] * mult, animatorProps.p.v[1] * mult, -animatorProps.p.v[2] * mult);
+                        
                         }
                     }
                 }
diff --git a/player/js/utils/text/TextProperty.js b/player/js/utils/text/TextProperty.js
index 5d2d15e..9bec2f2 100644
--- a/player/js/utils/text/TextProperty.js
+++ b/player/js/utils/text/TextProperty.js
@@ -4,15 +4,17 @@
 	this.v = '';
 	this.kf = false;
 	this._isFirstFrame = true;
-	this._mdf = true;
+	this._mdf = false;
 	this.data = data;
 	this.elem = elem;
+    this.comp = this.elem.comp;
 	this.keysIndex = -1;
     this.canResize = false;
     this.minimumFontSize = 1;
+    this.effectsSequence = [];
 	this.currentData = {
 		ascent: 0,
-        boxWidth: [0,0],
+        boxWidth: this.defaultBoxWidth,
         f: '',
         fStyle: '',
         fWeight: '',
@@ -30,26 +32,49 @@
         t: 0,
         tr: 0,
         sz:0,
-        ps:[0,0],
+        ps:null,
         fillColorAnim: false,
         strokeColorAnim: false,
         strokeWidthAnim: false,
         yOffset: 0,
-        __complete: false,
         finalSize:0,
         finalText:[],
-        finalLineHeight: 0
+        finalLineHeight: 0,
+        __test: true
 
 	};
-	if(this.searchProperty()) {
-        elem.addDynamicProperty(this);
-	} else {
-		this.getValue(true);
-	}
+    this.copyFromDocumentData(this.data.d.k[0].s);
+    
+    if(!this.searchProperty()) {
+        this.completeTextData(this.currentData);
+        this.keysIndex = 0;
+    }
 }
 
-TextProperty.prototype.setCurrentData = function(data){
-		var currentData = this.currentData;
+TextProperty.prototype.defaultBoxWidth = [0,0];
+
+TextProperty.prototype.copyFromDocumentData = function(data) {
+    for(var s in data) {
+        this.currentData[s] = data[s];
+    }
+}
+
+TextProperty.prototype.setCurrentData = function(data, currentTextValue){
+        if(this.currentData !== data) {
+            if(!data.__complete) {
+                this.completeTextData(data);
+            }
+            this.copyFromDocumentData(data);
+            this.currentData.boxWidth = this.currentData.boxWidth || this.defaultBoxWidth;
+            this.currentData.fillColorAnim = data.fillColorAnim || this.currentData.fillColorAnim;
+            this.currentData.strokeColorAnim = data.strokeColorAnim || this.currentData.strokeColorAnim;
+            this.currentData.strokeWidthAnim = data.strokeWidthAnim || this.currentData.strokeWidthAnim;
+            this._mdf = true;
+        } else if(currentTextValue !== this.currentData.t) {
+            this._mdf = true;
+            this.completeTextData(data);
+        }
+		/*var currentData = this.currentData;
         currentData.ascent = data.ascent;
         currentData.boxWidth = data.boxWidth ? data.boxWidth : currentData.boxWidth;
         currentData.f = data.f;
@@ -76,21 +101,50 @@
         currentData.yOffset = data.yOffset;
         currentData.finalSize = data.finalSize;
         currentData.finalLineHeight = data.finalLineHeight;
-        currentData.finalText = data.finalText;
-        currentData.__complete = false;
+        currentData.finalText = data.finalText;*/
 };
 
 TextProperty.prototype.searchProperty = function() {
-	this.kf = this.data.d.k.length > 1;
-	return this.kf;
+    return this.searchKeyframes();
 };
 
-TextProperty.prototype.getValue = function(_forceRender) {
-	this._mdf = false;
-	var frameId = this.elem.globalData.frameId;
-	if((frameId === this._frameId || !this.kf) && !this._isFirstFrame && !_forceRender) {
-		return;
-	}
+TextProperty.prototype.searchKeyframes = function() {
+    this.kf = this.data.d.k.length > 1;
+    if(this.kf) {
+        this.addEffect(this.getKeyframeValue.bind(this));
+    }
+    return this.kf;
+}
+
+TextProperty.prototype.addEffect = function(effectFunction) {
+	this.effectsSequence.push(effectFunction);
+    this.elem.addDynamicProperty(this);
+};
+
+TextProperty.prototype.getValue = function(_finalValue) {
+    if((this.elem.globalData.frameId === this.frameId || !this.effectsSequence.length) && !_finalValue) {
+        return;
+    }
+    var currentTextValue = this.currentData.t;        
+    if(this.lock) {
+        this.setCurrentData(this.currentData, currentTextValue);
+        return;
+    }
+    this.lock = true;
+    this._mdf = false;
+    var multipliedValue;
+    var i, len = this.effectsSequence.length;
+    var finalValue = _finalValue || this.currentData;
+    for(i = 0; i < len; i += 1) {
+        finalValue = this.effectsSequence[i](finalValue);
+    }
+    this.setCurrentData(finalValue, currentTextValue);
+    this.pv = this.v = this.currentData;
+    this.lock = false;
+    this.frameId = this.elem.globalData.frameId;
+}
+
+TextProperty.prototype.getKeyframeValue = function(currentValue) {
     var textKeys = this.data.d.k, textDocumentData;
     var frameNum = this.elem.comp.renderedFrame;
     var i = 0, len = textKeys.length;
@@ -102,15 +156,10 @@
         i += 1;
     }
     if(this.keysIndex !== i) {
-    	if(!textDocumentData.__complete) {
-            this.completeTextData(textDocumentData);
-        }
-        this.setCurrentData(textDocumentData);
-        this._mdf = !this._isFirstFrame;
-        this.pv = this.v = this.currentData.t;
+        currentValue = textDocumentData;
         this.keysIndex = i;
     }
-	this._frameId = frameId;
+    return currentValue;
 };
 
 TextProperty.prototype.buildFinalText = function(text) {
@@ -374,7 +423,11 @@
 };
 
 TextProperty.prototype.updateDocumentData = function(newData, index) {
-	index = index === undefined ? this.keysIndex : index;
+	index = index === undefined 
+    ? this.keysIndex === -1 
+        ? 0 
+        : this.keysIndex 
+    : index;
     var dData = this.data.d.k[index].s;
     for(var s in newData) {
         dData[s] = newData[s];
@@ -385,9 +438,9 @@
 TextProperty.prototype.recalculate = function(index) {
     var dData = this.data.d.k[index].s;
     dData.__complete = false;
-    this.keysIndex = -1;
+    this.keysIndex = this.kf ? -1 : 0;
     this._isFirstFrame = true;
-    this.getValue(true);
+    this.getValue(dData);
 }
 
 TextProperty.prototype.canResizeFont = function(_canResize) {
diff --git a/player/js/utils/text/TextSelectorProperty.js b/player/js/utils/text/TextSelectorProperty.js
index 8b82ba2..dbbbffd 100644
--- a/player/js/utils/text/TextSelectorProperty.js
+++ b/player/js/utils/text/TextSelectorProperty.js
@@ -5,6 +5,7 @@
 
     function TextSelectorProp(elem,data){
         this._mdf = false;
+        this._currentTextLength = -1;
         this.k = false;
         this.data = data;
         this.dynamicProperties = [];
@@ -31,6 +32,9 @@
     TextSelectorProp.prototype = {
         addDynamicProperty: addDynamicProperty,
         getMult: function(ind) {
+            if(this._currentTextLength !== this.elem.textProperty.currentData.l.length) {
+                this.getValue();
+            }
             //var easer = bez.getEasingCurve(this.ne.v/100,0,1-this.xe.v/100,1);
             var easer = BezierFactory.getBezierEasing(this.ne.v/100,0,1-this.xe.v/100,1).get;
             var mult = 0;
@@ -112,11 +116,11 @@
                     }
                 }
             }
-            var totalChars = this.data.totalChars || this.elem.textProperty.currentData.l.length || 0;
+            this._currentTextLength = this.elem.textProperty.currentData.l.length || 0;
             if(newCharsFlag && this.data.r === 2) {
-                this.e.v = totalChars;
+                this.e.v = this._currentTextLength;
             }
-            var divisor = this.data.r === 2 ? 1 : 100 / totalChars;
+            var divisor = this.data.r === 2 ? 1 : 100 / this._currentTextLength;
             var o = this.o.v/divisor;
             var s = this.s.v/divisor + o;
             var e = (this.e.v/divisor) + o;