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