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 = null;
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, result) {
204         var rect = result || 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                 var tiles;
686                 switch (compression) {
687                     case 'gzip':
688                         tiles = cc.unzipBase64AsArray(nodeValue, 4);
689                         break;
690                     case 'zlib':
691                         var inflator = new Zlib.Inflate(cc.Codec.Base64.decodeAsArray(nodeValue, 1));
692                         tiles = cc.uint8ArrayToUint32Array(inflator.decompress());
693                         break;
694                     case null:
695                     case '':
696                         // Uncompressed
697                         if (encoding === "base64")
698                             tiles = cc.Codec.Base64.decodeAsArray(nodeValue, 4);
699                         else if (encoding === "csv") {
700                             tiles = [];
701                             var csvTiles = nodeValue.split(',');
702                             for (var csvIdx = 0; csvIdx < csvTiles.length; csvIdx++)
703                                 tiles.push(parseInt(csvTiles[csvIdx]));
704                         } else {
705                             //XML format
706                             var selDataTiles = data.getElementsByTagName("tile");
707                             tiles = [];
708                             for (var xmlIdx = 0; xmlIdx < selDataTiles.length; xmlIdx++)
709                                 tiles.push(parseInt(selDataTiles[xmlIdx].getAttribute("gid")));
710                         }
711                         break;
712                     default:
713                         if(this.layerAttrs === cc.TMXLayerInfo.ATTRIB_NONE)
714                             cc.log("cc.TMXMapInfo.parseXMLFile(): Only base64 and/or gzip/zlib maps are supported");
715                         break;
716                 }
717                 if (tiles) {
718                     layer._tiles = new Uint32Array(tiles);
719                 }
720 
721                 // The parent element is the last layer
722                 var layerProps = selLayer.querySelectorAll("properties > property");
723                 if (layerProps) {
724                     var layerProp = {};
725                     for (j = 0; j < layerProps.length; j++) {
726                         layerProp[layerProps[j].getAttribute('name')] = layerProps[j].getAttribute('value');
727                     }
728                     layer.properties = layerProp;
729                 }
730                 this.setLayers(layer);
731             }
732         }
733 
734         // PARSE <objectgroup>
735         var objectGroups = map.getElementsByTagName('objectgroup');
736         if (objectGroups) {
737             for (i = 0; i < objectGroups.length; i++) {
738                 var selGroup = objectGroups[i];
739                 var objectGroup = new cc.TMXObjectGroup();
740                 objectGroup.groupName = selGroup.getAttribute('name');
741                 objectGroup.setPositionOffset(cc.p(parseFloat(selGroup.getAttribute('x')) * this.getTileSize().width || 0,
742                     parseFloat(selGroup.getAttribute('y')) * this.getTileSize().height || 0));
743 
744                 var groupProps = selGroup.querySelectorAll("objectgroup > properties > property");
745                 if (groupProps) {
746                     for (j = 0; j < groupProps.length; j++) {
747                         var groupProp = {};
748                         groupProp[groupProps[j].getAttribute('name')] = groupProps[j].getAttribute('value');
749                         // Add the property to the layer
750                         objectGroup.properties = groupProp;
751                     }
752                 }
753 
754                 var objects = selGroup.querySelectorAll('object');
755                 var getContentScaleFactor = cc.director.getContentScaleFactor();
756                 if (objects) {
757                     for (j = 0; j < objects.length; j++) {
758                         var selObj = objects[j];
759                         // The value for "type" was blank or not a valid class name
760                         // Create an instance of TMXObjectInfo to store the object and its properties
761                         var objectProp = {};
762 
763                         // Set the name of the object to the value for "name"
764                         objectProp["name"] = selObj.getAttribute('name') || "";
765 
766                         // Assign all the attributes as key/name pairs in the properties dictionary
767                         objectProp["type"] = selObj.getAttribute('type') || "";
768 
769                         objectProp["width"] = parseInt(selObj.getAttribute('width')) || 0;
770                         objectProp["height"] = parseInt(selObj.getAttribute('height')) || 0;
771 
772                         objectProp["x"] = (((selObj.getAttribute('x') || 0) | 0) + objectGroup.getPositionOffset().x) / getContentScaleFactor;
773                         var y = ((selObj.getAttribute('y') || 0) | 0) + objectGroup.getPositionOffset().y / getContentScaleFactor;
774                         // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
775                         objectProp["y"] = (parseInt(this.getMapSize().height * this.getTileSize().height) - y - objectProp["height"]) / cc.director.getContentScaleFactor();
776 
777                         objectProp["rotation"] = parseInt(selObj.getAttribute('rotation')) || 0;
778 
779                         var docObjProps = selObj.querySelectorAll("properties > property");
780                         if (docObjProps) {
781                             for (var k = 0; k < docObjProps.length; k++)
782                                 objectProp[docObjProps[k].getAttribute('name')] = docObjProps[k].getAttribute('value');
783                         }
784 
785                         //polygon
786                         var polygonProps = selObj.querySelectorAll("polygon");
787                         if(polygonProps && polygonProps.length > 0) {
788                             var selPgPointStr = polygonProps[0].getAttribute('points');
789                             if(selPgPointStr)
790                                 objectProp["points"] = this._parsePointsString(selPgPointStr);
791                         }
792 
793                         //polyline
794                         var polylineProps = selObj.querySelectorAll("polyline");
795                         if(polylineProps && polylineProps.length > 0) {
796                             var selPlPointStr = polylineProps[0].getAttribute('points');
797                             if(selPlPointStr)
798                                 objectProp["polylinePoints"] = this._parsePointsString(selPlPointStr);
799                         }
800 
801                         // Add the object to the objectGroup
802                         objectGroup.setObjects(objectProp);
803                     }
804                 }
805 
806                 this.setObjectGroups(objectGroup);
807             }
808         }
809         return map;
810     },
811 
812     _parsePointsString:function(pointsString){
813          if(!pointsString)
814             return null;
815 
816         var points = [];
817         var pointsStr = pointsString.split(' ');
818         for(var i = 0; i < pointsStr.length; i++){
819             var selPointStr = pointsStr[i].split(',');
820             points.push({'x':selPointStr[0], 'y':selPointStr[1]});
821         }
822         return points;
823     },
824 
825     /**
826      * initializes parsing of an XML string, either a tmx (Map) string or tsx (Tileset) string
827      * @param {String} xmlString
828      * @return {Boolean}
829      */
830     parseXMLString:function (xmlString) {
831         return this.parseXMLFile(xmlString, true);
832     },
833 
834     /**
835      * Gets the tile properties.
836      * @return {object}
837      */
838     getTileProperties:function () {
839         return this._tileProperties;
840     },
841 
842     /**
843      * Set the tile properties.
844      * @param {object} tileProperties
845      */
846     setTileProperties:function (tileProperties) {
847         this._tileProperties.push(tileProperties);
848     },
849 
850     /**
851      * Gets the currentString
852      * @return {String}
853      */
854     getCurrentString:function () {
855         return this.currentString;
856     },
857 
858     /**
859      * Set the currentString
860      * @param {String} currentString
861      */
862     setCurrentString:function (currentString) {
863         this.currentString = currentString;
864     },
865 
866     /**
867      * Gets the tmxFileName
868      * @return {String}
869      */
870     getTMXFileName:function () {
871         return this.tmxFileName;
872     },
873 
874     /**
875      * Set the tmxFileName
876      * @param {String} fileName
877      */
878     setTMXFileName:function (fileName) {
879         this.tmxFileName = fileName;
880     },
881 
882     _internalInit:function (tmxFileName, resourcePath) {
883         this._tilesets.length = 0;
884         this._layers.length = 0;
885 
886         this.tmxFileName = tmxFileName;
887         if (resourcePath)
888             this._resources = resourcePath;
889 
890         this._objectGroups.length = 0;
891         this.properties.length = 0;
892         this._tileProperties.length = 0;
893 
894         // tmp vars
895         this.currentString = "";
896         this.storingCharacters = false;
897         this.layerAttrs = cc.TMXLayerInfo.ATTRIB_NONE;
898         this.parentElement = cc.TMX_PROPERTY_NONE;
899         this._currentFirstGID = 0;
900     }
901 });
902 
903 var _p = cc.TMXMapInfo.prototype;
904 
905 // Extended properties
906 /** @expose */
907 _p.mapWidth;
908 cc.defineGetterSetter(_p, "mapWidth", _p._getMapWidth, _p._setMapWidth);
909 /** @expose */
910 _p.mapHeight;
911 cc.defineGetterSetter(_p, "mapHeight", _p._getMapHeight, _p._setMapHeight);
912 /** @expose */
913 _p.tileWidth;
914 cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth);
915 /** @expose */
916 _p.tileHeight;
917 cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight);
918 
919 
920 /**
921  * Creates a TMX Format with a tmx file or content string
922  * @deprecated since v3.0 please use new cc.TMXMapInfo(tmxFile, resourcePath) instead.
923  * @param {String} tmxFile fileName or content string
924  * @param {String} resourcePath  If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required.
925  * @return {cc.TMXMapInfo}
926  */
927 cc.TMXMapInfo.create = function (tmxFile, resourcePath) {
928     return new cc.TMXMapInfo(tmxFile, resourcePath);
929 };
930 
931 
932 cc.loader.register(["tmx", "tsx"], cc._txtLoader);
933 
934 
935 /**
936  * @constant
937  * @type Number
938  */
939 cc.TMXLayerInfo.ATTRIB_NONE = 1 << 0;
940 /**
941  * @constant
942  * @type Number
943  */
944 cc.TMXLayerInfo.ATTRIB_BASE64 = 1 << 1;
945 /**
946  * @constant
947  * @type Number
948  */
949 cc.TMXLayerInfo.ATTRIB_GZIP = 1 << 2;
950 /**
951  * @constant
952  * @type Number
953  */
954 cc.TMXLayerInfo.ATTRIB_ZLIB = 1 << 3;
955