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) 2012 Neofect. All rights reserved.
  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  Created by Jung Sang-Taik on 2012-03-16
 28  ****************************************************************************/
 29 
 30 /**
 31  * A 9-slice sprite for cocos2d.
 32  *
 33  * 9-slice scaling allows you to specify how scaling is applied
 34  * to specific areas of a sprite. With 9-slice scaling (3x3 grid),
 35  * you can ensure that the sprite does not become distorted when
 36  * scaled.
 37  *
 38  * @see http://yannickloriot.com/library/ios/cccontrolextension/Classes/CCScale9Sprite.html
 39  * @class
 40  * @extends cc.NodeRGBA
 41  *
 42  * @property {cc.Size}  preferredSize   - The preferred size of the 9-slice sprite
 43  * @property {cc.Rect}  capInsets       - The cap insets of the 9-slice sprite
 44  * @property {Number}   insetLeft       - The left inset of the 9-slice sprite
 45  * @property {Number}   insetTop        - The top inset of the 9-slice sprite
 46  * @property {Number}   insetRight      - The right inset of the 9-slice sprite
 47  * @property {Number}   insetBottom     - The bottom inset of the 9-slice sprite
 48  */
 49 cc.Scale9Sprite = cc.NodeRGBA.extend(/** @lends cc.Scale9Sprite# */{
 50     RGBAProtocol: true,
 51 
 52     _spriteRect: null,
 53     _capInsetsInternal: null,
 54     _positionsAreDirty: false,
 55 
 56     _scale9Image: null,
 57     _topLeft: null,
 58     _top: null,
 59     _topRight: null,
 60     _left: null,
 61     _centre: null,
 62     _right: null,
 63     _bottomLeft: null,
 64     _bottom: null,
 65     _bottomRight: null,
 66 
 67     _colorUnmodified: null,
 68     _opacityModifyRGB: false,
 69 
 70     _originalSize: null,
 71     _preferredSize: null,
 72     _opacity: 0,
 73     _color: null,
 74     _capInsets: null,
 75     _insetLeft: 0,
 76     _insetTop: 0,
 77     _insetRight: 0,
 78     _insetBottom: 0,
 79 
 80     _spritesGenerated: false,
 81     _spriteFrameRotated: false,
 82     _textureLoaded:false,
 83     _loadedEventListeners: null,
 84     _className:"Scale9Sprite",
 85 
 86     /**
 87      * return  texture is loaded
 88      * @returns {boolean}
 89      */
 90     textureLoaded:function(){
 91         return this._textureLoaded;
 92     },
 93 
 94     /**
 95      * add texture loaded event listener
 96      * @param {Function} callback
 97      * @param {Object} target
 98      */
 99     addLoadedEventListener:function(callback, target){
100         this._loadedEventListeners.push({eventCallback:callback, eventTarget:target});
101     },
102 
103     _callLoadedEventCallbacks:function(){
104         this._textureLoaded = true;
105         var locListeners = this._loadedEventListeners;
106         for(var i = 0, len = locListeners.length;  i < len; i++){
107             var selCallback = locListeners[i];
108             selCallback.eventCallback.call(selCallback.eventTarget, this);
109         }
110         locListeners.length = 0;
111     },
112 
113     _updateCapInset: function () {
114         var insets, locInsetLeft = this._insetLeft, locInsetTop = this._insetTop, locInsetRight = this._insetRight;
115         var locSpriteRect = this._spriteRect, locInsetBottom = this._insetBottom;
116         if (locInsetLeft === 0 && locInsetTop === 0 && locInsetRight === 0 && locInsetBottom === 0) {
117             insets = cc.rect(0, 0, 0, 0);
118         } else {
119             insets = this._spriteFrameRotated ? cc.rect(locInsetBottom, locInsetLeft,
120                 locSpriteRect.width - locInsetRight - locInsetLeft,
121                 locSpriteRect.height - locInsetTop - locInsetBottom) :
122                 cc.rect(locInsetLeft, locInsetTop,
123                     locSpriteRect.width - locInsetLeft - locInsetRight,
124                     locSpriteRect.height - locInsetTop - locInsetBottom);
125         }
126         this.setCapInsets(insets);
127     },
128 
129     _updatePositions: function () {
130         // Check that instances are non-NULL
131         if (!((this._topLeft) && (this._topRight) && (this._bottomRight) &&
132             (this._bottomLeft) && (this._centre))) {
133             // if any of the above sprites are NULL, return
134             return;
135         }
136 
137         var size = this._contentSize;
138         var locTopLeft = this._topLeft, locTopRight = this._topRight, locBottomRight = this._bottomRight, locBottomLeft = this._bottomLeft;
139         var locCenter = this._centre, locCenterContentSize = this._centre.getContentSize();
140         var locTopLeftContentSize = locTopLeft.getContentSize();
141         var locBottomLeftContentSize = locBottomLeft.getContentSize();
142 
143         var sizableWidth = size.width - locTopLeftContentSize.width - locTopRight.getContentSize().width;
144         var sizableHeight = size.height - locTopLeftContentSize.height - locBottomRight.getContentSize().height;
145         var horizontalScale = sizableWidth / locCenterContentSize.width;
146         var verticalScale = sizableHeight / locCenterContentSize.height;
147         var rescaledWidth = locCenterContentSize.width * horizontalScale;
148         var rescaledHeight = locCenterContentSize.height * verticalScale;
149 
150         var leftWidth = locBottomLeftContentSize.width;
151         var bottomHeight = locBottomLeftContentSize.height;
152 
153         if(cc._renderType == cc._RENDER_TYPE_WEBGL){
154             //browser is in canvas mode, need to manually control rounding to prevent overlapping pixels
155             var roundedRescaledWidth = Math.round(rescaledWidth);
156             if(rescaledWidth != roundedRescaledWidth) {
157                 rescaledWidth = roundedRescaledWidth;
158                 horizontalScale = rescaledWidth/locCenterContentSize.width;
159             }
160             var roundedRescaledHeight = Math.round(rescaledHeight);
161             if(rescaledHeight != roundedRescaledHeight) {
162                 rescaledHeight = roundedRescaledHeight;
163                 verticalScale = rescaledHeight/locCenterContentSize.height;
164             }
165         }
166         locCenter.setScaleX(horizontalScale);
167         locCenter.setScaleY(verticalScale);
168 
169         var locLeft = this._left, locRight = this._right, locTop = this._top, locBottom = this._bottom;
170         var tempAP = cc.p(0, 0);
171         locBottomLeft.setAnchorPoint(tempAP);
172         locBottomRight.setAnchorPoint(tempAP);
173         locTopLeft.setAnchorPoint(tempAP);
174         locTopRight.setAnchorPoint(tempAP);
175         locLeft.setAnchorPoint(tempAP);
176         locRight.setAnchorPoint(tempAP);
177         locTop.setAnchorPoint(tempAP);
178         locBottom.setAnchorPoint(tempAP);
179         locCenter.setAnchorPoint(tempAP);
180 
181         // Position corners
182         locBottomLeft.setPosition(0, 0);
183         locBottomRight.setPosition(leftWidth + rescaledWidth, 0);
184         locTopLeft.setPosition(0, bottomHeight + rescaledHeight);
185         locTopRight.setPosition(leftWidth + rescaledWidth, bottomHeight + rescaledHeight);
186 
187         // Scale and position borders
188         locLeft.setPosition(0, bottomHeight);
189         locLeft.setScaleY(verticalScale);
190         locRight.setPosition(leftWidth + rescaledWidth, bottomHeight);
191         locRight.setScaleY(verticalScale);
192         locBottom.setPosition(leftWidth, 0);
193         locBottom.setScaleX(horizontalScale);
194         locTop.setPosition(leftWidth, bottomHeight + rescaledHeight);
195         locTop.setScaleX(horizontalScale);
196 
197         // Position centre
198         locCenter.setPosition(leftWidth, bottomHeight);
199     },
200 
201     ctor: function () {
202         cc.NodeRGBA.prototype.ctor.call(this);
203         this._spriteRect = cc.rect(0, 0, 0, 0);
204         this._capInsetsInternal = cc.rect(0, 0, 0, 0);
205 
206         this._colorUnmodified = cc.color(255, 255, 255, 255);
207         this._originalSize = cc.size(0, 0);
208         this._preferredSize = cc.size(0, 0);
209         this._color = cc.color(255, 255, 255, 255);
210         this._opacity = 255;
211         this._capInsets = cc.rect(0, 0, 0, 0);
212         this._loadedEventListeners = [];
213     },
214 
215     /** Original sprite's size. */
216     getOriginalSize: function () {
217         return this._originalSize;
218     },
219 
220     //if the preferredSize component is given as -1, it is ignored
221     getPreferredSize: function () {
222         return this._preferredSize;
223     },
224 	_getPreferredWidth: function () {
225 		return this._preferredSize.width;
226 	},
227 	_getPreferredHeight: function () {
228 		return this._preferredSize.height;
229 	},
230     setPreferredSize: function (preferredSize) {
231         this.setContentSize(preferredSize);
232         this._preferredSize = preferredSize;
233     },
234 	_setPreferredWidth: function (value) {
235 		this._setWidth(value);
236 		this._preferredSize.width = value;
237 	},
238 	_setPreferredHeight: function (value) {
239 		this._setHeight(value);
240 		this._preferredSize.height = value;
241 	},
242 
243     /** Opacity: conforms to CCRGBAProtocol protocol */
244     getOpacity: function () {
245         return this._opacity;
246     },
247     setOpacity: function (opacity) {
248         if(!this._scale9Image){
249             return;
250         }
251         this._opacity = opacity;
252         var scaleChildren = this._scale9Image.getChildren();
253         for (var i = 0; i < scaleChildren.length; i++) {
254             var selChild = scaleChildren[i];
255             if (selChild && selChild.RGBAProtocol)
256                 selChild.setOpacity(opacity);
257         }
258         this._color.a = opacity;
259     },
260 
261     updateDisplayedOpacity: function(parentOpacity){
262         cc.NodeRGBA.prototype.updateDisplayedOpacity.call(this, parentOpacity);
263         this.setOpacity(this._displayedOpacity);
264     },
265 
266     /** Color: conforms to CCRGBAProtocol protocol */
267     getColor: function () {
268         var locColor = this._color;
269         return cc.color(locColor.r, locColor.g, locColor.b, locColor.a);
270     },
271     setColor: function (color) {
272         if(!this._scale9Image){
273             return;
274         }
275         var locColor = this._color;
276         locColor.r = color.r;
277         locColor.g = color.g;
278         locColor.b = color.b;
279 
280         var scaleChildren = this._scale9Image.getChildren();
281         for (var i = 0; i < scaleChildren.length; i++) {
282             var selChild = scaleChildren[i];
283             if (selChild && selChild.RGBAProtocol)
284                 selChild.setColor(color);
285         }
286 
287         if (color.a !== undefined && !color.a_undefined) {
288             this.setOpacity(color.a);
289         }
290     },
291 
292     updateDisplayedColor: function(parentColor){
293         cc.NodeRGBA.prototype.updateDisplayedColor.call(this, parentColor);
294         this.setColor(this._displayedColor);
295     },
296 
297     getCapInsets: function () {
298         return this._capInsets;
299     },
300 
301     setCapInsets: function (capInsets) {
302         if(!this._scale9Image){
303             return;
304         }
305         //backup the contentSize
306         var contentSize = this._contentSize;
307         var tempWidth = contentSize.width, tempHeight = contentSize.height;
308 
309         this.updateWithBatchNode(this._scale9Image, this._spriteRect, this._spriteFrameRotated, capInsets);
310         //restore the contentSize
311         this.setContentSize(tempWidth, tempHeight);
312     },
313 
314     /**
315      * Gets the left side inset
316      * @returns {number}
317      */
318     getInsetLeft: function () {
319         return this._insetLeft;
320     },
321 
322     /**
323      * Sets the left side inset
324      * @param {Number} insetLeft
325      */
326     setInsetLeft: function (insetLeft) {
327         this._insetLeft = insetLeft;
328         this._updateCapInset();
329     },
330 
331     /**
332      * Gets the top side inset
333      * @returns {number}
334      */
335     getInsetTop: function () {
336         return this._insetTop;
337     },
338 
339     /**
340      * Sets the top side inset
341      * @param {Number} insetTop
342      */
343     setInsetTop: function (insetTop) {
344         this._insetTop = insetTop;
345         this._updateCapInset();
346     },
347 
348     /**
349      * Gets the right side inset
350      * @returns {number}
351      */
352     getInsetRight: function () {
353         return this._insetRight;
354     },
355     /**
356      * Sets the right side inset
357      * @param {Number} insetRight
358      */
359     setInsetRight: function (insetRight) {
360         this._insetRight = insetRight;
361         this._updateCapInset();
362     },
363 
364     /**
365      * Gets the bottom side inset
366      * @returns {number}
367      */
368     getInsetBottom: function () {
369         return this._insetBottom;
370     },
371     /**
372      * Sets the bottom side inset
373      * @param {number} insetBottom
374      */
375     setInsetBottom: function (insetBottom) {
376         this._insetBottom = insetBottom;
377         this._updateCapInset();
378     },
379 
380     /**
381      * Sets the untransformed size of the Scale9Sprite.
382      * @override
383      * @param {cc.Size|Number} size The untransformed size of the Scale9Sprite or The untransformed size's width of the Scale9Sprite.
384      * @param {Number} [height] The untransformed size's height of the Scale9Sprite.
385      */
386     setContentSize: function (size, height) {
387 	    cc.Node.prototype.setContentSize.call(this, size, height);
388         this._positionsAreDirty = true;
389     },
390 	_setWidth: function (value) {
391 		cc.Node.prototype._setWidth.call(this, value);
392 		this._positionsAreDirty = true;
393 	},
394 	_setHeight: function (value) {
395 		cc.Node.prototype._setHeight.call(this, value);
396 		this._positionsAreDirty = true;
397 	},
398 
399     visit: function (ctx) {
400         if (this._positionsAreDirty) {
401             this._updatePositions();
402             this._positionsAreDirty = false;
403         }
404         cc.NodeRGBA.prototype.visit.call(this, ctx);
405     },
406 
407     init: function () {
408         return this.initWithBatchNode(null, cc.rect(0, 0, 0, 0), false, cc.rect(0, 0, 0, 0));
409     },
410 
411     initWithBatchNode: function (batchNode, rect, rotated, capInsets) {
412         if (capInsets === undefined) {
413             capInsets = rotated;
414             rotated = false;
415         }
416 
417         if (batchNode) {
418             this.updateWithBatchNode(batchNode, rect, rotated, capInsets);
419         }
420         this.setAnchorPoint(0.5, 0.5);
421         this._positionsAreDirty = true;
422         return true;
423     },
424 
425     /**
426      * Initializes a 9-slice sprite with a texture file, a delimitation zone and
427      * with the specified cap insets.
428      * Once the sprite is created, you can then call its "setContentSize:" method
429      * to resize the sprite will all it's 9-slice goodness intact.
430      * It respects the anchorPoint too.
431      *
432      * @param file The name of the texture file.
433      * @param rect The rectangle that describes the sub-part of the texture that
434      * is the whole image. If the shape is the whole texture, set this to the
435      * texture's full rect.
436      * @param capInsets The values to use for the cap insets.
437      */
438     initWithFile: function (file, rect, capInsets) {
439         if (file instanceof cc.Rect) {
440             file = arguments[1];
441             capInsets = arguments[0];
442             rect = cc.rect(0, 0, 0, 0);
443         } else {
444             rect = rect || cc.rect(0, 0, 0, 0);
445             capInsets = capInsets || cc.rect(0, 0, 0, 0);
446         }
447 
448         if(!file)
449             throw "cc.Scale9Sprite.initWithFile(): file should be non-null";
450 
451         var texture = cc.textureCache.textureForKey(file);
452         if (!texture) {
453             texture = cc.textureCache.addImage(file);
454             var locLoaded = texture.isLoaded();
455             this._textureLoaded = locLoaded;
456             if(!locLoaded){
457                 texture.addLoadedEventListener(function(sender){
458                     // the texture is rotated on Canvas render mode, so isRotated always is false.
459                     var preferredSize = this._preferredSize;
460                     preferredSize = cc.size(preferredSize.width, preferredSize.height);
461                     var size  = sender.getContentSize();
462                     this.updateWithBatchNode(this._scale9Image, cc.rect(0,0,size.width,size.height), false, this._capInsets);
463                     this.setPreferredSize(preferredSize);
464                     this._positionsAreDirty = true;
465                     this._callLoadedEventCallbacks();
466                 }, this);
467             }
468         }
469         return this.initWithBatchNode(cc.SpriteBatchNode.create(file, 9), rect, false, capInsets);
470     },
471 
472     /**
473      * Initializes a 9-slice sprite with an sprite frame and with the specified
474      * cap insets.
475      * Once the sprite is created, you can then call its "setContentSize:" method
476      * to resize the sprite will all it's 9-slice goodness intract.
477      * It respects the anchorPoint too.
478      *
479      * @param spriteFrame The sprite frame object.
480      * @param capInsets The values to use for the cap insets.
481      */
482     initWithSpriteFrame: function (spriteFrame, capInsets) {
483         if(!spriteFrame || !spriteFrame.getTexture())
484             throw "cc.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null";
485 
486         capInsets = capInsets || cc.rect(0, 0, 0, 0);
487         var locLoaded = spriteFrame.textureLoaded();
488         this._textureLoaded = locLoaded;
489         if(!locLoaded){
490             spriteFrame.addLoadedEventListener(function(sender){
491                 // the texture is rotated on Canvas render mode, so isRotated always is false.
492                 var preferredSize = this._preferredSize;
493                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
494                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets);
495                 this.setPreferredSize(preferredSize);
496                 this._positionsAreDirty = true;
497                 this._callLoadedEventCallbacks();
498             },this);
499         }
500         var batchNode = cc.SpriteBatchNode.create(spriteFrame.getTexture(), 9);
501         // the texture is rotated on Canvas render mode, so isRotated always is false.
502         return this.initWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), capInsets);
503     },
504 
505     /**
506      * Initializes a 9-slice sprite with an sprite frame name and with the specified
507      * cap insets.
508      * Once the sprite is created, you can then call its "setContentSize:" method
509      * to resize the sprite will all it's 9-slice goodness intract.
510      * It respects the anchorPoint too.
511      *
512      * @param spriteFrameName The sprite frame name.
513      * @param capInsets The values to use for the cap insets.
514      */
515     initWithSpriteFrameName: function (spriteFrameName, capInsets) {
516         if(!spriteFrameName)
517             throw "cc.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null";
518         capInsets = capInsets || cc.rect(0, 0, 0, 0);
519 
520         var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName);
521         if (frame == null) {
522             cc.log("cc.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName");
523             return false;
524         }
525 
526         return this.initWithSpriteFrame(frame, capInsets);
527     },
528 
529     /**
530      * Creates and returns a new sprite object with the specified cap insets.
531      * You use this method to add cap insets to a sprite or to change the existing
532      * cap insets of a sprite. In both cases, you get back a new image and the
533      * original sprite remains untouched.
534      *
535      * @param capInsets The values to use for the cap insets.
536      */
537     resizableSpriteWithCapInsets: function (capInsets) {
538         var pReturn = new cc.Scale9Sprite();
539         if (pReturn && pReturn.initWithBatchNode(this._scale9Image, this._spriteRect, false, capInsets)) {
540             return pReturn;
541         }
542         return null;
543     },
544 
545     /** sets the premultipliedAlphaOpacity property.
546      If set to NO then opacity will be applied as: glColor(R,G,B,opacity);
547      If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity );
548      Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO
549      @since v0.8
550      */
551     setOpacityModifyRGB: function (value) {
552         if(!this._scale9Image){
553             return;
554         }
555         this._opacityModifyRGB = value;
556         var scaleChildren = this._scale9Image.getChildren();
557         if (scaleChildren) {
558             for (var i = 0, len = scaleChildren.length; i < len; i++)
559                 scaleChildren[i].setOpacityModifyRGB(value);
560         }
561     },
562 
563     /** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity);
564      @since v0.8
565      */
566     isOpacityModifyRGB: function () {
567         return this._opacityModifyRGB;
568     },
569 
570     updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) {
571         var opacity = this.getOpacity();
572         var color = this.getColor();
573         var rect = cc.rect(originalRect.x, originalRect.y, originalRect.width, originalRect.height);
574 
575         // Release old sprites
576         this.removeAllChildren(true);
577 
578         if (this._scale9Image != batchNode){
579             this._scale9Image = batchNode;
580         }
581         var tmpTexture = batchNode.getTexture();
582         var locLoaded = tmpTexture.isLoaded();
583         this._textureLoaded = locLoaded;
584         if(!locLoaded){
585             tmpTexture.addLoadedEventListener(function(sender){
586                 this._positionsAreDirty = true;
587                 this._callLoadedEventCallbacks();
588             },this);
589             return;
590         }
591         var locScale9Image = this._scale9Image;
592         locScale9Image.removeAllChildren(true);
593 
594         //this._capInsets = capInsets;
595         var locCapInsets = this._capInsets;
596         locCapInsets.x = capInsets.x;
597         locCapInsets.y = capInsets.y;
598         locCapInsets.width = capInsets.width;
599         locCapInsets.height = capInsets.height;
600         this._spriteFrameRotated = rotated;
601 
602         var selTexture = locScale9Image.getTexture();
603 
604         // If there is no given rect
605         if (cc._rectEqualToZero(rect)) {
606             // Get the texture size as original
607             var textureSize = selTexture.getContentSize();
608             rect = cc.rect(0, 0, textureSize.width, textureSize.height);
609         }
610 
611         // Set the given rect's size as original size
612         this._spriteRect = rect;
613         var locSpriteRect = this._spriteRect;
614         locSpriteRect.x = rect.x;
615         locSpriteRect.y = rect.y;
616         locSpriteRect.width = rect.width;
617         locSpriteRect.height = rect.height;
618 
619         this._originalSize.width = rect.width;
620         this._originalSize.height = rect.height;
621 
622         var locPreferredSize = this._preferredSize;
623         if(locPreferredSize.width === 0 && locPreferredSize.height === 0){
624             locPreferredSize.width = rect.width;
625             locPreferredSize.height = rect.height;
626         }
627 
628         var locCapInsetsInternal = this._capInsetsInternal;
629         if(capInsets){
630             locCapInsetsInternal.x = capInsets.x;
631             locCapInsetsInternal.y = capInsets.y;
632             locCapInsetsInternal.width = capInsets.width;
633             locCapInsetsInternal.height = capInsets.height;
634         }
635         var w = rect.width;
636         var h = rect.height;
637 
638         // If there is no specified center region
639         if (cc._rectEqualToZero(locCapInsetsInternal)) {
640             // CCLog("... cap insets not specified : using default cap insets ...");
641             locCapInsetsInternal.x = w / 3;
642             locCapInsetsInternal.y = h / 3;
643             locCapInsetsInternal.width = w / 3;
644             locCapInsetsInternal.height = h / 3;
645         }
646 
647         var left_w = locCapInsetsInternal.x;
648         var center_w = locCapInsetsInternal.width;
649         var right_w = w - (left_w + center_w);
650 
651         var top_h = locCapInsetsInternal.y;
652         var center_h = locCapInsetsInternal.height;
653         var bottom_h = h - (top_h + center_h);
654 
655         // calculate rects
656         // ... top row
657         var x = 0.0;
658         var y = 0.0;
659 
660         // top left
661         var lefttopbounds = cc.rect(x, y, left_w, top_h);
662 
663         // top center
664         x += left_w;
665         var centertopbounds = cc.rect(x, y, center_w, top_h);
666 
667         // top right
668         x += center_w;
669         var righttopbounds = cc.rect(x, y, right_w, top_h);
670 
671         // ... center row
672         x = 0.0;
673         y = 0.0;
674 
675         y += top_h;
676         // center left
677         var leftcenterbounds = cc.rect(x, y, left_w, center_h);
678 
679         // center center
680         x += left_w;
681         var centerbounds = cc.rect(x, y, center_w, center_h);
682 
683         // center right
684         x += center_w;
685         var rightcenterbounds = cc.rect(x, y, right_w, center_h);
686 
687         // ... bottom row
688         x = 0.0;
689         y = 0.0;
690         y += top_h;
691         y += center_h;
692 
693         // bottom left
694         var leftbottombounds = cc.rect(x, y, left_w, bottom_h);
695 
696         // bottom center
697         x += left_w;
698         var centerbottombounds = cc.rect(x, y, center_w, bottom_h);
699 
700         // bottom right
701         x += center_w;
702         var rightbottombounds = cc.rect(x, y, right_w, bottom_h);
703 
704         var t = cc.AffineTransformMakeIdentity();
705         if (!rotated) {
706             // CCLog("!rotated");
707             t = cc.AffineTransformTranslate(t, rect.x, rect.y);
708 
709             cc._RectApplyAffineTransformIn(centerbounds, t);
710             cc._RectApplyAffineTransformIn(rightbottombounds, t);
711             cc._RectApplyAffineTransformIn(leftbottombounds, t);
712             cc._RectApplyAffineTransformIn(righttopbounds, t);
713             cc._RectApplyAffineTransformIn(lefttopbounds, t);
714             cc._RectApplyAffineTransformIn(rightcenterbounds, t);
715             cc._RectApplyAffineTransformIn(leftcenterbounds, t);
716             cc._RectApplyAffineTransformIn(centerbottombounds, t);
717             cc._RectApplyAffineTransformIn(centertopbounds, t);
718 
719             // Centre
720             this._centre = new cc.Sprite();
721             this._centre.initWithTexture(selTexture, centerbounds);
722             locScale9Image.addChild(this._centre, 0, cc.Scale9Sprite.POSITIONS_CENTRE);
723 
724             // Top
725             this._top = new cc.Sprite();
726             this._top.initWithTexture(selTexture, centertopbounds);
727             locScale9Image.addChild(this._top, 1, cc.Scale9Sprite.POSITIONS_TOP);
728 
729             // Bottom
730             this._bottom = new cc.Sprite();
731             this._bottom.initWithTexture(selTexture, centerbottombounds);
732             locScale9Image.addChild(this._bottom, 1, cc.Scale9Sprite.POSITIONS_BOTTOM);
733 
734             // Left
735             this._left = new cc.Sprite();
736             this._left.initWithTexture(selTexture, leftcenterbounds);
737             locScale9Image.addChild(this._left, 1, cc.Scale9Sprite.POSITIONS_LEFT);
738 
739             // Right
740             this._right = new cc.Sprite();
741             this._right.initWithTexture(selTexture, rightcenterbounds);
742             locScale9Image.addChild(this._right, 1, cc.Scale9Sprite.POSITIONS_RIGHT);
743 
744             // Top left
745             this._topLeft = new cc.Sprite();
746             this._topLeft.initWithTexture(selTexture, lefttopbounds);
747             locScale9Image.addChild(this._topLeft, 2, cc.Scale9Sprite.POSITIONS_TOPLEFT);
748 
749             // Top right
750             this._topRight = new cc.Sprite();
751             this._topRight.initWithTexture(selTexture, righttopbounds);
752             locScale9Image.addChild(this._topRight, 2, cc.Scale9Sprite.POSITIONS_TOPRIGHT);
753 
754             // Bottom left
755             this._bottomLeft = new cc.Sprite();
756             this._bottomLeft.initWithTexture(selTexture, leftbottombounds);
757             locScale9Image.addChild(this._bottomLeft, 2, cc.Scale9Sprite.POSITIONS_BOTTOMLEFT);
758 
759             // Bottom right
760             this._bottomRight = new cc.Sprite();
761             this._bottomRight.initWithTexture(selTexture, rightbottombounds);
762             locScale9Image.addChild(this._bottomRight, 2, cc.Scale9Sprite.POSITIONS_BOTTOMRIGHT);
763         } else {
764             // set up transformation of coordinates
765             // to handle the case where the sprite is stored rotated
766             // in the spritesheet
767             // CCLog("rotated");
768             var rotatedcenterbounds = centerbounds;
769             var rotatedrightbottombounds = rightbottombounds;
770             var rotatedleftbottombounds = leftbottombounds;
771             var rotatedrighttopbounds = righttopbounds;
772             var rotatedlefttopbounds = lefttopbounds;
773             var rotatedrightcenterbounds = rightcenterbounds;
774             var rotatedleftcenterbounds = leftcenterbounds;
775             var rotatedcenterbottombounds = centerbottombounds;
776             var rotatedcentertopbounds = centertopbounds;
777 
778             t = cc.AffineTransformTranslate(t, rect.height + rect.x, rect.y);
779             t = cc.AffineTransformRotate(t, 1.57079633);
780 
781             centerbounds = cc.RectApplyAffineTransform(centerbounds, t);
782             rightbottombounds = cc.RectApplyAffineTransform(rightbottombounds, t);
783             leftbottombounds = cc.RectApplyAffineTransform(leftbottombounds, t);
784             righttopbounds = cc.RectApplyAffineTransform(righttopbounds, t);
785             lefttopbounds = cc.RectApplyAffineTransform(lefttopbounds, t);
786             rightcenterbounds = cc.RectApplyAffineTransform(rightcenterbounds, t);
787             leftcenterbounds = cc.RectApplyAffineTransform(leftcenterbounds, t);
788             centerbottombounds = cc.RectApplyAffineTransform(centerbottombounds, t);
789             centertopbounds = cc.RectApplyAffineTransform(centertopbounds, t);
790 
791             rotatedcenterbounds.x = centerbounds.x;
792             rotatedcenterbounds.y = centerbounds.y;
793 
794             rotatedrightbottombounds.x = rightbottombounds.x;
795             rotatedrightbottombounds.y = rightbottombounds.y;
796 
797             rotatedleftbottombounds.x = leftbottombounds.x;
798             rotatedleftbottombounds.y = leftbottombounds.y;
799 
800             rotatedrighttopbounds.x = righttopbounds.x;
801             rotatedrighttopbounds.y = righttopbounds.y;
802 
803             rotatedlefttopbounds.x = lefttopbounds.x;
804             rotatedlefttopbounds.y = lefttopbounds.y;
805 
806             rotatedrightcenterbounds.x = rightcenterbounds.x;
807             rotatedrightcenterbounds.y = rightcenterbounds.y;
808 
809             rotatedleftcenterbounds.x = leftcenterbounds.x;
810             rotatedleftcenterbounds.y = leftcenterbounds.y;
811 
812             rotatedcenterbottombounds.x = centerbottombounds.x;
813             rotatedcenterbottombounds.y = centerbottombounds.y;
814 
815             rotatedcentertopbounds.x = centertopbounds.x;
816             rotatedcentertopbounds.y = centertopbounds.y;
817 
818             // Centre
819             this._centre = new cc.Sprite();
820             this._centre.initWithTexture(selTexture, rotatedcenterbounds, true);
821             locScale9Image.addChild(this._centre, 0, cc.Scale9Sprite.POSITIONS_CENTRE);
822 
823             // Top
824             this._top = new cc.Sprite();
825             this._top.initWithTexture(selTexture, rotatedcentertopbounds, true);
826             locScale9Image.addChild(this._top, 1, cc.Scale9Sprite.POSITIONS_TOP);
827 
828             // Bottom
829             this._bottom = new cc.Sprite();
830             this._bottom.initWithTexture(selTexture, rotatedcenterbottombounds, true);
831             locScale9Image.addChild(this._bottom, 1, cc.Scale9Sprite.POSITIONS_BOTTOM);
832 
833             // Left
834             this._left = new cc.Sprite();
835             this._left.initWithTexture(selTexture, rotatedleftcenterbounds, true);
836             locScale9Image.addChild(this._left, 1, cc.Scale9Sprite.POSITIONS_LEFT);
837 
838             // Right
839             this._right = new cc.Sprite();
840             this._right.initWithTexture(selTexture, rotatedrightcenterbounds, true);
841             locScale9Image.addChild(this._right, 1, cc.Scale9Sprite.POSITIONS_RIGHT);
842 
843             // Top left
844             this._topLeft = new cc.Sprite();
845             this._topLeft.initWithTexture(selTexture, rotatedlefttopbounds, true);
846             locScale9Image.addChild(this._topLeft, 2, cc.Scale9Sprite.POSITIONS_TOPLEFT);
847 
848             // Top right
849             this._topRight = new cc.Sprite();
850             this._topRight.initWithTexture(selTexture, rotatedrighttopbounds, true);
851             locScale9Image.addChild(this._topRight, 2, cc.Scale9Sprite.POSITIONS_TOPRIGHT);
852 
853             // Bottom left
854             this._bottomLeft = new cc.Sprite();
855             this._bottomLeft.initWithTexture(selTexture, rotatedleftbottombounds, true);
856             locScale9Image.addChild(this._bottomLeft, 2, cc.Scale9Sprite.POSITIONS_BOTTOMLEFT);
857 
858             // Bottom right
859             this._bottomRight = new cc.Sprite();
860             this._bottomRight.initWithTexture(selTexture, rotatedrightbottombounds, true);
861             locScale9Image.addChild(this._bottomRight, 2, cc.Scale9Sprite.POSITIONS_BOTTOMRIGHT);
862         }
863 
864         this.setContentSize(rect);
865         this.addChild(locScale9Image);
866 
867         if (this._spritesGenerated) {
868             // Restore color and opacity
869             this.setOpacity(opacity);
870             if(color.r !== 255 || color.g !== 255 || color.b !== 255){
871                 this.setColor(color);
872             }
873         }
874         this._spritesGenerated = true;
875         return true;
876     },
877 
878     setSpriteFrame: function (spriteFrame) {
879         var batchNode = cc.SpriteBatchNode.create(spriteFrame.getTexture(), 9);
880         // the texture is rotated on Canvas render mode, so isRotated always is false.
881         var locLoaded = spriteFrame.textureLoaded();
882         this._textureLoaded = locLoaded;
883         if(!locLoaded){
884             spriteFrame.addLoadedEventListener(function(sender){
885                 // the texture is rotated on Canvas render mode, so isRotated always is false.
886                 var preferredSize = this._preferredSize;
887                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
888                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets);
889                 this.setPreferredSize(preferredSize);
890                 this._positionsAreDirty = true;
891                 this._callLoadedEventCallbacks();
892             },this);
893         }
894         this.updateWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), cc.rect(0, 0, 0, 0));
895 
896         // Reset insets
897         this._insetLeft = 0;
898         this._insetTop = 0;
899         this._insetRight = 0;
900         this._insetBottom = 0;
901     }
902 });
903 
904 var _p = cc.Scale9Sprite.prototype;
905 
906 // Extended properties
907 /** @expose */
908 _p.preferredSize;
909 cc.defineGetterSetter(_p, "preferredSize", _p.getPreferredSize, _p.setPreferredSize);
910 /** @expose */
911 _p.capInsets;
912 cc.defineGetterSetter(_p, "capInsets", _p.getCapInsets, _p.setCapInsets);
913 /** @expose */
914 _p.insetLeft;
915 cc.defineGetterSetter(_p, "insetLeft", _p.getInsetLeft, _p.setInsetLeft);
916 /** @expose */
917 _p.insetTop;
918 cc.defineGetterSetter(_p, "insetTop", _p.getInsetTop, _p.setInsetTop);
919 /** @expose */
920 _p.insetRight;
921 cc.defineGetterSetter(_p, "insetRight", _p.getInsetRight, _p.setInsetRight);
922 /** @expose */
923 _p.insetBottom;
924 cc.defineGetterSetter(_p, "insetBottom", _p.getInsetBottom, _p.setInsetBottom);
925 
926 _p = null;
927 
928 /**
929  * Creates a 9-slice sprite with a texture file, a delimitation zone and
930  * with the specified cap insets.
931  *
932  * @see initWithFile:rect:centerRegion:
933  */
934 cc.Scale9Sprite.create = function (file, rect, capInsets) {
935     var pReturn;
936     if (arguments.length === 2) {
937         if (typeof(file) == "string") {
938             pReturn = new cc.Scale9Sprite();
939             if (pReturn && pReturn.initWithFile(file, rect)) {
940                 return pReturn;
941             }
942         } else if (file instanceof cc.Rect) {
943             pReturn = new cc.Scale9Sprite();
944             if (pReturn && pReturn.initWithFile(file, capInsets)) {
945                 return pReturn;
946             }
947         }
948     } else if (arguments.length === 3) {
949         pReturn = new cc.Scale9Sprite();
950         if (pReturn && pReturn.initWithFile(file, rect, capInsets)) {
951             return pReturn;
952         }
953     } else if (arguments.length === 1) {
954         pReturn = new cc.Scale9Sprite();
955         if (pReturn && pReturn.initWithFile(file)) {
956             return pReturn;
957         }
958     } else if (arguments.length === 0) {
959         pReturn = new cc.Scale9Sprite();
960         if (pReturn && pReturn.init()) {
961             return pReturn;
962         }
963     }
964     return null;
965 };
966 
967 cc.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) {
968     var pReturn = new cc.Scale9Sprite();
969     if (pReturn && pReturn.initWithSpriteFrame(spriteFrame, capInsets)) {
970         return pReturn;
971     }
972     return null;
973 };
974 
975 cc.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) {
976     if(!spriteFrameName)
977         throw "cc.Scale9Sprite.createWithSpriteFrameName(): spriteFrameName should be non-null";
978     var pReturn = new cc.Scale9Sprite();
979     if (pReturn && pReturn.initWithSpriteFrameName(spriteFrameName, capInsets))
980         return pReturn;
981     return null;
982 };
983 
984 
985 /**
986  * @ignore
987  */
988 cc.Scale9Sprite.POSITIONS_CENTRE = 0;
989 cc.Scale9Sprite.POSITIONS_TOP = 1;
990 cc.Scale9Sprite.POSITIONS_LEFT = 2;
991 cc.Scale9Sprite.POSITIONS_RIGHT = 3;
992 cc.Scale9Sprite.POSITIONS_BOTTOM = 4;
993 cc.Scale9Sprite.POSITIONS_TOPRIGHT = 5;
994 cc.Scale9Sprite.POSITIONS_TOPLEFT = 6;
995 cc.Scale9Sprite.POSITIONS_BOTTOMRIGHT = 7;
996