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 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * @constant
 29  * @type Number
 30  */
 31 cc.TMX_PROPERTY_NONE = 0;
 32 
 33 /**
 34  * @constant
 35  * @type Number
 36  */
 37 cc.TMX_PROPERTY_MAP = 1;
 38 
 39 /**
 40  * @constant
 41  * @type Number
 42  */
 43 cc.TMX_PROPERTY_LAYER = 2;
 44 
 45 /**
 46  * @constant
 47  * @type Number
 48  */
 49 cc.TMX_PROPERTY_OBJECTGROUP = 3;
 50 
 51 /**
 52  * @constant
 53  * @type Number
 54  */
 55 cc.TMX_PROPERTY_OBJECT = 4;
 56 
 57 /**
 58  * @constant
 59  * @type Number
 60  */
 61 cc.TMX_PROPERTY_TILE = 5;
 62 
 63 /**
 64  * @constant
 65  * @type Number
 66  */
 67 cc.TMX_TILE_HORIZONTAL_FLAG = 0x80000000;
 68 
 69 
 70 /**
 71  * @constant
 72  * @type Number
 73  */
 74 cc.TMX_TILE_VERTICAL_FLAG = 0x40000000;
 75 
 76 /**
 77  * @constant
 78  * @type Number
 79  */
 80 cc.TMX_TILE_DIAGONAL_FLAG = 0x20000000;
 81 
 82 /**
 83  * @constant
 84  * @type Number
 85  */
 86 cc.TMX_TILE_FLIPPED_ALL = (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_DIAGONAL_FLAG) >>> 0;
 87 
 88 /**
 89  * @constant
 90  * @type Number
 91  */
 92 cc.TMX_TILE_FLIPPED_MASK = (~(cc.TMX_TILE_FLIPPED_ALL)) >>> 0;
 93 
 94 // Bits on the far end of the 32-bit global tile ID (GID's) are used for tile flags
 95 
 96 /**
 97  * <p>cc.TMXLayerInfo contains the information about the layers like: <br />
 98  * - Layer name<br />
 99  * - Layer size <br />
100  * - Layer opacity at creation time (it can be modified at runtime)  <br />
101  * - Whether the layer is visible (if it's not visible, then the CocosNode won't be created) <br />
102  *  <br />
103  * This information is obtained from the TMX file.</p>
104  * @class
105  * @extends cc.Class
106  *
107  * @property {Array}    properties  - Properties of the layer info.
108  */
109 cc.TMXLayerInfo = cc.Class.extend(/** @lends cc.TMXLayerInfo# */{
110     properties:null,
111 
112 	name:"",
113     _layerSize:null,
114     _tiles:null,
115     visible:null,
116     _opacity:null,
117     ownTiles:true,
118     _minGID:100000,
119     _maxGID:0,
120     offset:null,
121 
122     ctor:function () {
123         this.properties = [];
124         this.name = "";
125         this._layerSize = null;
126         this._tiles = [];
127         this.visible = true;
128         this._opacity = 0;
129         this.ownTiles = true;
130         this._minGID = 100000;
131         this._maxGID = 0;
132         this.offset = cc.p(0,0);
133     },
134 
135     /**
136      * Gets the Properties.
137      * @return {Array}
138      */
139     getProperties:function () {
140         return this.properties;
141     },
142 
143     /**
144      * Set the Properties.
145      * @param {object} value
146      */
147     setProperties:function (value) {
148         this.properties = value;
149     }
150 });
151 
152 /**
153  * <p>cc.TMXTilesetInfo contains the information about the tilesets like: <br />
154  * - Tileset name<br />
155  * - Tileset spacing<br />
156  * - Tileset margin<br />
157  * - size of the tiles<br />
158  * - Image used for the tiles<br />
159  * - Image size<br />
160  *
161  * This information is obtained from the TMX file. </p>
162  * @class
163  * @extends cc.Class
164  *
165  * @property {string} name - Tileset name
166  * @property {number} firstGid - First grid
167  * @property {number} spacing - Spacing
168  * @property {number} margin - Margin
169  * @property {string} sourceImage - Filename containing the tiles (should be sprite sheet / texture atlas)
170  * @property {cc.Size|null} imageSize - Size in pixels of the image
171  */
172 cc.TMXTilesetInfo = cc.Class.extend(/** @lends cc.TMXTilesetInfo# */{
173 
174     //Tileset name
175     name:"",
176 
177     //First grid
178     firstGid:0,
179     _tileSize:null,
180 
181     //Spacing
182     spacing:0,
183 
184     //Margin
185     margin:0,
186 
187     //Filename containing the tiles (should be sprite sheet / texture atlas)
188     sourceImage:"",
189 
190     //Size in pixels of the image
191     imageSize:null,
192 
193     ctor:function () {
194         this._tileSize = cc.size(0, 0);
195         this.imageSize = cc.size(0, 0);
196     },
197 
198     /**
199      * Return rect
200      * @param {Number} gid
201      * @return {cc.Rect}
202      */
203     rectForGID:function (gid) {
204         var rect = cc.rect(0, 0, 0, 0);
205         rect.width = this._tileSize.width;
206         rect.height = this._tileSize.height;
207         gid &= cc.TMX_TILE_FLIPPED_MASK;
208         gid = gid - parseInt(this.firstGid, 10);
209         var max_x = parseInt((this.imageSize.width - this.margin * 2 + this.spacing) / (this._tileSize.width + this.spacing), 10);
210         rect.x = parseInt((gid % max_x) * (this._tileSize.width + this.spacing) + this.margin, 10);
211         rect.y = parseInt(parseInt(gid / max_x, 10) * (this._tileSize.height + this.spacing) + this.margin, 10);
212         return rect;
213     }
214 });
215 
216 /**
217  * <p>cc.TMXMapInfo contains the information about the map like: <br/>
218  *- Map orientation (hexagonal, isometric or orthogonal)<br/>
219  *- Tile size<br/>
220  *- Map size</p>
221  *
222  * <p>And it also contains: <br/>
223  * - Layers (an array of TMXLayerInfo objects)<br/>
224  * - Tilesets (an array of TMXTilesetInfo objects) <br/>
225  * - ObjectGroups (an array of TMXObjectGroupInfo objects) </p>
226  *
227  * <p>This information is obtained from the TMX file. </p>
228  * @class
229  * @extends cc.saxParser
230  *
231  * @property {Array}    properties          - Properties of the map info.
232  * @property {Number}   orientation         - Map orientation.
233  * @property {Object}   parentElement       - Parent element.
234  * @property {Number}   parentGID           - Parent GID.
235  * @property {Object}   layerAttrs        - Layer attributes.
236  * @property {Boolean}  storingCharacters   - Is reading storing characters stream.
237  * @property {String}   tmxFileName         - TMX file name.
238  * @property {String}   currentString       - Current string stored from characters stream.
239  * @property {Number}   mapWidth            - Width of the map
240  * @property {Number}   mapHeight           - Height of the map
241  * @property {Number}   tileWidth           - Width of a tile
242  * @property {Number}   tileHeight          - Height of a tile
243  *
244  * @param {String} tmxFile fileName or content string
245  * @param {String} resourcePath  If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required.
246  * @example
247  * 1.
248  * //create a TMXMapInfo with file name
249  * var tmxMapInfo = new cc.TMXMapInfo("res/orthogonal-test1.tmx");
250  * 2.
251  * //create a TMXMapInfo with content string and resource path
252  * var resources = "res/TileMaps";
253  * var filePath = "res/TileMaps/orthogonal-test1.tmx";
254  * var xmlStr = cc.loader.getRes(filePath);
255  * var tmxMapInfo = new cc.TMXMapInfo(xmlStr, resources);
256  */
257 cc.TMXMapInfo = cc.SAXParser.extend(/** @lends cc.TMXMapInfo# */{
258 	properties:null,
259     orientation:null,
260 	parentElement:null,
261 	parentGID:null,
262 	layerAttrs:0,
263 	storingCharacters:false,
264 	tmxFileName:null,
265 	currentString:null,
266 
267 	_objectGroups:null,
268     _mapSize:null,
269     _tileSize:null,
270     _layers:null,
271     _tilesets:null,
272     // tile properties
273     _tileProperties:null,
274     _resources:"",
275     _currentFirstGID:0,
276 
277     /**
278      * Creates a TMX Format with a tmx file or content string                           <br/>
279      * Constructor of cc.TMXMapInfo
280      * @param {String} tmxFile fileName or content string
281      * @param {String} resourcePath  If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required.
282      */
283     ctor:function (tmxFile, resourcePath) {
284         cc.SAXParser.prototype.ctor.apply(this);
285         this._mapSize = cc.size(0, 0);
286         this._tileSize = cc.size(0, 0);
287         this._layers = [];
288         this._tilesets = [];
289         this._objectGroups = [];
290         this.properties = [];
291         this._tileProperties = {};
292 
293         this._currentFirstGID = 0;
294 
295         if (resourcePath !== undefined) {
296             this.initWithXML(tmxFile,resourcePath);
297         } else if(tmxFile !== undefined){
298             this.initWithTMXFile(tmxFile);
299         }
300     },
301     /**
302      * Gets Map orientation.
303      * @return {Number}
304      */
305     getOrientation:function () {
306         return this.orientation;
307     },
308 
309     /**
310      * Set the Map orientation.
311      * @param {Number} value
312      */
313     setOrientation:function (value) {
314         this.orientation = value;
315     },
316 
317     /**
318      * Map width & height
319      * @return {cc.Size}
320      */
321     getMapSize:function () {
322         return cc.size(this._mapSize.width,this._mapSize.height);
323     },
324 
325     /**
326      * Map width & height
327      * @param {cc.Size} value
328      */
329     setMapSize:function (value) {
330         this._mapSize.width = value.width;
331         this._mapSize.height = value.height;
332     },
333 
334 	_getMapWidth: function () {
335 		return this._mapSize.width;
336 	},
337 	_setMapWidth: function (width) {
338 		this._mapSize.width = width;
339 	},
340 	_getMapHeight: function () {
341 		return this._mapSize.height;
342 	},
343 	_setMapHeight: function (height) {
344 		this._mapSize.height = height;
345 	},
346 
347     /**
348      * Tiles width & height
349      * @return {cc.Size}
350      */
351     getTileSize:function () {
352         return cc.size(this._tileSize.width, this._tileSize.height);
353     },
354 
355     /**
356      * Tiles width & height
357      * @param {cc.Size} value
358      */
359     setTileSize:function (value) {
360         this._tileSize.width = value.width;
361         this._tileSize.height = value.height;
362     },
363 
364 	_getTileWidth: function () {
365 		return this._tileSize.width;
366 	},
367 	_setTileWidth: function (width) {
368 		this._tileSize.width = width;
369 	},
370 	_getTileHeight: function () {
371 		return this._tileSize.height;
372 	},
373 	_setTileHeight: function (height) {
374 		this._tileSize.height = height;
375 	},
376 
377     /**
378      * Layers
379      * @return {Array}
380      */
381     getLayers:function () {
382         return this._layers;
383     },
384 
385     /**
386      * Layers
387      * @param {cc.TMXLayerInfo} value
388      */
389     setLayers:function (value) {
390         this._layers.push(value);
391     },
392 
393     /**
394      * tilesets
395      * @return {Array}
396      */
397     getTilesets:function () {
398         return this._tilesets;
399     },
400 
401     /**
402      * tilesets
403      * @param {cc.TMXTilesetInfo} value
404      */
405     setTilesets:function (value) {
406         this._tilesets.push(value);
407     },
408 
409     /**
410      * ObjectGroups
411      * @return {Array}
412      */
413     getObjectGroups:function () {
414         return this._objectGroups;
415     },
416 
417     /**
418      * ObjectGroups
419      * @param {cc.TMXObjectGroup} value
420      */
421     setObjectGroups:function (value) {
422         this._objectGroups.push(value);
423     },
424 
425     /**
426      * parent element
427      * @return {Object}
428      */
429     getParentElement:function () {
430         return this.parentElement;
431     },
432 
433     /**
434      * parent element
435      * @param {Object} value
436      */
437     setParentElement:function (value) {
438         this.parentElement = value;
439     },
440 
441     /**
442      * parent GID
443      * @return {Number}
444      */
445     getParentGID:function () {
446         return this.parentGID;
447     },
448 
449     /**
450      * parent GID
451      * @param {Number} value
452      */
453     setParentGID:function (value) {
454         this.parentGID = value;
455     },
456 
457     /**
458      * Layer attribute
459      * @return {Object}
460      */
461     getLayerAttribs:function () {
462         return this.layerAttrs;
463     },
464 
465     /**
466      * Layer attribute
467      * @param {Object} value
468      */
469     setLayerAttribs:function (value) {
470         this.layerAttrs = value;
471     },
472 
473     /**
474      * Is reading storing characters stream
475      * @return {Boolean}
476      */
477     getStoringCharacters:function () {
478         return this.storingCharacters;
479     },
480 
481     /**
482      * Is reading storing characters stream
483      * @param {Boolean} value
484      */
485     setStoringCharacters:function (value) {
486         this.storingCharacters = value;
487     },
488 
489     /**
490      * Properties
491      * @return {Array}
492      */
493     getProperties:function () {
494         return this.properties;
495     },
496 
497     /**
498      * Properties
499      * @param {object} value
500      */
501     setProperties:function (value) {
502         this.properties = value;
503     },
504 
505     /**
506      * Initializes a TMX format with a  tmx file
507      * @param {String} tmxFile
508      * @return {Element}
509      */
510     initWithTMXFile:function (tmxFile) {
511         this._internalInit(tmxFile, null);
512         return this.parseXMLFile(tmxFile);
513     },
514 
515     /**
516      * initializes a TMX format with an XML string and a TMX resource path
517      * @param {String} tmxString
518      * @param {String} resourcePath
519      * @return {Boolean}
520      */
521     initWithXML:function (tmxString, resourcePath) {
522         this._internalInit(null, resourcePath);
523         return this.parseXMLString(tmxString);
524     },
525 
526     /** Initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file
527      * @param {String} tmxFile
528      * @param {boolean} [isXmlString=false]
529      * @return {Element}
530      */
531     parseXMLFile:function (tmxFile, isXmlString) {
532         isXmlString = isXmlString || false;
533 	    var xmlStr = isXmlString ? tmxFile : cc.loader.getRes(tmxFile);
534         if(!xmlStr) throw new Error("Please load the resource first : " + tmxFile);
535 
536         var mapXML = this._parseXML(xmlStr);
537         var i, j;
538 
539         // PARSE <map>
540         var map = mapXML.documentElement;
541 
542         var version = map.getAttribute('version');
543         var orientationStr = map.getAttribute('orientation');
544 
545         if (map.nodeName === "map") {
546             if (version !== "1.0" && version !== null)
547                 cc.log("cocos2d: TMXFormat: Unsupported TMX version:" + version);
548 
549             if (orientationStr === "orthogonal")
550                 this.orientation = cc.TMX_ORIENTATION_ORTHO;
551             else if (orientationStr === "isometric")
552                 this.orientation = cc.TMX_ORIENTATION_ISO;
553             else if (orientationStr === "hexagonal")
554                 this.orientation = cc.TMX_ORIENTATION_HEX;
555             else if (orientationStr !== null)
556                 cc.log("cocos2d: TMXFomat: Unsupported orientation:" + orientationStr);
557 
558             var mapSize = cc.size(0, 0);
559             mapSize.width = parseFloat(map.getAttribute('width'));
560             mapSize.height = parseFloat(map.getAttribute('height'));
561             this.setMapSize(mapSize);
562 
563             mapSize = cc.size(0, 0);
564             mapSize.width = parseFloat(map.getAttribute('tilewidth'));
565             mapSize.height = parseFloat(map.getAttribute('tileheight'));
566             this.setTileSize(mapSize);
567 
568             // The parent element is the map
569             var propertyArr = map.querySelectorAll("map > properties >  property");
570             if (propertyArr) {
571                 var aPropertyDict = {};
572                 for (i = 0; i < propertyArr.length; i++) {
573                     aPropertyDict[propertyArr[i].getAttribute('name')] = propertyArr[i].getAttribute('value');
574                 }
575                 this.properties = aPropertyDict;
576             }
577         }
578 
579         // PARSE <tileset>
580         var tilesets = map.getElementsByTagName('tileset');
581         if (map.nodeName !== "map") {
582             tilesets = [];
583             tilesets.push(map);
584         }
585 
586         for (i = 0; i < tilesets.length; i++) {
587             var selTileset = tilesets[i];
588             // If this is an external tileset then start parsing that
589             var tsxName = selTileset.getAttribute('source');
590             if (tsxName) {
591                 //this._currentFirstGID = parseInt(selTileset.getAttribute('firstgid'));
592                 var tsxPath = isXmlString ? cc.path.join(this._resources, tsxName) : cc.path.changeBasename(tmxFile, tsxName);
593                 this.parseXMLFile(tsxPath);
594             } else {
595                 var tileset = new cc.TMXTilesetInfo();
596                 tileset.name = selTileset.getAttribute('name') || "";
597                 //TODO need fix
598                 //if(this._currentFirstGID === 0){
599                 tileset.firstGid = parseInt(selTileset.getAttribute('firstgid')) || 0;
600                 //}else{
601                 //    tileset.firstGid = this._currentFirstGID;
602                 //    this._currentFirstGID = 0;
603                 //}
604 
605                 tileset.spacing = parseInt(selTileset.getAttribute('spacing')) || 0;
606                 tileset.margin = parseInt(selTileset.getAttribute('margin')) || 0;
607 
608                 var tilesetSize = cc.size(0, 0);
609                 tilesetSize.width = parseFloat(selTileset.getAttribute('tilewidth'));
610                 tilesetSize.height = parseFloat(selTileset.getAttribute('tileheight'));
611                 tileset._tileSize = tilesetSize;
612 
613                 var image = selTileset.getElementsByTagName('image')[0];
614                 var imagename = image.getAttribute('source');
615                 var num = -1;
616                 if(this.tmxFileName)
617                     num  = this.tmxFileName.lastIndexOf("/");
618                 if (num !== -1) {
619                     var dir = this.tmxFileName.substr(0, num + 1);
620                     tileset.sourceImage = dir + imagename;
621                 } else {
622                     tileset.sourceImage = this._resources + (this._resources ? "/" : "") + imagename;
623                 }
624                 this.setTilesets(tileset);
625 
626                 // PARSE  <tile>
627                 var tiles = selTileset.getElementsByTagName('tile');
628                 if (tiles) {
629                     for (var tIdx = 0; tIdx < tiles.length; tIdx++) {
630                         var t = tiles[tIdx];
631                         this.parentGID = parseInt(tileset.firstGid) + parseInt(t.getAttribute('id') || 0);
632                         var tp = t.querySelectorAll("properties > property");
633                         if (tp) {
634                             var dict = {};
635                             for (j = 0; j < tp.length; j++) {
636                                 var name = tp[j].getAttribute('name');
637                                 dict[name] = tp[j].getAttribute('value');
638                             }
639                             this._tileProperties[this.parentGID] = dict;
640                         }
641                     }
642                 }
643             }
644         }
645 
646         // PARSE  <layer>
647         var layers = map.getElementsByTagName('layer');
648         if (layers) {
649             for (i = 0; i < layers.length; i++) {
650                 var selLayer = layers[i];
651                 var data = selLayer.getElementsByTagName('data')[0];
652 
653                 var layer = new cc.TMXLayerInfo();
654                 layer.name = selLayer.getAttribute('name');
655 
656                 var layerSize = cc.size(0, 0);
657                 layerSize.width = parseFloat(selLayer.getAttribute('width'));
658                 layerSize.height = parseFloat(selLayer.getAttribute('height'));
659                 layer._layerSize = layerSize;
660 
661                 var visible = selLayer.getAttribute('visible');
662                 layer.visible = !(visible == "0");
663 
664                 var opacity = selLayer.getAttribute('opacity') || 1;
665 
666                 if (opacity)
667                     layer._opacity = parseInt(255 * parseFloat(opacity));
668                 else
669                     layer._opacity = 255;
670                 layer.offset = cc.p(parseFloat(selLayer.getAttribute('x')) || 0, parseFloat(selLayer.getAttribute('y')) || 0);
671 
672                 var nodeValue = '';
673                 for (j = 0; j < data.childNodes.length; j++) {
674                     nodeValue += data.childNodes[j].nodeValue
675                 }
676                 nodeValue = nodeValue.trim();
677 
678                 // Unpack the tilemap data
679                 var compression = data.getAttribute('compression');
680                 var encoding = data.getAttribute('encoding');
681                 if(compression && compression !== "gzip" && compression !== "zlib"){
682                     cc.log("cc.TMXMapInfo.parseXMLFile(): unsupported compression method");
683                     return null;
684                 }
685                 switch (compression) {
686                     case 'gzip':
687                         layer._tiles = cc.unzipBase64AsArray(nodeValue, 4);
688                         break;
689                     case 'zlib':
690                         var inflator = new Zlib.Inflate(cc.Codec.Base64.decodeAsArray(nodeValue, 1));
691                         layer._tiles = cc.uint8ArrayToUint32Array(inflator.decompress());
692                         break;
693                     case null:
694                     case '':
695                         // Uncompressed
696                         if (encoding === "base64")
697                             layer._tiles = cc.Codec.Base64.decodeAsArray(nodeValue, 4);
698                         else if (encoding === "csv") {
699                             layer._tiles = [];
700                             var csvTiles = nodeValue.split(',');
701                             for (var csvIdx = 0; csvIdx < csvTiles.length; csvIdx++)
702                                 layer._tiles.push(parseInt(csvTiles[csvIdx]));
703                         } else {
704                             //XML format
705                             var selDataTiles = data.getElementsByTagName("tile");
706                             layer._tiles = [];
707                             for (var xmlIdx = 0; xmlIdx < selDataTiles.length; xmlIdx++)
708                                 layer._tiles.push(parseInt(selDataTiles[xmlIdx].getAttribute("gid")));
709                         }
710                         break;
711                     default:
712                         if(this.layerAttrs === cc.TMXLayerInfo.ATTRIB_NONE)
713                             cc.log("cc.TMXMapInfo.parseXMLFile(): Only base64 and/or gzip/zlib maps are supported");
714                         break;
715                 }
716 
717                 // The parent element is the last layer
718                 var layerProps = selLayer.querySelectorAll("properties > property");
719                 if (layerProps) {
720                     var layerProp = {};
721                     for (j = 0; j < layerProps.length; j++) {
722                         layerProp[layerProps[j].getAttribute('name')] = layerProps[j].getAttribute('value');
723                     }
724                     layer.properties = layerProp;
725                 }
726                 this.setLayers(layer);
727             }
728         }
729 
730         // PARSE <objectgroup>
731         var objectGroups = map.getElementsByTagName('objectgroup');
732         if (objectGroups) {
733             for (i = 0; i < objectGroups.length; i++) {
734                 var selGroup = objectGroups[i];
735                 var objectGroup = new cc.TMXObjectGroup();
736                 objectGroup.groupName = selGroup.getAttribute('name');
737                 objectGroup.setPositionOffset(cc.p(parseFloat(selGroup.getAttribute('x')) * this.getTileSize().width || 0,
738                     parseFloat(selGroup.getAttribute('y')) * this.getTileSize().height || 0));
739 
740                 var groupProps = selGroup.querySelectorAll("objectgroup > properties > property");
741                 if (groupProps) {
742                     for (j = 0; j < groupProps.length; j++) {
743                         var groupProp = {};
744                         groupProp[groupProps[j].getAttribute('name')] = groupProps[j].getAttribute('value');
745                         // Add the property to the layer
746                         objectGroup.properties = groupProp;
747                     }
748                 }
749 
750                 var objects = selGroup.querySelectorAll('object');
751                 var getContentScaleFactor = cc.director.getContentScaleFactor();
752                 if (objects) {
753                     for (j = 0; j < objects.length; j++) {
754                         var selObj = objects[j];
755                         // The value for "type" was blank or not a valid class name
756                         // Create an instance of TMXObjectInfo to store the object and its properties
757                         var objectProp = {};
758 
759                         // Set the name of the object to the value for "name"
760                         objectProp["name"] = selObj.getAttribute('name') || "";
761 
762                         // Assign all the attributes as key/name pairs in the properties dictionary
763                         objectProp["type"] = selObj.getAttribute('type') || "";
764 
765                         objectProp["width"] = parseInt(selObj.getAttribute('width')) || 0;
766                         objectProp["height"] = parseInt(selObj.getAttribute('height')) || 0;
767 
768                         objectProp["x"] = (((selObj.getAttribute('x') || 0) | 0) + objectGroup.getPositionOffset().x) / getContentScaleFactor;
769                         var y = ((selObj.getAttribute('y') || 0) | 0) + objectGroup.getPositionOffset().y / getContentScaleFactor;
770                         // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
771                         objectProp["y"] = (parseInt(this.getMapSize().height * this.getTileSize().height) - y - objectProp["height"]) / cc.director.getContentScaleFactor();
772 
773                         objectProp["rotation"] = parseInt(selObj.getAttribute('rotation')) || 0;
774 
775                         var docObjProps = selObj.querySelectorAll("properties > property");
776                         if (docObjProps) {
777                             for (var k = 0; k < docObjProps.length; k++)
778                                 objectProp[docObjProps[k].getAttribute('name')] = docObjProps[k].getAttribute('value');
779                         }
780 
781                         //polygon
782                         var polygonProps = selObj.querySelectorAll("polygon");
783                         if(polygonProps && polygonProps.length > 0) {
784                             var selPgPointStr = polygonProps[0].getAttribute('points');
785                             if(selPgPointStr)
786                                 objectProp["points"] = this._parsePointsString(selPgPointStr);
787                         }
788 
789                         //polyline
790                         var polylineProps = selObj.querySelectorAll("polyline");
791                         if(polylineProps && polylineProps.length > 0) {
792                             var selPlPointStr = polylineProps[0].getAttribute('points');
793                             if(selPlPointStr)
794                                 objectProp["polylinePoints"] = this._parsePointsString(selPlPointStr);
795                         }
796 
797                         // Add the object to the objectGroup
798                         objectGroup.setObjects(objectProp);
799                     }
800                 }
801 
802                 this.setObjectGroups(objectGroup);
803             }
804         }
805         return map;
806     },
807 
808     _parsePointsString:function(pointsString){
809          if(!pointsString)
810             return null;
811 
812         var points = [];
813         var pointsStr = pointsString.split(' ');
814         for(var i = 0; i < pointsStr.length; i++){
815             var selPointStr = pointsStr[i].split(',');
816             points.push({'x':selPointStr[0], 'y':selPointStr[1]});
817         }
818         return points;
819     },
820 
821     /**
822      * initializes parsing of an XML string, either a tmx (Map) string or tsx (Tileset) string
823      * @param {String} xmlString
824      * @return {Boolean}
825      */
826     parseXMLString:function (xmlString) {
827         return this.parseXMLFile(xmlString, true);
828     },
829 
830     /**
831      * Gets the tile properties.
832      * @return {object}
833      */
834     getTileProperties:function () {
835         return this._tileProperties;
836     },
837 
838     /**
839      * Set the tile properties.
840      * @param {object} tileProperties
841      */
842     setTileProperties:function (tileProperties) {
843         this._tileProperties.push(tileProperties);
844     },
845 
846     /**
847      * Gets the currentString
848      * @return {String}
849      */
850     getCurrentString:function () {
851         return this.currentString;
852     },
853 
854     /**
855      * Set the currentString
856      * @param {String} currentString
857      */
858     setCurrentString:function (currentString) {
859         this.currentString = currentString;
860     },
861 
862     /**
863      * Gets the tmxFileName
864      * @return {String}
865      */
866     getTMXFileName:function () {
867         return this.tmxFileName;
868     },
869 
870     /**
871      * Set the tmxFileName
872      * @param {String} fileName
873      */
874     setTMXFileName:function (fileName) {
875         this.tmxFileName = fileName;
876     },
877 
878     _internalInit:function (tmxFileName, resourcePath) {
879         this._tilesets.length = 0;
880         this._layers.length = 0;
881 
882         this.tmxFileName = tmxFileName;
883         if (resourcePath)
884             this._resources = resourcePath;
885 
886         this._objectGroups.length = 0;
887         this.properties.length = 0;
888         this._tileProperties.length = 0;
889 
890         // tmp vars
891         this.currentString = "";
892         this.storingCharacters = false;
893         this.layerAttrs = cc.TMXLayerInfo.ATTRIB_NONE;
894         this.parentElement = cc.TMX_PROPERTY_NONE;
895         this._currentFirstGID = 0;
896     }
897 });
898 
899 var _p = cc.TMXMapInfo.prototype;
900 
901 // Extended properties
902 /** @expose */
903 _p.mapWidth;
904 cc.defineGetterSetter(_p, "mapWidth", _p._getMapWidth, _p._setMapWidth);
905 /** @expose */
906 _p.mapHeight;
907 cc.defineGetterSetter(_p, "mapHeight", _p._getMapHeight, _p._setMapHeight);
908 /** @expose */
909 _p.tileWidth;
910 cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth);
911 /** @expose */
912 _p.tileHeight;
913 cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight);
914 
915 
916 /**
917  * Creates a TMX Format with a tmx file or content string
918  * @deprecated since v3.0 please use new cc.TMXMapInfo(tmxFile, resourcePath) instead.
919  * @param {String} tmxFile fileName or content string
920  * @param {String} resourcePath  If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required.
921  * @return {cc.TMXMapInfo}
922  */
923 cc.TMXMapInfo.create = function (tmxFile, resourcePath) {
924     return new cc.TMXMapInfo(tmxFile, resourcePath);
925 };
926 
927 
928 cc.loader.register(["tmx", "tsx"], cc._txtLoader);
929 
930 
931 /**
932  * @constant
933  * @type Number
934  */
935 cc.TMXLayerInfo.ATTRIB_NONE = 1 << 0;
936 /**
937  * @constant
938  * @type Number
939  */
940 cc.TMXLayerInfo.ATTRIB_BASE64 = 1 << 1;
941 /**
942  * @constant
943  * @type Number
944  */
945 cc.TMXLayerInfo.ATTRIB_GZIP = 1 << 2;
946 /**
947  * @constant
948  * @type Number
949  */
950 cc.TMXLayerInfo.ATTRIB_ZLIB = 1 << 3;
951