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