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         return this.initWithBatchNode(cc.SpriteBatchNode.create(file, 9), rect, false, capInsets);
471     },
472 
473     /**
474      * Initializes a 9-slice sprite with an sprite frame and with the specified
475      * cap insets.
476      * Once the sprite is created, you can then call its "setContentSize:" method
477      * to resize the sprite will all it's 9-slice goodness intract.
478      * It respects the anchorPoint too.
479      *
480      * @param spriteFrame The sprite frame object.
481      * @param capInsets The values to use for the cap insets.
482      */
483     initWithSpriteFrame: function (spriteFrame, capInsets) {
484         if(!spriteFrame || !spriteFrame.getTexture())
485             throw "cc.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null";
486 
487         capInsets = capInsets || cc.rect(0, 0, 0, 0);
488         var locLoaded = spriteFrame.textureLoaded();
489         this._textureLoaded = locLoaded;
490         if(!locLoaded){
491             spriteFrame.addLoadedEventListener(function(sender){
492                 // the texture is rotated on Canvas render mode, so isRotated always is false.
493                 var preferredSize = this._preferredSize;
494                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
495                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets);
496                 this.setPreferredSize(preferredSize);
497                 this._positionsAreDirty = true;
498                 this._callLoadedEventCallbacks();
499             },this);
500         }
501         var batchNode = cc.SpriteBatchNode.create(spriteFrame.getTexture(), 9);
502         // the texture is rotated on Canvas render mode, so isRotated always is false.
503         return this.initWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), capInsets);
504     },
505 
506     /**
507      * Initializes a 9-slice sprite with an sprite frame name and with the specified
508      * cap insets.
509      * Once the sprite is created, you can then call its "setContentSize:" method
510      * to resize the sprite will all it's 9-slice goodness intract.
511      * It respects the anchorPoint too.
512      *
513      * @param spriteFrameName The sprite frame name.
514      * @param capInsets The values to use for the cap insets.
515      */
516     initWithSpriteFrameName: function (spriteFrameName, capInsets) {
517         if(!spriteFrameName)
518             throw "cc.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null";
519         capInsets = capInsets || cc.rect(0, 0, 0, 0);
520 
521         var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName);
522         if (frame == null) {
523             cc.log("cc.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName");
524             return false;
525         }
526 
527         return this.initWithSpriteFrame(frame, capInsets);
528     },
529 
530     /**
531      * Creates and returns a new sprite object with the specified cap insets.
532      * You use this method to add cap insets to a sprite or to change the existing
533      * cap insets of a sprite. In both cases, you get back a new image and the
534      * original sprite remains untouched.
535      *
536      * @param capInsets The values to use for the cap insets.
537      */
538     resizableSpriteWithCapInsets: function (capInsets) {
539         var pReturn = new cc.Scale9Sprite();
540         if (pReturn && pReturn.initWithBatchNode(this._scale9Image, this._spriteRect, false, capInsets)) {
541             return pReturn;
542         }
543         return null;
544     },
545 
546     /** sets the premultipliedAlphaOpacity property.
547      If set to NO then opacity will be applied as: glColor(R,G,B,opacity);
548      If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity );
549      Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO
550      @since v0.8
551      */
552     setOpacityModifyRGB: function (value) {
553         if(!this._scale9Image){
554             return;
555         }
556         this._opacityModifyRGB = value;
557         var scaleChildren = this._scale9Image.getChildren();
558         if (scaleChildren) {
559             for (var i = 0, len = scaleChildren.length; i < len; i++)
560                 scaleChildren[i].setOpacityModifyRGB(value);
561         }
562     },
563 
564     /** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity);
565      @since v0.8
566      */
567     isOpacityModifyRGB: function () {
568         return this._opacityModifyRGB;
569     },
570 
571     updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) {
572         var opacity = this.getOpacity();
573         var color = this.getColor();
574         var rect = cc.rect(originalRect.x, originalRect.y, originalRect.width, originalRect.height);
575 
576         // Release old sprites
577         this.removeAllChildren(true);
578 
579         if (this._scale9Image != batchNode){
580             this._scale9Image = batchNode;
581         }
582         var tmpTexture = batchNode.getTexture();
583         var locLoaded = tmpTexture.isLoaded();
584         this._textureLoaded = locLoaded;
585         if(!locLoaded){
586             tmpTexture.addLoadedEventListener(function(sender){
587                 this._positionsAreDirty = true;
588                 this._callLoadedEventCallbacks();
589             },this);
590             return;
591         }
592         var locScale9Image = this._scale9Image;
593         locScale9Image.removeAllChildren(true);
594 
595         //this._capInsets = capInsets;
596         var locCapInsets = this._capInsets;
597         locCapInsets.x = capInsets.x;
598         locCapInsets.y = capInsets.y;
599         locCapInsets.width = capInsets.width;
600         locCapInsets.height = capInsets.height;
601         this._spriteFrameRotated = rotated;
602 
603         var selTexture = locScale9Image.getTexture();
604 
605         // If there is no given rect
606         if (cc._rectEqualToZero(rect)) {
607             // Get the texture size as original
608             var textureSize = selTexture.getContentSize();
609             rect = cc.rect(0, 0, textureSize.width, textureSize.height);
610         }
611 
612         // Set the given rect's size as original size
613         this._spriteRect = rect;
614         var locSpriteRect = this._spriteRect;
615         locSpriteRect.x = rect.x;
616         locSpriteRect.y = rect.y;
617         locSpriteRect.width = rect.width;
618         locSpriteRect.height = rect.height;
619 
620         this._originalSize.width = rect.width;
621         this._originalSize.height = rect.height;
622 
623         var locPreferredSize = this._preferredSize;
624         if(locPreferredSize.width === 0 && locPreferredSize.height === 0){
625             locPreferredSize.width = rect.width;
626             locPreferredSize.height = rect.height;
627         }
628 
629         var locCapInsetsInternal = this._capInsetsInternal;
630         if(capInsets){
631             locCapInsetsInternal.x = capInsets.x;
632             locCapInsetsInternal.y = capInsets.y;
633             locCapInsetsInternal.width = capInsets.width;
634             locCapInsetsInternal.height = capInsets.height;
635         }
636         var w = rect.width;
637         var h = rect.height;
638 
639         // If there is no specified center region
640         if (cc._rectEqualToZero(locCapInsetsInternal)) {
641             // CCLog("... cap insets not specified : using default cap insets ...");
642             locCapInsetsInternal.x = w / 3;
643             locCapInsetsInternal.y = h / 3;
644             locCapInsetsInternal.width = w / 3;
645             locCapInsetsInternal.height = h / 3;
646         }
647 
648         var left_w = locCapInsetsInternal.x;
649         var center_w = locCapInsetsInternal.width;
650         var right_w = w - (left_w + center_w);
651 
652         var top_h = locCapInsetsInternal.y;
653         var center_h = locCapInsetsInternal.height;
654         var bottom_h = h - (top_h + center_h);
655 
656         // calculate rects
657         // ... top row
658         var x = 0.0;
659         var y = 0.0;
660 
661         // top left
662         var lefttopbounds = cc.rect(x, y, left_w, top_h);
663 
664         // top center
665         x += left_w;
666         var centertopbounds = cc.rect(x, y, center_w, top_h);
667 
668         // top right
669         x += center_w;
670         var righttopbounds = cc.rect(x, y, right_w, top_h);
671 
672         // ... center row
673         x = 0.0;
674         y = 0.0;
675 
676         y += top_h;
677         // center left
678         var leftcenterbounds = cc.rect(x, y, left_w, center_h);
679 
680         // center center
681         x += left_w;
682         var centerbounds = cc.rect(x, y, center_w, center_h);
683 
684         // center right
685         x += center_w;
686         var rightcenterbounds = cc.rect(x, y, right_w, center_h);
687 
688         // ... bottom row
689         x = 0.0;
690         y = 0.0;
691         y += top_h;
692         y += center_h;
693 
694         // bottom left
695         var leftbottombounds = cc.rect(x, y, left_w, bottom_h);
696 
697         // bottom center
698         x += left_w;
699         var centerbottombounds = cc.rect(x, y, center_w, bottom_h);
700 
701         // bottom right
702         x += center_w;
703         var rightbottombounds = cc.rect(x, y, right_w, bottom_h);
704 
705         var t = cc.AffineTransformMakeIdentity();
706         if (!rotated) {
707             // CCLog("!rotated");
708             t = cc.AffineTransformTranslate(t, rect.x, rect.y);
709 
710             cc._RectApplyAffineTransformIn(centerbounds, t);
711             cc._RectApplyAffineTransformIn(rightbottombounds, t);
712             cc._RectApplyAffineTransformIn(leftbottombounds, t);
713             cc._RectApplyAffineTransformIn(righttopbounds, t);
714             cc._RectApplyAffineTransformIn(lefttopbounds, t);
715             cc._RectApplyAffineTransformIn(rightcenterbounds, t);
716             cc._RectApplyAffineTransformIn(leftcenterbounds, t);
717             cc._RectApplyAffineTransformIn(centerbottombounds, t);
718             cc._RectApplyAffineTransformIn(centertopbounds, t);
719 
720             // Centre
721             this._centre = new cc.Sprite();
722             this._centre.initWithTexture(selTexture, centerbounds);
723             locScale9Image.addChild(this._centre, 0, cc.POSITIONS_CENTRE);
724 
725             // Top
726             this._top = new cc.Sprite();
727             this._top.initWithTexture(selTexture, centertopbounds);
728             locScale9Image.addChild(this._top, 1, cc.POSITIONS_TOP);
729 
730             // Bottom
731             this._bottom = new cc.Sprite();
732             this._bottom.initWithTexture(selTexture, centerbottombounds);
733             locScale9Image.addChild(this._bottom, 1, cc.POSITIONS_BOTTOM);
734 
735             // Left
736             this._left = new cc.Sprite();
737             this._left.initWithTexture(selTexture, leftcenterbounds);
738             locScale9Image.addChild(this._left, 1, cc.POSITIONS_LEFT);
739 
740             // Right
741             this._right = new cc.Sprite();
742             this._right.initWithTexture(selTexture, rightcenterbounds);
743             locScale9Image.addChild(this._right, 1, cc.POSITIONS_RIGHT);
744 
745             // Top left
746             this._topLeft = new cc.Sprite();
747             this._topLeft.initWithTexture(selTexture, lefttopbounds);
748             locScale9Image.addChild(this._topLeft, 2, cc.POSITIONS_TOPLEFT);
749 
750             // Top right
751             this._topRight = new cc.Sprite();
752             this._topRight.initWithTexture(selTexture, righttopbounds);
753             locScale9Image.addChild(this._topRight, 2, cc.POSITIONS_TOPRIGHT);
754 
755             // Bottom left
756             this._bottomLeft = new cc.Sprite();
757             this._bottomLeft.initWithTexture(selTexture, leftbottombounds);
758             locScale9Image.addChild(this._bottomLeft, 2, cc.POSITIONS_BOTTOMLEFT);
759 
760             // Bottom right
761             this._bottomRight = new cc.Sprite();
762             this._bottomRight.initWithTexture(selTexture, rightbottombounds);
763             locScale9Image.addChild(this._bottomRight, 2, cc.POSITIONS_BOTTOMRIGHT);
764         } else {
765             // set up transformation of coordinates
766             // to handle the case where the sprite is stored rotated
767             // in the spritesheet
768             // CCLog("rotated");
769             var rotatedcenterbounds = centerbounds;
770             var rotatedrightbottombounds = rightbottombounds;
771             var rotatedleftbottombounds = leftbottombounds;
772             var rotatedrighttopbounds = righttopbounds;
773             var rotatedlefttopbounds = lefttopbounds;
774             var rotatedrightcenterbounds = rightcenterbounds;
775             var rotatedleftcenterbounds = leftcenterbounds;
776             var rotatedcenterbottombounds = centerbottombounds;
777             var rotatedcentertopbounds = centertopbounds;
778 
779             t = cc.AffineTransformTranslate(t, rect.height + rect.x, rect.y);
780             t = cc.AffineTransformRotate(t, 1.57079633);
781 
782             centerbounds = cc.RectApplyAffineTransform(centerbounds, t);
783             rightbottombounds = cc.RectApplyAffineTransform(rightbottombounds, t);
784             leftbottombounds = cc.RectApplyAffineTransform(leftbottombounds, t);
785             righttopbounds = cc.RectApplyAffineTransform(righttopbounds, t);
786             lefttopbounds = cc.RectApplyAffineTransform(lefttopbounds, t);
787             rightcenterbounds = cc.RectApplyAffineTransform(rightcenterbounds, t);
788             leftcenterbounds = cc.RectApplyAffineTransform(leftcenterbounds, t);
789             centerbottombounds = cc.RectApplyAffineTransform(centerbottombounds, t);
790             centertopbounds = cc.RectApplyAffineTransform(centertopbounds, t);
791 
792             rotatedcenterbounds.x = centerbounds.x;
793             rotatedcenterbounds.y = centerbounds.y;
794 
795             rotatedrightbottombounds.x = rightbottombounds.x;
796             rotatedrightbottombounds.y = rightbottombounds.y;
797 
798             rotatedleftbottombounds.x = leftbottombounds.x;
799             rotatedleftbottombounds.y = leftbottombounds.y;
800 
801             rotatedrighttopbounds.x = righttopbounds.x;
802             rotatedrighttopbounds.y = righttopbounds.y;
803 
804             rotatedlefttopbounds.x = lefttopbounds.x;
805             rotatedlefttopbounds.y = lefttopbounds.y;
806 
807             rotatedrightcenterbounds.x = rightcenterbounds.x;
808             rotatedrightcenterbounds.y = rightcenterbounds.y;
809 
810             rotatedleftcenterbounds.x = leftcenterbounds.x;
811             rotatedleftcenterbounds.y = leftcenterbounds.y;
812 
813             rotatedcenterbottombounds.x = centerbottombounds.x;
814             rotatedcenterbottombounds.y = centerbottombounds.y;
815 
816             rotatedcentertopbounds.x = centertopbounds.x;
817             rotatedcentertopbounds.y = centertopbounds.y;
818 
819             // Centre
820             this._centre = new cc.Sprite();
821             this._centre.initWithTexture(selTexture, rotatedcenterbounds, true);
822             locScale9Image.addChild(this._centre, 0, cc.POSITIONS_CENTRE);
823 
824             // Top
825             this._top = new cc.Sprite();
826             this._top.initWithTexture(selTexture, rotatedcentertopbounds, true);
827             locScale9Image.addChild(this._top, 1, cc.POSITIONS_TOP);
828 
829             // Bottom
830             this._bottom = new cc.Sprite();
831             this._bottom.initWithTexture(selTexture, rotatedcenterbottombounds, true);
832             locScale9Image.addChild(this._bottom, 1, cc.POSITIONS_BOTTOM);
833 
834             // Left
835             this._left = new cc.Sprite();
836             this._left.initWithTexture(selTexture, rotatedleftcenterbounds, true);
837             locScale9Image.addChild(this._left, 1, cc.POSITIONS_LEFT);
838 
839             // Right
840             this._right = new cc.Sprite();
841             this._right.initWithTexture(selTexture, rotatedrightcenterbounds, true);
842             locScale9Image.addChild(this._right, 1, cc.POSITIONS_RIGHT);
843 
844             // Top left
845             this._topLeft = new cc.Sprite();
846             this._topLeft.initWithTexture(selTexture, rotatedlefttopbounds, true);
847             locScale9Image.addChild(this._topLeft, 2, cc.POSITIONS_TOPLEFT);
848 
849             // Top right
850             this._topRight = new cc.Sprite();
851             this._topRight.initWithTexture(selTexture, rotatedrighttopbounds, true);
852             locScale9Image.addChild(this._topRight, 2, cc.POSITIONS_TOPRIGHT);
853 
854             // Bottom left
855             this._bottomLeft = new cc.Sprite();
856             this._bottomLeft.initWithTexture(selTexture, rotatedleftbottombounds, true);
857             locScale9Image.addChild(this._bottomLeft, 2, cc.POSITIONS_BOTTOMLEFT);
858 
859             // Bottom right
860             this._bottomRight = new cc.Sprite();
861             this._bottomRight.initWithTexture(selTexture, rotatedrightbottombounds, true);
862             locScale9Image.addChild(this._bottomRight, 2, cc.POSITIONS_BOTTOMRIGHT);
863         }
864 
865         this.setContentSize(rect);
866         this.addChild(locScale9Image);
867 
868         if (this._spritesGenerated) {
869             // Restore color and opacity
870             this.setOpacity(opacity);
871             if(color.r !== 255 || color.g !== 255 || color.b !== 255){
872                 this.setColor(color);
873             }
874         }
875         this._spritesGenerated = true;
876         return true;
877     },
878 
879     setSpriteFrame: function (spriteFrame) {
880         var batchNode = cc.SpriteBatchNode.create(spriteFrame.getTexture(), 9);
881         // the texture is rotated on Canvas render mode, so isRotated always is false.
882         var locLoaded = spriteFrame.textureLoaded();
883         this._textureLoaded = locLoaded;
884         if(!locLoaded){
885             spriteFrame.addLoadedEventListener(function(sender){
886                 // the texture is rotated on Canvas render mode, so isRotated always is false.
887                 var preferredSize = this._preferredSize;
888                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
889                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets);
890                 this.setPreferredSize(preferredSize);
891                 this._positionsAreDirty = true;
892                 this._callLoadedEventCallbacks();
893             },this);
894         }
895         this.updateWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), cc.rect(0, 0, 0, 0));
896 
897         // Reset insets
898         this._insetLeft = 0;
899         this._insetTop = 0;
900         this._insetRight = 0;
901         this._insetBottom = 0;
902     }
903 });
904 
905 window._p = cc.Scale9Sprite.prototype;
906 
907 // Extended properties
908 /** @expose */
909 _p.preferredSize;
910 cc.defineGetterSetter(_p, "preferredSize", _p.getPreferredSize, _p.setPreferredSize);
911 /** @expose */
912 _p.capInsets;
913 cc.defineGetterSetter(_p, "capInsets", _p.getCapInsets, _p.setCapInsets);
914 /** @expose */
915 _p.insetLeft;
916 cc.defineGetterSetter(_p, "insetLeft", _p.getInsetLeft, _p.setInsetLeft);
917 /** @expose */
918 _p.insetTop;
919 cc.defineGetterSetter(_p, "insetTop", _p.getInsetTop, _p.setInsetTop);
920 /** @expose */
921 _p.insetRight;
922 cc.defineGetterSetter(_p, "insetRight", _p.getInsetRight, _p.setInsetRight);
923 /** @expose */
924 _p.insetBottom;
925 cc.defineGetterSetter(_p, "insetBottom", _p.getInsetBottom, _p.setInsetBottom);
926 
927 delete window._p;
928 
929 /**
930  * Creates a 9-slice sprite with a texture file, a delimitation zone and
931  * with the specified cap insets.
932  *
933  * @see initWithFile:rect:centerRegion:
934  */
935 cc.Scale9Sprite.create = function (file, rect, capInsets) {
936     var pReturn;
937     if (arguments.length === 2) {
938         if (typeof(file) == "string") {
939             pReturn = new cc.Scale9Sprite();
940             if (pReturn && pReturn.initWithFile(file, rect)) {
941                 return pReturn;
942             }
943         } else if (file instanceof cc.Rect) {
944             pReturn = new cc.Scale9Sprite();
945             if (pReturn && pReturn.initWithFile(file, capInsets)) {
946                 return pReturn;
947             }
948         }
949     } else if (arguments.length === 3) {
950         pReturn = new cc.Scale9Sprite();
951         if (pReturn && pReturn.initWithFile(file, rect, capInsets)) {
952             return pReturn;
953         }
954     } else if (arguments.length === 1) {
955         pReturn = new cc.Scale9Sprite();
956         if (pReturn && pReturn.initWithFile(file)) {
957             return pReturn;
958         }
959     } else if (arguments.length === 0) {
960         pReturn = new cc.Scale9Sprite();
961         if (pReturn && pReturn.init()) {
962             return pReturn;
963         }
964     }
965     return null;
966 };
967 
968 cc.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) {
969     var pReturn = new cc.Scale9Sprite();
970     if (pReturn && pReturn.initWithSpriteFrame(spriteFrame, capInsets)) {
971         return pReturn;
972     }
973     return null;
974 };
975 
976 cc.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) {
977     if(!spriteFrameName)
978         throw "cc.Scale9Sprite.createWithSpriteFrameName(): spriteFrameName should be non-null";
979     var pReturn = new cc.Scale9Sprite();
980     if (pReturn && pReturn.initWithSpriteFrameName(spriteFrameName, capInsets))
981         return pReturn;
982     return null;
983 };
984