1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25 
 26  Use any of these editors to generate BMFonts:
 27  http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)
 28  http://www.n4te.com/hiero/hiero.jnlp (Free, Java)
 29  http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)
 30  http://www.angelcode.com/products/bmfont/ (Free, Windows only)
 31  ****************************************************************************/
 32 /**
 33  * @constant
 34  * @type Number
 35  */
 36 cc.LabelAutomaticWidth = -1;
 37 
 38 cc._KerningHashElement = function (key, amount) {
 39     this.key = key;   //key for the hash. 16-bit for 1st element, 16-bit for 2nd element
 40     this.amount = amount;
 41 };
 42 
 43 cc._FontDefHashElement = function (key, fontDef) {
 44     this.key = key || 0;        // key. Font Unicode value
 45     this.fontDef = fontDef || new cc._BMFontDef();    // font definition
 46 };
 47 
 48 cc._BMFontDef = function (charID, rect, xOffset, yOffset, xAdvance) {
 49     //! ID of the character
 50     this.charID = charID || 0;
 51     //! origin and size of the font
 52     this.rect = rect || cc.rect(0, 0, 0.1, 0.1);
 53     //! The X amount the image should be offset when drawing the image (in pixels)
 54     this.xOffset = xOffset || 0;
 55     //! The Y amount the image should be offset when drawing the image (in pixels)
 56     this.yOffset = yOffset || 0;
 57     //! The amount to move the current position after drawing the character (in pixels)
 58     this.xAdvance = xAdvance || 0;
 59 };
 60 
 61 cc._BMFontPadding = function (left, top, right, bottom) {
 62     /// padding left
 63     this.left = left || 0;
 64     /// padding top
 65     this.top = top || 0;
 66     /// padding right
 67     this.right = right || 0;
 68     /// padding bottom
 69     this.bottom = bottom || 0;
 70 };
 71 
 72 /**
 73  * cc.BMFontConfiguration has parsed _configuration of the the .fnt file
 74  * @class
 75  * @extends cc.Class
 76  */
 77 cc.BMFontConfiguration = cc.Class.extend(/** @lends cc.BMFontConfiguration# */{
 78     // XXX: Creating a public interface so that the bitmapFontArray[] is acc.esible
 79     //@public
 80     /**
 81      *  The characters building up the font
 82      *  @type object
 83      */
 84     bitmapFontArray:{},
 85 
 86     /**
 87      * FNTConfig: Common Height
 88      * @type Number
 89      */
 90     commonHeight:0,
 91 
 92     /**
 93      *  Padding
 94      *  @type cc._BMFontPadding
 95      */
 96     padding:new cc._BMFontPadding(),
 97 
 98     /**
 99      * atlas name
100      * @type String
101      */
102     atlasName:"",
103 
104     /**
105      * values for kerning
106      * @type cc._KerningHashElement
107      */
108     kerningDictionary:{},
109 
110     /**
111      * values for FontDef
112      * @type cc._FontDefHashElement
113      */
114     fontDefDictionary:null,
115     /**
116      * Constructor
117      */
118     ctor:function () {
119         this.fontDefDictionary = {};
120         this.fontDefDictionary["0"] = new cc._FontDefHashElement();
121     },
122 
123     /**
124      * Description of BMFontConfiguration
125      * @return {String}
126      */
127     description:function () {
128         return "<cc.BMFontConfiguration | Kernings:" + this.kerningDictionary + " | Image = " + this.atlasName.toString() + ">";
129     },
130 
131     /**
132      * @return {String}
133      */
134     getAtlasName:function () {
135         return this.atlasName;
136     },
137 
138     /**
139      * @param {String} atlasName
140      */
141     setAtlasName:function (atlasName) {
142         this.atlasName = atlasName;
143     },
144 
145     /**
146      * initializes a BitmapFontConfiguration with a FNT file
147      * @param {String} FNT file path
148      * @return {Boolean}
149      */
150     initWithFNTfile:function (FNTfile) {
151         cc.Assert(FNTfile != null && FNTfile.length != 0, "");
152         this._parseConfigFile(FNTfile);
153         return true;
154     },
155 
156     _parseConfigFile:function (controlFile) {
157         var data = cc.SAXParser.getInstance().getList(controlFile);
158         cc.Assert(data, "cc.BMFontConfiguration._parseConfigFile | Open file error.");
159 
160         // parse spacing / padding
161         var line, re;
162 
163         re = /padding+[a-z0-9\-= ",]+/gi;
164         line = re.exec(data)[0];
165         if (line) {
166             this._parseInfoArguments(line);
167         }
168 
169         re = /common lineHeight+[a-z0-9\-= ",]+/gi;
170         line = re.exec(data)[0];
171         if (line) {
172             this._parseCommonArguments(line);
173         }
174 
175         re = /page id=[a-zA-Z0-9\.\-= ",]+/gi;
176         line = re.exec(data)[0];
177         if (line) {
178             this._parseImageFileName(line, controlFile);
179         }
180 
181         re = /chars c+[a-z0-9\-= ",]+/gi;
182         line = re.exec(data)[0];
183         if (line) {
184             // Ignore this line
185         }
186 
187         re = /char id=\w[a-z0-9\-= ]+/gi;
188         line = data.match(re);
189         if (line) {
190             // Parse the current line and create a new CharDef
191             for (var i = 0; i < line.length; i++) {
192                 var element = new cc._FontDefHashElement();
193                 this._parseCharacterDefinition(line[i], element.fontDef);
194                 element.key = element.fontDef.charID;
195                 this.fontDefDictionary[element.key] = element;
196             }
197         }
198 
199         re = /kernings count+[a-z0-9\-= ",]+/gi;
200         if (re.test(data)) {
201             line = RegExp.$1[0];
202         }
203         if (line) {
204             this._parseKerningCapacity(line);
205         }
206 
207         re = /first=\w[a-z0-9\-= ]+/gi;
208         line = data.match(re);
209         if (line) {
210             for (var i = 0; i < line.length; i++) {
211                 this._parseKerningEntry(line[i]);
212             }
213         }
214     },
215 
216     _parseCharacterDefinition:function (line, characterDefinition) {
217         //////////////////////////////////////////////////////////////////////////
218         // line to parse:
219         // char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=44    xadvance=14     page=0  chnl=0
220         //////////////////////////////////////////////////////////////////////////
221         // Character ID
222         var value = /id=(\d+)/gi.exec(line)[1];
223         characterDefinition.charID = value.toString();
224 
225         // Character x
226         value = /x=([\-\d]+)/gi.exec(line)[1];
227         characterDefinition.rect.origin.x = parseInt(value);
228 
229         // Character y
230         value = /y=([\-\d]+)/gi.exec(line)[1];
231         characterDefinition.rect.origin.y = parseInt(value);
232 
233         // Character width
234         value = /width=([\-\d]+)/gi.exec(line)[1];
235         characterDefinition.rect.size.width = parseInt(value);
236 
237         // Character height
238         value = /height=([\-\d]+)/gi.exec(line)[1];
239         characterDefinition.rect.size.height = parseInt(value);
240 
241         // Character xoffset
242         value = /xoffset=([\-\d]+)/gi.exec(line)[1];
243         characterDefinition.xOffset = parseInt(value);
244 
245         // Character yoffset
246         value = /yoffset=([\-\d]+)/gi.exec(line)[1];
247         characterDefinition.yOffset = parseInt(value);
248 
249         // Character xadvance
250         value = /xadvance=([\-\d]+)/gi.exec(line)[1];
251         characterDefinition.xAdvance = parseInt(value);
252 
253     },
254 
255     _parseInfoArguments:function (line) {
256         //////////////////////////////////////////////////////////////////////////
257         // possible lines to parse:
258         // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0
259         // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
260         //////////////////////////////////////////////////////////////////////////
261 
262         // padding
263         var tmpPadding = /padding=(\d+)[,](\d+)[,](\d+)[,](\d+)/gi.exec(line);
264         this.padding.left = tmpPadding[1];
265         this.padding.top = tmpPadding[2]
266         this.padding.right = tmpPadding[3];
267         this.padding.bottom = tmpPadding[4];
268     },
269 
270     _parseCommonArguments:function (line) {
271         //////////////////////////////////////////////////////////////////////////
272         // line to parse:
273         // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0
274         //////////////////////////////////////////////////////////////////////////
275 
276         var value;
277         // Height
278         this.commonHeight = parseInt(/lineHeight=(\d+)/gi.exec(line)[1]);
279 
280         // pages. sanity check
281         value = /pages=(\d+)/gi.exec(line)[1];
282         cc.Assert(parseInt(value) == 1, "cc.BitfontAtlas: only supports 1 page");
283 
284         // packed (ignore) What does this mean ??
285     },
286 
287     _parseImageFileName:function (line, fntFile) {
288         //////////////////////////////////////////////////////////////////////////
289         // line to parse:
290         // page id=0 file="bitmapFontTest.png"
291         //////////////////////////////////////////////////////////////////////////
292         var value;
293         // page ID. Sanity check
294         value = /id=(\d+)/gi.exec(line)[1];
295         cc.Assert(parseInt(value) == 0, "LabelBMFont file could not be found");
296 
297         // file
298         value = /file="([a-zA-Z0-9\-\._]+)/gi.exec(line)[1];
299 
300         this.atlasName = cc.FileUtils.getInstance().fullPathFromRelativeFile(value, fntFile);
301     },
302 
303     _parseKerningCapacity:function (line) {
304     },
305 
306     _parseKerningEntry:function (line) {
307         //////////////////////////////////////////////////////////////////////////
308         // line to parse:
309         // kerning first=121  second=44  amount=-7
310         //////////////////////////////////////////////////////////////////////////
311         // first
312         var value = /first=([\-\d]+)/gi.exec(line)[1];
313         var first = parseInt(value);
314 
315         // second
316         value = /second=([\-\d]+)/gi.exec(line)[1];
317         var second = parseInt(value);
318 
319         // amount
320         value = /amount=([\-\d]+)/gi.exec(line)[1];
321         var amount = parseInt(value);
322 
323         var element = new cc._KerningHashElement();
324         element.amount = amount;
325         element.key = (first << 16) | (second & 0xffff);
326 
327         this.kerningDictionary[element.key] = element;
328 
329     },
330 
331     _purgeKerningDictionary:function () {
332         this.kerningDictionary = {};
333     },
334 
335     _purgeFontDefDictionary:function () {
336         this.fontDefDictionary = {
337             "0":{
338                 "key":"0",
339                 "fontDef":{
340                     "charID":"0",
341                     "rect":{
342                         "origin":{
343                             "x":0,
344                             "y":0
345                         },
346                         "size":{
347                             "width":1,
348                             "height":1
349                         }
350                     },
351                     "xOffset":0,
352                     "yOffset":0,
353                     "xAdvance":0}
354             }
355         };
356     }
357 });
358 
359 /**
360  * Create a cc.BMFontConfiguration
361  * @param {String} FNTfile
362  * @return {cc.BMFontConfiguration|Null} returns the configuration or null if error
363  * @example
364  * // Example
365  * var conf = cc.BMFontConfiguration.create('myfont.fnt');
366  */
367 cc.BMFontConfiguration.create = function (FNTfile) {
368     var ret = new cc.BMFontConfiguration();
369     if (ret.initWithFNTfile(FNTfile)) {
370         return ret;
371     }
372     return null;
373 };
374 
375 /**
376  * <p>cc.LabelBMFont is a subclass of cc.SpriteSheet.</p>
377  *
378  * <p>Features:<br/>
379  * <ul><li>- Treats each character like a cc.Sprite. This means that each individual character can be:</li>
380  * <li>- rotated</li>
381  * <li>- scaled</li>
382  * <li>- translated</li>
383  * <li>- tinted</li>
384  * <li>- chage the opacity</li>
385  * <li>- It can be used as part of a menu item.</li>
386  * <li>- anchorPoint can be used to align the "label"</li>
387  * <li>- Supports AngelCode text format</li></ul></p>
388  *
389  * <p>Limitations:<br/>
390  * - All inner characters are using an anchorPoint of (0.5, 0.5) and it is not recommend to change it
391  * because it might affect the rendering</p>
392  *
393  * <p>cc.LabelBMFont implements the protocol cc.LabelProtocol, like cc.Label and cc.LabelAtlas.<br/>
394  * cc.LabelBMFont has the flexibility of cc.Label, the speed of cc.LabelAtlas and all the features of cc.Sprite.<br/>
395  * If in doubt, use cc.LabelBMFont instead of cc.LabelAtlas / cc.Label.</p>
396  *
397  * <p>Supported editors:<br/>
398  * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)<br/>
399  * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)<br/>
400  * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)<br/>
401  * http://www.angelcode.com/products/bmfont/ (Free, Windows only)</p>
402  * @class
403  * @extends cc.
404  */
405 cc.LabelBMFont = cc.SpriteBatchNode.extend(/** @lends cc.LabelBMFont# */{
406     RGBAProtocol:true,
407     _opacity:0,
408     _color:null,
409     _opacityModifyRGB:false,
410     _string:"",
411     _configuration:null,
412     // name of fntFile
413     _fntFile:"",
414     // initial string without line breaks
415     _initialString:"",
416     // alignment of all lines
417     _alignment:null,
418     // max width until a line break is added
419     _width:0,
420     _lineBreakWithoutSpaces:false,
421     _imageOffset:cc.PointZero(),
422     /**
423      * Constructor
424      */
425     /*    ctor:function () {
426      this._super();
427      },*/
428     /**
429      * @param {CanvasContext} ctx
430      */
431     draw:function (ctx) {
432         this._super();
433         var context = ctx || cc.renderContext;
434         //LabelBMFont - Debug draw
435         if (cc.LABELBMFONT_DEBUG_DRAW) {
436             var s = this.getContentSize();
437             var pos = cc.p(0 | ( -this._anchorPointInPoints.x), 0 | ( -this._anchorPointInPoints.y));
438             var vertices = [cc.p(pos.x, pos.y), cc.p(pos.x + s.width, pos.y), cc.p(pos.x + s.width, pos.y + s.height), cc.p(pos.x, pos.y + s.height)];
439             context.strokeStyle = "rgba(0,255,0,1)";
440             cc.drawingUtil.drawPoly(vertices, 4, true);
441         }
442     },
443 
444     /**
445      * conforms to cc.RGBAProtocol protocol
446      * @return {Number}
447      */
448     getOpacity:function () {
449         return this._opacity;
450     },
451 
452     /**
453      * set the opacity of this label
454      * @param {Number} Var
455      */
456     setOpacity:function (Var) {
457         this._opacity = Var;
458         if (this._children) {
459             for (var i = 0, len = this._children.length; i < len; i++) {
460                 var node = this._children[i];
461                 if (node) {
462                     node.setOpacity(this._opacity);
463                 }
464             }
465         }
466     },
467 
468     /**
469      * conforms to cc.RGBAProtocol protocol
470      * @return {cc.Color3B}
471      */
472     getColor:function () {
473         return this._color;
474     },
475 
476     /**
477      * tint this label
478      * @param {cc.Color3B} Var
479      */
480     setColor:function (color3) {
481         if ((this._color.r == color3.r) && (this._color.g == color3.g) && (this._color.b == color3.b)) {
482             return;
483         }
484         this._color = color3;
485         if (this.getTexture()) {
486             if (cc.renderContextType == cc.CANVAS) {
487                 var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(this._originalTexture);
488                 if (cacheTextureForColor) {
489                     //generate color texture cache
490                     var tx = this.getTexture();
491                     var textureRect = cc.rect(0, 0, tx.width, tx.height);
492                     var colorTexture = cc.generateTintImage(tx, cacheTextureForColor, this._color, textureRect);
493                     var img = new Image();
494                     img.src = colorTexture.toDataURL();
495                     this.setTexture(img);
496                     this.updateString(false);
497                 }
498             }
499         }
500     },
501 
502     /**
503      * conforms to cc.RGBAProtocol protocol
504      * @return {Boolean}
505      */
506     isOpacityModifyRGB:function () {
507         return this._opacityModifyRGB;
508     },
509 
510     /**
511      * @param {Boolean} Var
512      */
513     setOpacityModifyRGB:function (Var) {
514         this._opacityModifyRGB = Var;
515         if (this._children && this._children.length != 0) {
516             for (var i = 0, len = this._children.length; i < len; i++) {
517                 var node = this._children[i];
518                 if (node) {
519                     if (node.RGBAProtocol) {
520                         node.setOpacity(255);
521                     }
522                 }
523             }
524         }
525     },
526 
527     /**
528      *  init LabelBMFont
529      */
530     init:function () {
531         return this.initWithString(null, null, null, null, null);
532     },
533 
534     /**
535      * init a bitmap font altas with an initial string and the FNT file
536      * @param {String} str
537      * @param {String} fntFile
538      * @param {String} width
539      * @param {Number} alignment
540      * @param {Number} imageOffset
541      * @return {Boolean}
542      */
543     initWithString:function (str, fntFile, width, alignment, imageOffset) {
544         var theString = str;
545 
546         cc.Assert(!this._configuration, "re-init is no longer supported");
547 
548         var texture;
549         if (fntFile) {
550             var newConf = cc.FNTConfigLoadFile(fntFile);
551             cc.Assert(newConf, "cc.LabelBMFont: Impossible to create font. Please check file");
552             this._configuration = newConf;
553             this._fntFile = fntFile;
554             texture = cc.TextureCache.getInstance().addImage(this._configuration.getAtlasName());
555         }
556         else {
557             texture = new Image();
558         }
559 
560         if (theString == null) {
561             theString = "";
562         }
563 
564         if (this.initWithTexture(texture, theString.length)) {
565             this._alignment = alignment || cc.TEXT_ALIGNMENT_LEFT;
566             this._imageOffset = imageOffset || cc.PointZero();
567             this._width = width || cc.LabelAutomaticWidth;
568             this._opacity = 255;
569             this._color = cc.white();
570             this._contentSize = cc.SizeZero();
571             this.setString(theString);
572             this.setAnchorPoint(cc.p(0.5, 0.5));
573             return true;
574         }
575         return false;
576     },
577 
578     /**
579      * updates the font chars based on the string to render
580      */
581     createFontChars:function () {
582         var nextFontPositionX = 0;
583         var nextFontPositionY = 0;
584         var prev = -1;
585         var kerningAmount = 0;
586 
587         var tmpSize = cc.SizeZero();
588 
589         var longestLine = 0;
590         var totalHeight = 0;
591 
592         var quantityOfLines = 1;
593 
594         var stringLen = this._string.length;
595 
596         if (stringLen == 0) {
597             return;
598         }
599         var i;
600         for (i = 0; i < stringLen; i++) {
601             if (this._string.charCodeAt(i) == 10) {
602                 quantityOfLines++;
603             }
604         }
605 
606         totalHeight = this._configuration.commonHeight * quantityOfLines;
607         nextFontPositionY = -(this._configuration.commonHeight - this._configuration.commonHeight * quantityOfLines);
608 
609         for (i = 0; i < stringLen; i++) {
610             var key = this._string.charCodeAt(i);
611 
612             if (key == 10) {
613                 nextFontPositionX = 0;
614                 nextFontPositionY -= this._configuration.commonHeight;
615                 continue;
616             }
617 
618             var element = this._configuration.fontDefDictionary[key];
619             cc.Assert(element, "FontDefinition could not be found!");
620 
621             var fontDef = element.fontDef;
622 
623             var rect = cc.rect(fontDef.rect.origin.x, fontDef.rect.origin.y, fontDef.rect.size.width, fontDef.rect.size.height);
624             rect = cc.RECT_PIXELS_TO_POINTS(rect);
625             rect.origin.x += this._imageOffset.x;
626             rect.origin.y += this._imageOffset.y;
627 
628             var fontChar = this.getChildByTag(i);
629             if (!fontChar) {
630                 fontChar = new cc.Sprite();
631                 if (key == 32) {
632                     fontChar.init();
633                     fontChar.setTextureRect(cc.RectZero(), false, cc.SizeZero());
634                 } else {
635                     fontChar.initWithTexture(this._textureAtlas.getTexture(), rect, false);
636                 }
637                 this.addChild(fontChar, 0, i);
638             } else {
639                 if (key == 32) {
640                     fontChar.init();
641                     fontChar.setTextureRect(cc.RectZero(), false, cc.SizeZero());
642                 } else {
643                     // reusing fonts
644                     fontChar.initWithTexture(this._textureAtlas.getTexture(), rect, false);
645                     // restore to default in case they were modified
646                     fontChar.setVisible(true);
647                     fontChar.setOpacity(255);
648                 }
649             }
650 
651             var yOffset = this._configuration.commonHeight - fontDef.yOffset;
652             var fontPos = cc.p(nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width * 0.5 + kerningAmount,
653                 nextFontPositionY + yOffset - rect.size.height * 0.5 * cc.CONTENT_SCALE_FACTOR());
654             fontChar.setPosition(cc.POINT_PIXELS_TO_POINTS(fontPos));
655 
656             // update kerning
657             nextFontPositionX += fontDef.xAdvance + kerningAmount;
658             prev = key;
659 
660             // Apply label properties
661             fontChar.setOpacityModifyRGB(this._opacityModifyRGB);
662 
663             // only apply opacity if it is different than 255 )
664             if (this._opacity != 255) {
665                 fontChar.setOpacity(this._opacity);
666             }
667 
668             if (longestLine < nextFontPositionX) {
669                 longestLine = nextFontPositionX;
670             }
671         }
672 
673         tmpSize.width = longestLine;
674         tmpSize.height = totalHeight;
675         this.setContentSize(cc.SIZE_PIXELS_TO_POINTS(tmpSize));
676     },
677 
678     /**
679      * update String
680      * @param {Boolean} fromUpdate
681      */
682     updateString:function (fromUpdate) {
683         if (this._children) {
684             for (var i = 0; i < this._children.length; i++) {
685                 var node = this._children[i];
686                 if (node) {
687                     node.setVisible(false);
688                 }
689             }
690         }
691         if (this._configuration) {
692             this.createFontChars();
693         }
694         if (!fromUpdate) {
695             this.updateLabel();
696         }
697     },
698 
699     /**
700      * get the text of this label
701      * @return {String}
702      */
703     getString:function () {
704         return this._initialString;
705     },
706 
707     /**
708      * set the text
709      * @param newString
710      */
711     setString:function (newString, fromUpdate) {
712         if (this._string != newString) {
713             this._string = newString + String.fromCharCode(0);
714             //  if(this._initialString == ""){
715             this._initialString = newString + String.fromCharCode(0);
716             //}
717             this.updateString(fromUpdate);
718         }
719     },
720 
721     /**
722      * @deprecated
723      * @param label
724      */
725     setCString:function (label) {
726         this.setString(label);
727     },
728 
729     /**
730      *  update Label
731      */
732     updateLabel:function () {
733         if (this._width > 0) {
734             this.setString(this._initialString, true);
735 
736             // Step 1: Make multiline
737             var stringLength = this._string.length;
738             var multiline_string = [];
739             var last_word = [];
740 
741             var line = 1, i = 0, start_line = false, start_word = false, startOfLine = -1, startOfWord = -1, skip = 0;
742 
743             var characterSprite;
744             for (var j = 0; j < this._children.length; j++) {
745                 while (!(characterSprite = this.getChildByTag(j + skip)))
746                     skip++;
747 
748                 if (!characterSprite.isVisible()) continue;
749                 if (i >= stringLength)
750                     break;
751 
752                 var character = this._string[i];
753 
754                 if (!start_word) {
755                     startOfWord = this._getLetterPosXLeft(characterSprite);
756                     start_word = true;
757                 }
758                 if (!start_line) {
759                     startOfLine = startOfWord;
760                     start_line = true;
761                 }
762 
763                 // Newline.
764                 if (character.charCodeAt(0) == 10) {
765                     last_word.push('\n');
766                     multiline_string = multiline_string.concat(last_word);
767                     last_word.length = 0;
768                     start_word = false;
769                     start_line = false;
770                     startOfWord = -1;
771                     startOfLine = -1;
772                     i++;
773                     line++;
774 
775                     if (i >= stringLength)
776                         break;
777 
778                     character = this._string[i];
779 
780                     if (!startOfWord) {
781                         startOfWord = this._getLetterPosXLeft(characterSprite);
782                         start_word = true;
783                     }
784                     if (!startOfLine) {
785                         startOfLine = startOfWord;
786                         start_line = true;
787                     }
788                 }
789 
790                 // Whitespace.
791                 if (character.charCodeAt(0) == 32) {
792                     last_word.push(character);
793                     multiline_string = multiline_string.concat(last_word);
794                     last_word.length = 0;
795                     start_word = false;
796                     startOfWord = -1;
797                     i++;
798                     continue;
799                 }
800 
801                 // Out of bounds.
802                 if (this._getLetterPosXRight(characterSprite) - startOfLine > this._width) {
803                     if (!this._lineBreakWithoutSpaces) {
804                         last_word.push(character);
805 
806                         var found = multiline_string.lastIndexOf(" ");
807                         if (found != -1)
808                             cc.utf8_trim_ws(multiline_string);
809                         else
810                             multiline_string = [];
811 
812                         if (multiline_string.length > 0)
813                             multiline_string.push('\n');
814 
815                         line++;
816                         start_line = false;
817                         startOfLine = -1;
818                         i++;
819                     }
820                     else {
821                         cc.utf8_trim_ws(last_word);
822 
823                         last_word.push('\n');
824                         multiline_string = multiline_string.concat(last_word);
825                         last_word.length = 0;
826                         start_word = false;
827                         start_line = false;
828                         startOfWord = -1;
829                         startOfLine = -1;
830                         line++;
831 
832                         if (i >= stringLength)
833                             break;
834 
835                         if (!startOfWord) {
836                             startOfWord = this._getLetterPosXLeft(characterSprite);
837                             start_word = true;
838                         }
839                         if (!startOfLine) {
840                             startOfLine = startOfWord;
841                             start_line = true;
842                         }
843 
844                         j--;
845                     }
846 
847                     continue;
848                 }
849                 else {
850                     // Character is normal.
851                     last_word.push(character);
852                     i++;
853                     continue;
854                 }
855             }
856 
857             multiline_string = multiline_string.concat(last_word);
858             var len = multiline_string.length;
859             var str_new = "";
860 
861             for (var i = 0; i < len; ++i) {
862                 str_new += multiline_string[i];
863             }
864 
865             this._string = str_new + String.fromCharCode(0);
866             this.updateString(true);
867         }
868 
869         // Step 2: Make alignment
870         if (this._alignment != cc.TEXT_ALIGNMENT_LEFT) {
871             var i = 0;
872 
873             var lineNumber = 0;
874             var strlen = this._string.length;
875             var last_line = [];
876 
877             for (var ctr = 0; ctr < strlen; ctr++) {
878                 if (this._string[ctr].charCodeAt(0) == 10 || this._string[ctr].charCodeAt(0) == 0) {
879                     var lineWidth = 0;
880                     var line_length = last_line.length;
881                     var index = i + line_length - 1 + lineNumber;
882                     if (index < 0) continue;
883 
884                     var lastChar = this.getChildByTag(index);
885                     if (lastChar == null)
886                         continue;
887                     lineWidth = lastChar.getPosition().x + lastChar.getContentSize().width / 2;
888 
889                     var shift = 0;
890                     switch (this._alignment) {
891                         case cc.TEXT_ALIGNMENT_CENTER:
892                             shift = this.getContentSize().width / 2 - lineWidth / 2;
893                             break;
894                         case cc.TEXT_ALIGNMENT_RIGHT:
895                             shift = this.getContentSize().width - lineWidth;
896                             break;
897                         default:
898                             break;
899                     }
900 
901                     if (shift != 0) {
902                         for (var j = 0; j < line_length; j++) {
903                             index = i + j + lineNumber;
904                             if (index < 0) continue;
905                             var characterSprite = this.getChildByTag(index);
906                             if(characterSprite){
907                                 characterSprite.setPosition(cc.pAdd(characterSprite.getPosition(), cc.p(shift, 0)));
908                             }
909                         }
910                     }
911 
912                     i += line_length;
913                     lineNumber++;
914 
915                     last_line.length = 0;
916                     continue;
917                 }
918 
919                 last_line.push(this._string[i]);
920             }
921         }
922     },
923 
924     /**
925      * Set text vertical alignment
926      * @param {Number} alignment
927      */
928     setAlignment:function (alignment) {
929         this._alignment = alignment;
930         this.updateLabel();
931     },
932 
933     /**
934      * @param {Number} width
935      */
936     setWidth:function (width) {
937         this._width = width;
938         this.updateLabel();
939     },
940 
941     /**
942      * @param {Boolean}  breakWithoutSpace
943      */
944     setLineBreakWithoutSpace:function (breakWithoutSpace) {
945         this._lineBreakWithoutSpaces = breakWithoutSpace;
946         this.updateLabel();
947     },
948 
949     /**
950      * @param {Number} scale
951      */
952     setScale:function (scale, scaleY) {
953         this._super(scale, scaleY);
954         this.updateLabel();
955     },
956 
957     /**
958      * @param {Number} scaleX
959      */
960     setScaleX:function (scaleX) {
961         this._super(scaleX);
962         this.updateLabel();
963     },
964 
965     /**
966      * @param {Number} scaleY
967      */
968     setScaleY:function (scaleY) {
969         this._super(scaleY);
970         this.updateLabel();
971     },
972     /**
973      * set fnt file path
974      * @param {String} fntFile
975      */
976     setFntFile:function (fntFile) {
977         if (fntFile != null && fntFile != this._fntFile) {
978             var newConf = cc.FNTConfigLoadFile(fntFile);
979 
980             cc.Assert(newConf, "cc.LabelBMFont: Impossible to create font. Please check file");
981 
982             this._fntFile = fntFile;
983             this._configuration = newConf;
984 
985             this.setTexture(cc.TextureCache.getInstance().addImage(this._configuration.getAtlasName()));
986             if (cc.renderContextType == cc.CANVAS) {
987                 this._originalTexture = this.getTexture();
988             }
989             this.createFontChars();
990         }
991     },
992 
993     /**
994      * @return {String}
995      */
996     getFntFile:function () {
997         return this._fntFile;
998     },
999 
1000     /**
1001      * set the anchorpoint of the label
1002      * @param {cc.Point} point
1003      */
1004     setAnchorPoint:function (point) {
1005         if (!cc.Point.CCPointEqualToPoint(point, this._anchorPoint)) {
1006             this._super(point);
1007             this.updateLabel();
1008         }
1009     },
1010 
1011     _atlasNameFromFntFile:function (fntFile) {
1012     },
1013     _kerningAmountForFirst:function (first, second) {
1014         var ret = 0;
1015         var key = (first << 16) | (second & 0xffff);
1016         if (this._configuration.kerningDictionary) {
1017             var element = this._configuration.kerningDictionary[key.toString()];
1018             if (element) {
1019                 ret = element.amount;
1020             }
1021         }
1022         return ret;
1023     },
1024     _getLetterPosXLeft:function (sp) {
1025         return sp.getPosition().x * this._scaleX + (sp.getContentSize().width * this._scaleX * sp.getAnchorPoint().x);
1026     },
1027     _getLetterPosXRight:function (sp) {
1028         return sp.getPosition().x * this._scaleX - (sp.getContentSize().width * this._scaleX * sp.getAnchorPoint().x);
1029     }
1030 });
1031 
1032 /**
1033  * creates a bitmap font altas with an initial string and the FNT file
1034  * @param {String} str
1035  * @param {String} fntFile
1036  * @param {String} width
1037  * @param {Number} alignment
1038  * @param {Number} imageOffset
1039  * @return {cc.LabelBMFont|Null}
1040  * @example
1041  * // Example 01
1042  * var label1 = cc.LabelBMFont.create("Test case", "test.fnt");
1043  *
1044  * // Example 02
1045  * var label2 = cc.LabelBMFont.create("test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT);
1046  *
1047  * // Example 03
1048  * var label3 = cc.LabelBMFont.create("This is a \n test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT, cc.PointZero());
1049  */
1050 cc.LabelBMFont.create = function (str, fntFile, width, alignment, imageOffset) {
1051     var ret = new cc.LabelBMFont();
1052     if(arguments.length == 0){
1053         if(ret && ret.init()){
1054             return ret;
1055         }
1056         return null;
1057     }
1058 
1059     if (ret && ret.initWithString(str, fntFile, width, alignment, imageOffset)) {
1060         return ret;
1061     }
1062     return null;
1063 };
1064 
1065 /**
1066  * shared instance of configuration
1067  * @type cc.BMFontConfiguration
1068  */
1069 cc.configurations = null;
1070 
1071 /**
1072  * Load the .fnt file
1073  * @param {String} fntFile
1074  * @return {cc.BMFontConfiguration}
1075  * Constructor
1076  */
1077 cc.FNTConfigLoadFile = function (fntFile) {
1078     if (!cc.configurations) {
1079         cc.configurations = {};
1080     }
1081     var ret = cc.configurations[fntFile];
1082     if (!ret) {
1083         ret = cc.BMFontConfiguration.create(fntFile);
1084     }
1085     return ret;
1086 };
1087 
1088 /**
1089  * Purges the cached .fnt data
1090  */
1091 cc.purgeCachedData = function () {
1092     cc.FNTConfigRemoveCache();
1093 };
1094 
1095 /**
1096  * Purges the FNT config cache
1097  */
1098 cc.FNTConfigRemoveCache = function () {
1099     if (cc.configurations) {
1100         cc.configurations = {};
1101     }
1102 };
1103 
1104 /**
1105  * @param {String} ch
1106  * @return {Boolean}  weather the character is a whitespace character.
1107  */
1108 cc.isspace_unicode = function (ch) {
1109     ch = ch.charCodeAt(0);
1110     return  ((ch >= 9 && ch <= 13) || ch == 32 || ch == 133 || ch == 160 || ch == 5760
1111         || (ch >= 8192 && ch <= 8202) || ch == 8232 || ch == 8233 || ch == 8239
1112         || ch == 8287 || ch == 12288)
1113 };
1114 
1115 /**
1116  * @param {String} str
1117  */
1118 cc.utf8_trim_ws = function (str) {
1119     var len = str.length;
1120 
1121     if (len <= 0)
1122         return;
1123 
1124     var last_index = len - 1;
1125 
1126     // Only start trimming if the last character is whitespace..
1127     if (cc.isspace_unicode(str[last_index])) {
1128         for (var i = last_index - 1; i >= 0; --i) {
1129             if (cc.isspace_unicode(str[i])) {
1130                 last_index = i;
1131             }
1132             else {
1133                 break;
1134             }
1135         }
1136         cc.utf8_trim_from(str, last_index);
1137     }
1138 };
1139 
1140 /**
1141  * Trims str st str=[0, index) after the operation.
1142  * Return value: the trimmed string.
1143  * @param {String} str  he string to trim
1144  * @param {Number} index  the index to start trimming from.
1145  */
1146 cc.utf8_trim_from = function (str, index) {
1147     var len = str.length;
1148     if (index >= len || index < 0)
1149         return;
1150     str.splice(index, len);
1151 };
1152