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  * a SAX Parser
 29  * @class
 30  * @extends cc.Class
 31  */
 32 cc.SAXParser = cc.Class.extend(/** @lends cc.SAXParser# */{
 33     xmlDoc: null,
 34     _parser: null,
 35     _xmlDict: null,
 36     _isSupportDOMParser: null,
 37 
 38     ctor: function () {
 39         this._xmlDict = {};
 40 
 41         if (window.DOMParser) {
 42             this._isSupportDOMParser = true;
 43             this._parser = new DOMParser();
 44         } else {
 45             this._isSupportDOMParser = false;
 46         }
 47     },
 48 
 49     /**
 50      * parse a xml from a string (xmlhttpObj.responseText)
 51      * @param {String} textxml plist xml contents
 52      * @return {Array} plist object array
 53      */
 54     parse: function (textxml) {
 55         var path = textxml;
 56         textxml = this.getList(textxml);
 57 
 58         var xmlDoc = this._parserXML(textxml, path);
 59 
 60         var plist = xmlDoc.documentElement;
 61         if (plist.tagName != 'plist')
 62             throw "cocos2d: " + path + " is not a plist file or you forgot to preload the plist file";
 63 
 64         // Get first real node
 65         var node = null;
 66         for (var i = 0, len = plist.childNodes.length; i < len; i++) {
 67             node = plist.childNodes[i];
 68             if (node.nodeType == 1)
 69                 break
 70         }
 71         xmlDoc = null;
 72 
 73         return this._parseNode(node);
 74     },
 75 
 76     /**
 77      * parse a tilemap xml from a string (xmlhttpObj.responseText)
 78      * @param  {String} textxml  tilemap xml content
 79      * @return {Document} xml document
 80      */
 81     tmxParse: function (textxml, isXMLString) {
 82         if ((isXMLString == null) || (isXMLString === false))
 83             textxml = this.getList(textxml);
 84 
 85         return this._parserXML(textxml);
 86     },
 87 
 88     _parserXML: function (textxml, path) {
 89         // get a reference to the requested corresponding xml file
 90         var xmlDoc;
 91         if (this._isSupportDOMParser) {
 92             xmlDoc = this._parser.parseFromString(textxml, "text/xml");
 93         } else {
 94             // Internet Explorer (untested!)
 95             xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
 96             xmlDoc.async = "false";
 97             xmlDoc.loadXML(textxml);
 98         }
 99 
100         if (xmlDoc == null)
101             cc.log("cocos2d:xml " + path + " not found!");
102 
103         return xmlDoc;
104     },
105 
106     _parseNode: function (node) {
107         var data = null;
108         switch (node.tagName) {
109             case 'dict':
110                 data = this._parseDict(node);
111                 break;
112             case 'array':
113                 data = this._parseArray(node);
114                 break;
115             case 'string':
116                 if (node.childNodes.length == 1)
117                     data = node.firstChild.nodeValue;
118                 else {
119                     //handle Firefox's 4KB nodeValue limit
120                     data = "";
121                     for (var i = 0; i < node.childNodes.length; i++)
122                         data += node.childNodes[i].nodeValue;
123                 }
124                 break;
125             case 'false':
126                 data = false;
127                 break;
128             case 'true':
129                 data = true;
130                 break;
131             case 'real':
132                 data = parseFloat(node.firstChild.nodeValue);
133                 break;
134             case 'integer':
135                 data = parseInt(node.firstChild.nodeValue, 10);
136                 break;
137         }
138 
139         return data;
140     },
141 
142     _parseArray: function (node) {
143         var data = [];
144         for (var i = 0, len = node.childNodes.length; i < len; i++) {
145             var child = node.childNodes[i];
146             if (child.nodeType != 1)
147                 continue;
148             data.push(this._parseNode(child));
149         }
150         return data;
151     },
152 
153     _parseDict: function (node) {
154         var data = {};
155 
156         var key = null;
157         for (var i = 0, len = node.childNodes.length; i < len; i++) {
158             var child = node.childNodes[i];
159             if (child.nodeType != 1)
160                 continue;
161 
162             // Grab the key, next noe should be the value
163             if (child.tagName == 'key')
164                 key = child.firstChild.nodeValue;
165             else
166                 data[key] = this._parseNode(child);                 // Parse the value node
167         }
168         return data;
169     },
170 
171     /**
172      * Preload plist file
173      * @param {String} filePath
174      */
175     preloadPlist: function (filePath) {
176         filePath = cc.FileUtils.getInstance().fullPathForFilename(filePath);
177 
178         if (window.XMLHttpRequest) {
179             var xmlhttp = new XMLHttpRequest();
180             if (xmlhttp.overrideMimeType)
181                 xmlhttp.overrideMimeType('text/xml');
182         }
183 
184         if (xmlhttp != null) {
185             var that = this;
186             xmlhttp.onreadystatechange = function () {
187                 if (xmlhttp.readyState == 4) {
188                     if (xmlhttp.responseText) {
189                         cc.Loader.getInstance().onResLoaded();
190                         that._xmlDict[filePath] = xmlhttp.responseText;
191                         xmlhttp = null;
192                     } else {
193                         cc.Loader.getInstance().onResLoaded();
194                         cc.log("cocos2d:There was a problem retrieving the xml data:" + xmlhttp.statusText);
195                     }
196                 }
197             };
198             // load xml
199             xmlhttp.open("GET", filePath, true);
200             xmlhttp.send(null);
201         } else
202             throw "cocos2d:Your browser does not support XMLHTTP.";
203     },
204 
205     /**
206      * Unload the preloaded plist from xmlList
207      * @param {String} filePath
208      */
209     unloadPlist: function (filePath) {
210         if (this._xmlDict.hasOwnProperty(filePath))
211             delete this._xmlDict[filePath];
212     },
213 
214     /**
215      * get filename from filepath
216      * @param {String} filePath
217      * @return {String}
218      */
219     getName: function (filePath) {
220         var startPos = filePath.lastIndexOf("/", filePath.length) + 1;
221         var endPos = filePath.lastIndexOf(".", filePath.length);
222         return filePath.substring(startPos, endPos);
223     },
224 
225     /**
226      * get file extension name from filepath
227      * @param {String} filePath
228      * @return {String}
229      */
230     getExt: function (filePath) {
231         var startPos = filePath.lastIndexOf(".", filePath.length) + 1;
232         return filePath.substring(startPos, filePath.length);
233     },
234 
235     /**
236      * get value by key from xmlList
237      * @param {String} key
238      * @return {String} xml content
239      */
240     getList: function (key) {
241         if (this._xmlDict != null) {
242             return this._xmlDict[key];
243         }
244         return null;
245     }
246 });
247 
248 /**
249  * get a singleton SAX parser
250  * @function
251  * @return {cc.SAXParser}
252  */
253 cc.SAXParser.getInstance = function () {
254     if (!this._instance) {
255         this._instance = new cc.SAXParser();
256     }
257     return this._instance;
258 };
259 
260 cc.SAXParser._instance = null;