1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5  Copyright (c) 2010      Lam Pham
  6 
  7  http://www.cocos2d-x.org
  8 
  9  Permission is hereby granted, free of charge, to any person obtaining a copy
 10  of this software and associated documentation files (the "Software"), to deal
 11  in the Software without restriction, including without limitation the rights
 12  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13  copies of the Software, and to permit persons to whom the Software is
 14  furnished to do so, subject to the following conditions:
 15 
 16  The above copyright notice and this permission notice shall be included in
 17  all copies or substantial portions of the Software.
 18 
 19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25  THE SOFTWARE.
 26  ****************************************************************************/
 27 
 28 /**
 29  * cc.Progresstimer is a subclass of cc.Node.   <br/>
 30  * It renders the inner sprite according to the percentage.<br/>
 31  * The progress can be Radial, Horizontal or vertical.
 32  * @class
 33  * @extends cc.Node
 34  *
 35  * @property {cc.Point}     midPoint        <p>- Midpoint is used to modify the progress start position.<br/>
 36  *                                          If you're using radials type then the midpoint changes the center point<br/>
 37  *                                          If you're using bar type the the midpoint changes the bar growth<br/>
 38  *                                              it expands from the center but clamps to the sprites edge so:<br/>
 39  *                                              you want a left to right then set the midpoint all the way to cc.p(0,y)<br/>
 40  *                                              you want a right to left then set the midpoint all the way to cc.p(1,y)<br/>
 41  *                                              you want a bottom to top then set the midpoint all the way to cc.p(x,0)<br/>
 42  *                                              you want a top to bottom then set the midpoint all the way to cc.p(x,1)</p>
 43  * @property {cc.Point}     barChangeRate   - This allows the bar type to move the component at a specific rate.
 44  * @property {enum}         type            - Type of the progress timer: cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR.
 45  * @property {Number}       percentage      - Percentage to change progress, from 0 to 100.
 46  * @property {cc.Sprite}    sprite          - The sprite to show the progress percentage.
 47  * @property {Boolean}      reverseDir      - Indicate whether the direction is reversed.
 48  */
 49 cc.ProgressTimer = cc.Node.extend(/** @lends cc.ProgressTimer# */{
 50     _type:null,
 51     _percentage:0.0,
 52     _sprite:null,
 53 
 54     _midPoint:null,
 55     _barChangeRate:null,
 56     _reverseDirection:false,
 57     _className:"ProgressTimer",
 58 
 59     /**
 60      *    Midpoint is used to modify the progress start position.
 61      *    If you're using radials type then the midpoint changes the center point
 62      *    If you're using bar type the the midpoint changes the bar growth
 63      *        it expands from the center but clamps to the sprites edge so:
 64      *        you want a left to right then set the midpoint all the way to cc.p(0,y)
 65      *        you want a right to left then set the midpoint all the way to cc.p(1,y)
 66      *        you want a bottom to top then set the midpoint all the way to cc.p(x,0)
 67      *        you want a top to bottom then set the midpoint all the way to cc.p(x,1)
 68      *  @return {cc.Point}
 69      */
 70     getMidpoint:function () {
 71         return cc.p(this._midPoint.x, this._midPoint.y);
 72     },
 73 
 74     /**
 75      * Midpoint setter
 76      * @param {cc.Point} mpoint
 77      */
 78     setMidpoint:function (mpoint) {
 79         this._midPoint = cc.pClamp(mpoint, cc.p(0, 0), cc.p(1, 1));
 80     },
 81 
 82     /**
 83      *    This allows the bar type to move the component at a specific rate
 84      *    Set the component to 0 to make sure it stays at 100%.
 85      *    For example you want a left to right bar but not have the height stay 100%
 86      *    Set the rate to be cc.p(0,1); and set the midpoint to = cc.p(0,.5f);
 87      *  @return {cc.Point}
 88      */
 89     getBarChangeRate:function () {
 90         return cc.p(this._barChangeRate.x, this._barChangeRate.y);
 91     },
 92 
 93     /**
 94      * @param {cc.Point} barChangeRate
 95      */
 96     setBarChangeRate:function (barChangeRate) {
 97         this._barChangeRate = cc.pClamp(barChangeRate, cc.p(0, 0), cc.p(1, 1));
 98     },
 99 
100     /**
101      *  Change the percentage to change progress
102      * @return {cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR}
103      */
104     getType:function () {
105         return this._type;
106     },
107 
108     /**
109      * Percentages are from 0 to 100
110      * @return {Number}
111      */
112     getPercentage:function () {
113         return this._percentage;
114     },
115 
116     /**
117      * The image to show the progress percentage, retain
118      * @return {cc.Sprite}
119      */
120     getSprite:function () {
121         return this._sprite;
122     },
123 
124     /**
125      * from 0-100
126      * @param {Number} percentage
127      */
128     setPercentage:function (percentage) {
129         if (this._percentage != percentage) {
130             this._percentage = cc.clampf(percentage, 0, 100);
131             this._updateProgress();
132         }
133     },
134 
135     setOpacityModifyRGB:function (bValue) {
136     },
137 
138     isOpacityModifyRGB:function () {
139         return false;
140     },
141 
142     isReverseDirection:function () {
143         return this._reverseDirection;
144     },
145 
146     _boundaryTexCoord:function (index) {
147         if (index < cc.ProgressTimer.TEXTURE_COORDS_COUNT) {
148             var locProTextCoords = cc.ProgressTimer.TEXTURE_COORDS;
149             if (this._reverseDirection)
150                 return cc.p((locProTextCoords >> (7 - (index << 1))) & 1, (locProTextCoords >> (7 - ((index << 1) + 1))) & 1);
151             else
152                 return cc.p((locProTextCoords >> ((index << 1) + 1)) & 1, (locProTextCoords >> (index << 1)) & 1);
153         }
154         return cc.p(0,0);
155     },
156 
157     _origin:null,
158     _startAngle:270,
159     _endAngle:270,
160     _radius:0,
161     _counterClockWise:false,
162     _barRect:null,
163 
164     _vertexDataCount:0,
165     _vertexData:null,
166     _vertexArrayBuffer:null,
167     _vertexWebGLBuffer:null,
168     _vertexDataDirty:false,
169 
170     ctor: null,
171 
172     _ctorForCanvas: function (sprite) {
173         cc.Node.prototype.ctor.call(this);
174 
175         this._type = cc.ProgressTimer.TYPE_RADIAL;
176         this._percentage = 0.0;
177         this._midPoint = cc.p(0, 0);
178         this._barChangeRate = cc.p(0, 0);
179         this._reverseDirection = false;
180 
181         this._sprite = null;
182 
183         this._origin = cc.p(0,0);
184         this._startAngle = 270;
185         this._endAngle = 270;
186         this._radius = 0;
187         this._counterClockWise = false;
188         this._barRect = cc.rect(0, 0, 0, 0);
189 
190         sprite && this._initWithSpriteForCanvas(sprite);
191     },
192 
193     _ctorForWebGL: function (sprite) {
194         cc.Node.prototype.ctor.call(this);
195         this._type = cc.ProgressTimer.TYPE_RADIAL;
196         this._percentage = 0.0;
197         this._midPoint = cc.p(0, 0);
198         this._barChangeRate = cc.p(0, 0);
199         this._reverseDirection = false;
200 
201         this._sprite = null;
202 
203         this._vertexWebGLBuffer = cc._renderContext.createBuffer();
204         this._vertexDataCount = 0;
205         this._vertexData = null;
206         this._vertexArrayBuffer = null;
207         this._vertexDataDirty = false;
208 
209         sprite && this._initWithSpriteForWebGL(sprite);
210     },
211 
212     /**
213      * set color of sprite
214      * @param {cc.Color} color
215      */
216     setColor:function (color) {
217         this._sprite.color = color;
218         this._updateColor();
219     },
220 
221     /**
222      * Opacity
223      * @param {Number} opacity
224      */
225     setOpacity:function (opacity) {
226         this._sprite.opacity = opacity;
227         this._updateColor();
228     },
229 
230     /**
231      * return color of sprite
232      * @return {cc.Color}
233      */
234     getColor:function () {
235         return this._sprite.color;
236     },
237 
238     /**
239      * return Opacity of sprite
240      * @return {Number}
241      */
242     getOpacity:function () {
243         return this._sprite.opacity;
244     },
245 
246     /**
247      * @function
248      * @param {Boolean} reverse
249      */
250     setReverseProgress:null,
251 
252     _setReverseProgressForCanvas:function (reverse) {
253         if (this._reverseDirection !== reverse)
254             this._reverseDirection = reverse;
255     },
256 
257     _setReverseProgressForWebGL:function (reverse) {
258         if (this._reverseDirection !== reverse) {
259             this._reverseDirection = reverse;
260 
261             //    release all previous information
262             this._vertexData = null;
263             this._vertexArrayBuffer = null;
264             this._vertexDataCount = 0;
265         }
266     },
267 
268     /**
269      * @function
270      * @param {cc.Sprite} sprite
271      */
272     setSprite:null,
273 
274     _setSpriteForCanvas:function (sprite) {
275         if (this._sprite != sprite) {
276             this._sprite = sprite;
277             this.width = this._sprite.width;
278 	        this.height = this._sprite.height;
279         }
280     },
281 
282     _setSpriteForWebGL:function (sprite) {
283         if (sprite && this._sprite != sprite) {
284             this._sprite = sprite;
285             this.width = sprite.width;
286 	        this.height = sprite.height;
287 
288             //	Everytime we set a new sprite, we free the current vertex data
289             if (this._vertexData) {
290                 this._vertexData = null;
291                 this._vertexArrayBuffer = null;
292                 this._vertexDataCount = 0;
293             }
294         }
295     },
296 
297     /**
298      * set Progress type of cc.ProgressTimer
299      * @function
300      * @param {cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR} type
301      */
302     setType:null,
303 
304     _setTypeForCanvas:function (type) {
305         if (type !== this._type)
306             this._type = type;
307     },
308 
309     _setTypeForWebGL:function (type) {
310         if (type !== this._type) {
311             //	release all previous information
312             if (this._vertexData) {
313                 this._vertexData = null;
314                 this._vertexArrayBuffer = null;
315                 this._vertexDataCount = 0;
316             }
317             this._type = type;
318         }
319     },
320 
321     /**
322      * Reverse Progress setter
323      * @function
324      * @param {Boolean} reverse
325      */
326     setReverseDirection: null,
327 
328     _setReverseDirectionForCanvas: function (reverse) {
329         if (this._reverseDirection !== reverse)
330             this._reverseDirection = reverse;
331     },
332 
333     _setReverseDirectionForWebGL: function (reverse) {
334         if (this._reverseDirection !== reverse) {
335             this._reverseDirection = reverse;
336             //release all previous information
337             this._vertexData = null;
338             this._vertexArrayBuffer = null;
339             this._vertexDataCount = 0;
340         }
341     },
342 
343     /**
344      * @param {cc.Point} alpha
345      * @return {cc.Vertex2F | Object} the vertex position from the texture coordinate
346      * @private
347      */
348     _textureCoordFromAlphaPoint:function (alpha) {
349         var locSprite = this._sprite;
350         if (!locSprite) {
351             return {u:0, v:0}; //new cc.Tex2F(0, 0);
352         }
353         var quad = locSprite.quad;
354         var min = cc.p(quad.bl.texCoords.u, quad.bl.texCoords.v);
355         var max = cc.p(quad.tr.texCoords.u, quad.tr.texCoords.v);
356 
357         //  Fix bug #1303 so that progress timer handles sprite frame texture rotation
358         if (locSprite.textureRectRotated) {
359             var temp = alpha.x;
360             alpha.x = alpha.y;
361             alpha.y = temp;
362         }
363         return {u: min.x * (1 - alpha.x) + max.x * alpha.x, v: min.y * (1 - alpha.y) + max.y * alpha.y};
364     },
365 
366     _vertexFromAlphaPoint:function (alpha) {
367         if (!this._sprite) {
368             return {x: 0, y: 0};
369         }
370         var quad = this._sprite.quad;
371         var min = cc.p(quad.bl.vertices.x, quad.bl.vertices.y);
372         var max = cc.p(quad.tr.vertices.x, quad.tr.vertices.y);
373         return {x: min.x * (1 - alpha.x) + max.x * alpha.x, y: min.y * (1 - alpha.y) + max.y * alpha.y};
374     },
375 
376     /**
377      * Initializes a progress timer with the sprite as the shape the timer goes through
378      * @function
379      * @param {cc.Sprite} sprite
380      * @return {Boolean}
381      */
382     initWithSprite:null,
383 
384     _initWithSpriteForCanvas:function (sprite) {
385         this.percentage = 0;
386         this.anchorX = 0.5;
387 	    this.anchorY = 0.5;
388 
389         this._type = cc.ProgressTimer.TYPE_RADIAL;
390         this._reverseDirection = false;
391 	    this.midPoint = cc.p(0.5, 0.5);
392 	    this.barChangeRate = cc.p(1, 1);
393 	    this.sprite = sprite;
394 
395         return true;
396     },
397 
398     _initWithSpriteForWebGL:function (sprite) {
399         this.percentage = 0;
400         this._vertexData = null;
401         this._vertexArrayBuffer = null;
402         this._vertexDataCount = 0;
403         this.anchorX = 0.5;
404 	    this.anchorY = 0.5;
405 
406         this._type = cc.ProgressTimer.TYPE_RADIAL;
407         this._reverseDirection = false;
408         this.midPoint = cc.p(0.5, 0.5);
409         this.barChangeRate = cc.p(1, 1);
410         this.sprite = sprite;
411 
412         //shader program
413         this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
414         return true;
415     },
416 
417     /**
418      * Stuff gets drawn here
419      * @function
420      * @param {CanvasRenderingContext2D} ctx
421      */
422     draw:null,
423 
424     _drawForCanvas:function (ctx) {
425         var context = ctx || cc._renderContext;
426 
427         var locSprite = this._sprite;
428         if (locSprite._isLighterMode)
429             context.globalCompositeOperation = 'lighter';
430 
431         var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY();
432 
433         context.globalAlpha = locSprite._displayedOpacity / 255;
434         var locRect = locSprite._rect, locContentSize = locSprite._contentSize, locOffsetPosition = locSprite._offsetPosition, locDrawSizeCanvas = locSprite._drawSize_Canvas;
435         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = locSprite._textureRect_Canvas;
436         locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX;
437         locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY;
438 
439         context.save();
440         if (locSprite._flippedX) {
441             flipXOffset = -locOffsetPosition.x - locRect.width;
442             context.scale(-1, 1);
443         }
444         if (locSprite._flippedY) {
445             flipYOffset = locOffsetPosition.y;
446             context.scale(1, -1);
447         }
448 
449         flipXOffset *= locEGL_ScaleX;
450         flipYOffset *= locEGL_ScaleY;
451 
452         //clip
453         if (this._type == cc.ProgressTimer.TYPE_BAR) {
454             var locBarRect = this._barRect;
455             context.beginPath();
456             context.rect(locBarRect.x * locEGL_ScaleX, locBarRect.y * locEGL_ScaleY, locBarRect.width * locEGL_ScaleX, locBarRect.height * locEGL_ScaleY);
457             context.clip();
458             context.closePath();
459         } else if (this._type == cc.ProgressTimer.TYPE_RADIAL) {
460             var locOriginX = this._origin.x * locEGL_ScaleX;
461             var locOriginY = this._origin.y * locEGL_ScaleY;
462             context.beginPath();
463             context.arc(locOriginX, locOriginY, this._radius * locEGL_ScaleY, (Math.PI / 180) * this._startAngle, (Math.PI / 180) * this._endAngle, this._counterClockWise);
464             context.lineTo(locOriginX, locOriginY);
465             context.clip();
466             context.closePath();
467         }
468 
469         //draw sprite
470         if (locSprite._texture && locTextureCoord.validRect) {
471             var image = locSprite._texture.getHtmlElementObj();
472             if (this._colorized) {
473                 context.drawImage(image,
474                     0, 0, locTextureCoord.width, locTextureCoord.height,
475                     flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height);
476             } else {
477                 context.drawImage(image,
478                     locTextureCoord.x, locTextureCoord.y, locTextureCoord.width,  locTextureCoord.height,
479                     flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height);
480             }
481         } else if (locContentSize.width !== 0) {
482             var curColor = this.color;
483             context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)";
484             context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY);
485         }
486 
487         context.restore();
488         cc.incrementGLDraws(1);
489     },
490 
491     _drawForWebGL:function (ctx) {
492         var context = ctx || cc._renderContext;
493         if (!this._vertexData || !this._sprite)
494             return;
495 
496         cc.nodeDrawSetup(this);
497 
498         var blendFunc = this._sprite.getBlendFunc();
499         cc.glBlendFunc(blendFunc.src, blendFunc.dst);
500         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
501 
502         cc.glBindTexture2D(this._sprite.texture);
503 
504         context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer);
505         if(this._vertexDataDirty){
506             context.bufferData(context.ARRAY_BUFFER, this._vertexArrayBuffer, context.DYNAMIC_DRAW);
507             this._vertexDataDirty = false;
508         }
509         var locVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
510         context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, locVertexDataLen, 0);
511         context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 8);
512         context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 12);
513 
514         if (this._type === cc.ProgressTimer.TYPE_RADIAL)
515             context.drawArrays(context.TRIANGLE_FAN, 0, this._vertexDataCount);
516         else if (this._type == cc.ProgressTimer.TYPE_BAR) {
517             if (!this._reverseDirection)
518                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount);
519             else {
520                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount / 2);
521                 context.drawArrays(context.TRIANGLE_STRIP, 4, this._vertexDataCount / 2);
522                 // 2 draw calls
523                 cc.g_NumberOfDraws++;
524             }
525         }
526         cc.g_NumberOfDraws++;
527     },
528 
529     /**
530      * <p>
531      *    Update does the work of mapping the texture onto the triangles            <br/>
532      *    It now doesn't occur the cost of free/alloc data every update cycle.      <br/>
533      *    It also only changes the percentage point but no other points if they have not been modified.       <br/>
534      *                                                                              <br/>
535      *    It now deals with flipped texture. If you run into this problem, just use the                       <br/>
536      *    sprite property and enable the methods flipX, flipY.                      <br/>
537      * </p>
538      * @private
539      */
540     _updateRadial:function () {
541         if (!this._sprite)
542             return;
543 
544         var i, locMidPoint = this._midPoint;
545         var alpha = this._percentage / 100;
546         var angle = 2 * (cc.PI) * ( this._reverseDirection ? alpha : 1.0 - alpha);
547 
548         //    We find the vector to do a hit detection based on the percentage
549         //    We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
550         //    from that by the progress angle around the m_tMidpoint pivot
551         var topMid = cc.p(locMidPoint.x, 1);
552         var percentagePt = cc.pRotateByAngle(topMid, locMidPoint, angle);
553 
554         var index = 0;
555         var hit;
556 
557         if (alpha == 0) {
558             //    More efficient since we don't always need to check intersection
559             //    If the alpha is zero then the hit point is top mid and the index is 0.
560             hit = topMid;
561             index = 0;
562         } else if (alpha == 1) {
563             //    More efficient since we don't always need to check intersection
564             //    If the alpha is one then the hit point is top mid and the index is 4.
565             hit = topMid;
566             index = 4;
567         } else {
568             //    We run a for loop checking the edges of the texture to find the
569             //    intersection point
570             //    We loop through five points since the top is split in half
571 
572             var min_t = cc.FLT_MAX;
573             var locProTextCoordsCount = cc.ProgressTimer.TEXTURE_COORDS_COUNT;
574             for (i = 0; i <= locProTextCoordsCount; ++i) {
575                 var pIndex = (i + (locProTextCoordsCount - 1)) % locProTextCoordsCount;
576 
577                 var edgePtA = this._boundaryTexCoord(i % locProTextCoordsCount);
578                 var edgePtB = this._boundaryTexCoord(pIndex);
579 
580                 //    Remember that the top edge is split in half for the 12 o'clock position
581                 //    Let's deal with that here by finding the correct endpoints
582                 if (i == 0)
583                     edgePtB = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
584                 else if (i == 4)
585                     edgePtA = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
586 
587                 // retPoint are returned by ccpLineIntersect
588                 var retPoint = cc.p(0, 0);
589                 if (cc.pLineIntersect(edgePtA, edgePtB, locMidPoint, percentagePt, retPoint)) {
590                     //    Since our hit test is on rays we have to deal with the top edge
591                     //    being in split in half so we have to test as a segment
592                     if ((i == 0 || i == 4)) {
593                         //    s represents the point between edgePtA--edgePtB
594                         if (!(0 <= retPoint.x && retPoint.x <= 1))
595                             continue;
596                     }
597                     //    As long as our t isn't negative we are at least finding a
598                     //    correct hitpoint from m_tMidpoint to percentagePt.
599                     if (retPoint.y >= 0) {
600                         //    Because the percentage line and all the texture edges are
601                         //    rays we should only account for the shortest intersection
602                         if (retPoint.y < min_t) {
603                             min_t = retPoint.y;
604                             index = i;
605                         }
606                     }
607                 }
608             }
609 
610             //    Now that we have the minimum magnitude we can use that to find our intersection
611             hit = cc.pAdd(locMidPoint, cc.pMult(cc.pSub(percentagePt, locMidPoint), min_t));
612         }
613 
614         //    The size of the vertex data is the index from the hitpoint
615         //    the 3 is for the m_tMidpoint, 12 o'clock point and hitpoint position.
616         var sameIndexCount = true;
617         if (this._vertexDataCount != index + 3) {
618             sameIndexCount = false;
619             this._vertexData = null;
620             this._vertexArrayBuffer = null;
621             this._vertexDataCount = 0;
622         }
623 
624         if (!this._vertexData) {
625             this._vertexDataCount = index + 3;
626             var locCount = this._vertexDataCount, vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
627             this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
628             var locData = [];
629             for (i = 0; i < locCount; i++)
630                 locData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
631 
632             this._vertexData = locData;
633             if(!this._vertexData){
634                 cc.log( "cc.ProgressTimer._updateRadial() : Not enough memory");
635                 return;
636             }
637         }
638         this._updateColor();
639 
640         var locVertexData = this._vertexData;
641         if (!sameIndexCount) {
642             //    First we populate the array with the m_tMidpoint, then all
643             //    vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
644             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(locMidPoint);
645             locVertexData[0].vertices = this._vertexFromAlphaPoint(locMidPoint);
646 
647             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(topMid);
648             locVertexData[1].vertices = this._vertexFromAlphaPoint(topMid);
649 
650             for (i = 0; i < index; i++) {
651                 var alphaPoint = this._boundaryTexCoord(i);
652                 locVertexData[i + 2].texCoords = this._textureCoordFromAlphaPoint(alphaPoint);
653                 locVertexData[i + 2].vertices = this._vertexFromAlphaPoint(alphaPoint);
654             }
655         }
656 
657         //    hitpoint will go last
658         locVertexData[this._vertexDataCount - 1].texCoords = this._textureCoordFromAlphaPoint(hit);
659         locVertexData[this._vertexDataCount - 1].vertices = this._vertexFromAlphaPoint(hit);
660     },
661 
662     /**
663      * <p>
664      *    Update does the work of mapping the texture onto the triangles for the bar                            <br/>
665      *    It now doesn't occur the cost of free/alloc data every update cycle.                                  <br/>
666      *    It also only changes the percentage point but no other points if they have not been modified.         <br/>
667      *                                                                                                          <br/>
668      *    It now deals with flipped texture. If you run into this problem, just use the                         <br/>
669      *    sprite property and enable the methods flipX, flipY.                                                  <br/>
670      * </p>
671      * @private
672      */
673     _updateBar:function () {
674         if (!this._sprite)
675             return;
676 
677         var i;
678         var alpha = this._percentage / 100.0;
679         var locBarChangeRate = this._barChangeRate;
680         var alphaOffset = cc.pMult(cc.p((1.0 - locBarChangeRate.x) + alpha * locBarChangeRate.x,
681             (1.0 - locBarChangeRate.y) + alpha * locBarChangeRate.y), 0.5);
682         var min = cc.pSub(this._midPoint, alphaOffset);
683         var max = cc.pAdd(this._midPoint, alphaOffset);
684 
685         if (min.x < 0) {
686             max.x += -min.x;
687             min.x = 0;
688         }
689 
690         if (max.x > 1) {
691             min.x -= max.x - 1;
692             max.x = 1;
693         }
694 
695         if (min.y < 0) {
696             max.y += -min.y;
697             min.y = 0;
698         }
699 
700         if (max.y > 1) {
701             min.y -= max.y - 1;
702             max.y = 1;
703         }
704 
705         var locVertexData;
706         if (!this._reverseDirection) {
707             if (!this._vertexData) {
708                 this._vertexDataCount = 4;
709                 var vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, locCount = 4;
710                 this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
711                 this._vertexData = [];
712                 for (i = 0; i < locCount; i++)
713                     this._vertexData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
714             }
715 
716             locVertexData = this._vertexData;
717             //    TOPLEFT
718             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
719             locVertexData[0].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
720 
721             //    BOTLEFT
722             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
723             locVertexData[1].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
724 
725             //    TOPRIGHT
726             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
727             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
728 
729             //    BOTRIGHT
730             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
731             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
732         } else {
733             if (!this._vertexData) {
734                 this._vertexDataCount = 8;
735                 var rVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, rLocCount = 8;
736                 this._vertexArrayBuffer = new ArrayBuffer(rLocCount * rVertexDataLen);
737                 var rTempData = [];
738                 for (i = 0; i < rLocCount; i++)
739                     rTempData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * rVertexDataLen);
740                 //    TOPLEFT 1
741                 rTempData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 1));
742                 rTempData[0].vertices = this._vertexFromAlphaPoint(cc.p(0, 1));
743 
744                 //    BOTLEFT 1
745                 rTempData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 0));
746                 rTempData[1].vertices = this._vertexFromAlphaPoint(cc.p(0, 0));
747 
748                 //    TOPRIGHT 2
749                 rTempData[6].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 1));
750                 rTempData[6].vertices = this._vertexFromAlphaPoint(cc.p(1, 1));
751 
752                 //    BOTRIGHT 2
753                 rTempData[7].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 0));
754                 rTempData[7].vertices = this._vertexFromAlphaPoint(cc.p(1, 0));
755 
756                 this._vertexData = rTempData;
757             }
758 
759             locVertexData = this._vertexData;
760             //    TOPRIGHT 1
761             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
762             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
763 
764             //    BOTRIGHT 1
765             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
766             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
767 
768             //    TOPLEFT 2
769             locVertexData[4].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
770             locVertexData[4].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
771 
772             //    BOTLEFT 2
773             locVertexData[5].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
774             locVertexData[5].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
775         }
776         this._updateColor();
777     },
778 
779     _updateColor:function () {
780         if (!this._sprite || !this._vertexData)
781             return;
782 
783         var sc = this._sprite.quad.tl.colors;
784         var locVertexData = this._vertexData;
785         for (var i = 0, len = this._vertexDataCount; i < len; ++i)
786             locVertexData[i].colors = sc;
787         this._vertexDataDirty = true;
788     },
789 
790     _updateProgress:null,
791 
792     _updateProgressForCanvas:function () {
793         var locSprite = this._sprite;
794         var sw = locSprite.width, sh = locSprite.height;
795         var locMidPoint = this._midPoint;
796 
797         if (this._type == cc.ProgressTimer.TYPE_RADIAL) {
798             this._radius = Math.round(Math.sqrt(sw * sw + sh * sh));
799             var locStartAngle, locEndAngle, locCounterClockWise = false, locOrigin = this._origin;
800             locOrigin.x = sw * locMidPoint.x;
801             locOrigin.y = -sh * locMidPoint.y;
802 
803             if (this._reverseDirection) {
804                 locEndAngle = 270;
805                 locStartAngle = 270 - 3.6 * this._percentage;
806             } else {
807                 locStartAngle = -90;
808                 locEndAngle = -90 + 3.6 * this._percentage;
809             }
810 
811             if (locSprite._flippedX) {
812                 locOrigin.x -= sw * (this._midPoint.x * 2);
813                 locStartAngle= -locStartAngle;
814                 locEndAngle= -locEndAngle;
815                 locStartAngle -= 180;
816                 locEndAngle -= 180;
817                 locCounterClockWise = !locCounterClockWise;
818             }
819             if (locSprite._flippedY) {
820                 locOrigin.y+=sh*(this._midPoint.y*2);
821                 locCounterClockWise = !locCounterClockWise;
822                 locStartAngle= -locStartAngle;
823                 locEndAngle= -locEndAngle;
824             }
825 
826             this._startAngle = locStartAngle;
827             this._endAngle = locEndAngle;
828             this._counterClockWise = locCounterClockWise;
829         } else {
830             var locBarChangeRate = this._barChangeRate;
831             var percentageF = this._percentage / 100;
832             var locBarRect = this._barRect;
833 
834             var drawedSize = cc.size((sw * (1 - locBarChangeRate.x)), (sh * (1 - locBarChangeRate.y)));
835             var drawingSize = cc.size((sw - drawedSize.width) * percentageF, (sh - drawedSize.height) * percentageF);
836             var currentDrawSize = cc.size(drawedSize.width + drawingSize.width, drawedSize.height + drawingSize.height);
837 
838             var startPoint = cc.p(sw * locMidPoint.x, sh * locMidPoint.y);
839 
840             var needToLeft = startPoint.x - currentDrawSize.width / 2;
841             if (locMidPoint.x > 0.5) {
842                 if (currentDrawSize.width / 2 >= sw - startPoint.x) {
843                     needToLeft = sw - currentDrawSize.width;
844                 }
845             }
846 
847             var needToTop = startPoint.y - currentDrawSize.height / 2;
848             if (locMidPoint.y > 0.5) {
849                 if (currentDrawSize.height / 2 >= sh - startPoint.y) {
850                     needToTop = sh - currentDrawSize.height;
851                 }
852             }
853 
854             //left pos
855             locBarRect.x = 0;
856             var flipXNeed = 1;
857             if (locSprite._flippedX) {
858                 locBarRect.x -= currentDrawSize.width;
859                 flipXNeed = -1;
860             }
861 
862             if (needToLeft > 0)
863                 locBarRect.x += needToLeft * flipXNeed;
864 
865             //right pos
866             locBarRect.y = 0;
867             var flipYNeed = 1;
868             if (locSprite._flippedY) {
869                 locBarRect.y += currentDrawSize.height;
870                 flipYNeed = -1;
871             }
872 
873             if (needToTop > 0)
874                 locBarRect.y -= needToTop * flipYNeed;
875 
876             //clip width and clip height
877             locBarRect.width = currentDrawSize.width;
878             locBarRect.height = -currentDrawSize.height;
879         }
880     },
881 
882     _updateProgressForWebGL:function () {
883         var locType = this._type;
884         if(locType === cc.ProgressTimer.TYPE_RADIAL)
885             this._updateRadial();
886         else if(locType === cc.ProgressTimer.TYPE_BAR)
887             this._updateBar();
888         this._vertexDataDirty = true;
889     }
890 });
891 
892 var _p = cc.ProgressTimer.prototype;
893 if(cc._renderType == cc._RENDER_TYPE_WEBGL){
894     _p.ctor = _p._ctorForWebGL;
895     _p.setReverseProgress = _p._setReverseProgressForWebGL;
896     _p.setSprite = _p._setSpriteForWebGL;
897     _p.setType = _p._setTypeForWebGL;
898     _p.setReverseDirection = _p._setReverseDirectionForWebGL;
899     _p.initWithSprite = _p._initWithSpriteForWebGL;
900     _p.draw = _p._drawForWebGL;
901     _p._updateProgress = _p._updateProgressForWebGL;
902 } else {
903     _p.ctor = _p._ctorForCanvas;
904     _p.setReverseProgress = _p._setReverseProgressForCanvas;
905     _p.setSprite = _p._setSpriteForCanvas;
906     _p.setType = _p._setTypeForCanvas;
907     _p.setReverseDirection = _p._setReverseDirectionForCanvas;
908     _p.initWithSprite = _p._initWithSpriteForCanvas;
909     _p.draw = _p._drawForCanvas;
910     _p._updateProgress = cc.ProgressTimer.prototype._updateProgressForCanvas;
911 }
912 
913 // Extended properties
914 /** @expose */
915 _p.midPoint;
916 cc.defineGetterSetter(_p, "midPoint", _p.getMidpoint, _p.setMidpoint);
917 /** @expose */
918 _p.barChangeRate;
919 cc.defineGetterSetter(_p, "barChangeRate", _p.getBarChangeRate, _p.setBarChangeRate);
920 /** @expose */
921 _p.type;
922 cc.defineGetterSetter(_p, "type", _p.getType, _p.setType);
923 /** @expose */
924 _p.percentage;
925 cc.defineGetterSetter(_p, "percentage", _p.getPercentage, _p.setPercentage);
926 /** @expose */
927 _p.sprite;
928 cc.defineGetterSetter(_p, "sprite", _p.getSprite, _p.setSprite);
929 /** @expose */
930 _p.reverseDir;
931 cc.defineGetterSetter(_p, "reverseDir", _p.isReverseDirection, _p.setReverseDirection);
932 
933 
934 /**
935  * create a progress timer object with image file name that renders the inner sprite according to the percentage
936  * @deprecated
937  * @param {cc.Sprite} sprite
938  * @return {cc.ProgressTimer}
939  * @example
940  * // Example
941  * var progress = cc.ProgressTimer.create('progress.png')
942  */
943 cc.ProgressTimer.create = function (sprite) {
944     return new cc.ProgressTimer(sprite);
945 };
946 
947 /**
948  * @constant
949  * @type Number
950  */
951 cc.ProgressTimer.TEXTURE_COORDS_COUNT = 4;
952 
953 /**
954  * @constant
955  * @type Number
956  */
957 cc.ProgressTimer.TEXTURE_COORDS = 0x4b;
958 
959 /**
960  * Radial Counter-Clockwise
961  * @type Number
962  * @constant
963  */
964 cc.ProgressTimer.TYPE_RADIAL = 0;
965 
966 /**
967  * Bar
968  * @type Number
969  * @constant
970  */
971 cc.ProgressTimer.TYPE_BAR = 1;
972