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 = cc.TMXMapInfo.create("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 = cc.TMXMapInfo.create(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 "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                 if (objects) {
752                     for (j = 0; j < objects.length; j++) {
753                         var selObj = objects[j];
754                         // The value for "type" was blank or not a valid class name
755                         // Create an instance of TMXObjectInfo to store the object and its properties
756                         var objectProp = {};
757 
758                         // Set the name of the object to the value for "name"
759                         objectProp["name"] = selObj.getAttribute('name') || "";
760 
761                         // Assign all the attributes as key/name pairs in the properties dictionary
762                         objectProp["type"] = selObj.getAttribute('type') || "";
763 
764                         objectProp["x"] = parseInt(selObj.getAttribute('x') || 0) + objectGroup.getPositionOffset().x;
765                         var y = parseInt(selObj.getAttribute('y') || 0) + objectGroup.getPositionOffset().y;
766 
767                         objectProp["width"] = parseInt(selObj.getAttribute('width')) || 0;
768                         objectProp["height"] = parseInt(selObj.getAttribute('height')) || 0;
769 
770                         // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
771                         objectProp["y"] = parseInt(this.getMapSize().height * this.getTileSize().height) - y - objectProp["height"];
772 
773                         var docObjProps = selObj.querySelectorAll("properties > property");
774                         if (docObjProps) {
775                             for (var k = 0; k < docObjProps.length; k++)
776                                 objectProp[docObjProps[k].getAttribute('name')] = docObjProps[k].getAttribute('value');
777                         }
778 
779                         //polygon
780                         var polygonProps = selObj.querySelectorAll("polygon");
781                         if(polygonProps && polygonProps.length > 0) {
782                             var selPgPointStr = polygonProps[0].getAttribute('points');
783                             if(selPgPointStr)
784                                 objectProp["polygonPoints"] = this._parsePointsString(selPgPointStr);
785                         }
786 
787                         //polyline
788                         var polylineProps = selObj.querySelectorAll("polyline");
789                         if(polylineProps && polylineProps.length > 0) {
790                             var selPlPointStr = polylineProps[0].getAttribute('points');
791                             if(selPlPointStr)
792                                 objectProp["polylinePoints"] = this._parsePointsString(selPlPointStr);
793                         }
794 
795                         // Add the object to the objectGroup
796                         objectGroup.setObjects(objectProp);
797                     }
798                 }
799 
800                 this.setObjectGroups(objectGroup);
801             }
802         }
803         return map;
804     },
805 
806     _parsePointsString:function(pointsString){
807          if(!pointsString)
808             return null;
809 
810         var points = [];
811         var pointsStr = pointsString.split(' ');
812         for(var i = 0; i < pointsStr.length; i++){
813             var selPointStr = pointsStr[i].split(',');
814             points.push({'x':selPointStr[0], 'y':selPointStr[1]});
815         }
816         return points;
817     },
818 
819     /**
820      * initializes parsing of an XML string, either a tmx (Map) string or tsx (Tileset) string
821      * @param {String} xmlString
822      * @return {Boolean}
823      */
824     parseXMLString:function (xmlString) {
825         return this.parseXMLFile(xmlString, true);
826     },
827 
828     /**
829      * Gets the tile properties.
830      * @return {object}
831      */
832     getTileProperties:function () {
833         return this._tileProperties;
834     },
835 
836     /**
837      * Set the tile properties.
838      * @param {object} tileProperties
839      */
840     setTileProperties:function (tileProperties) {
841         this._tileProperties.push(tileProperties);
842     },
843 
844     /**
845      * Gets the currentString
846      * @return {String}
847      */
848     getCurrentString:function () {
849         return this.currentString;
850     },
851 
852     /**
853      * Set the currentString
854      * @param {String} currentString
855      */
856     setCurrentString:function (currentString) {
857         this.currentString = currentString;
858     },
859 
860     /**
861      * Gets the tmxFileName
862      * @return {String}
863      */
864     getTMXFileName:function () {
865         return this.tmxFileName;
866     },
867 
868     /**
869      * Set the tmxFileName
870      * @param {String} fileName
871      */
872     setTMXFileName:function (fileName) {
873         this.tmxFileName = fileName;
874     },
875 
876     _internalInit:function (tmxFileName, resourcePath) {
877         this._tilesets.length = 0;
878         this._layers.length = 0;
879 
880         this.tmxFileName = tmxFileName;
881         if (resourcePath)
882             this._resources = resourcePath;
883 
884         this._objectGroups.length = 0;
885         this.properties.length = 0;
886         this._tileProperties.length = 0;
887 
888         // tmp vars
889         this.currentString = "";
890         this.storingCharacters = false;
891         this.layerAttrs = cc.TMXLayerInfo.ATTRIB_NONE;
892         this.parentElement = cc.TMX_PROPERTY_NONE;
893         this._currentFirstGID = 0;
894     }
895 });
896 
897 var _p = cc.TMXMapInfo.prototype;
898 
899 // Extended properties
900 /** @expose */
901 _p.mapWidth;
902 cc.defineGetterSetter(_p, "mapWidth", _p._getMapWidth, _p._setMapWidth);
903 /** @expose */
904 _p.mapHeight;
905 cc.defineGetterSetter(_p, "mapHeight", _p._getMapHeight, _p._setMapHeight);
906 /** @expose */
907 _p.tileWidth;
908 cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth);
909 /** @expose */
910 _p.tileHeight;
911 cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight);
912 
913 
914 /**
915  * Creates a TMX Format with a tmx file or content string
916  * @deprecated since v3.0 please use new cc.TMXMapInfo(tmxFile, resourcePath) instead.
917  * @param {String} tmxFile fileName or content string
918  * @param {String} resourcePath  If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required.
919  * @return {cc.TMXMapInfo}
920  * @example
921  * 1.
922  * //create a TMXMapInfo with file name
923  * var tmxMapInfo = cc.TMXMapInfo.create("res/orthogonal-test1.tmx");
924  * 2.
925  * //create a TMXMapInfo with content string and resource path
926  * var resources = "res/TileMaps";
927  * var filePath = "res/TileMaps/orthogonal-test1.tmx";
928  * var xmlStr = cc.loader.getRes(filePath);
929  * var tmxMapInfo = cc.TMXMapInfo.create(xmlStr, resources);
930  */
931 cc.TMXMapInfo.create = function (tmxFile, resourcePath) {
932     return new cc.TMXMapInfo(tmxFile, resourcePath);
933 };
934 
935 
936 cc.loader.register(["tmx", "tsx"], cc._txtLoader);
937 
938 
939 /**
940  * @constant
941  * @type Number
942  */
943 cc.TMXLayerInfo.ATTRIB_NONE = 1 << 0;
944 /**
945  * @constant
946  * @type Number
947  */
948 cc.TMXLayerInfo.ATTRIB_BASE64 = 1 << 1;
949 /**
950  * @constant
951  * @type Number
952  */
953 cc.TMXLayerInfo.ATTRIB_GZIP = 1 << 2;
954 /**
955  * @constant
956  * @type Number
957  */
958 cc.TMXLayerInfo.ATTRIB_ZLIB = 1 << 3;
959