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";
 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.Assert("cocos2d:There was a problem retrieving the xml data:" + xmlhttp.statusText);
194                 }
195             };
196             // load xml
197             xmlhttp.open("GET", filePath, true);
198             xmlhttp.send(null);
199         } else
200             cc.Assert("cocos2d:Your browser does not support XMLHTTP.");
201     },
202 
203     /**
204      * Unload the preloaded plist from xmlList
205      * @param {String} filePath
206      */
207     unloadPlist: function (filePath) {
208         if (this._xmlDict.hasOwnProperty(filePath))
209             delete this._xmlDict[filePath];
210     },
211 
212     /**
213      * get filename from filepath
214      * @param {String} filePath
215      * @return {String}
216      */
217     getName: function (filePath) {
218         var startPos = filePath.lastIndexOf("/", filePath.length) + 1;
219         var endPos = filePath.lastIndexOf(".", filePath.length);
220         return filePath.substring(startPos, endPos);
221     },
222 
223     /**
224      * get file extension name from filepath
225      * @param {String} filePath
226      * @return {String}
227      */
228     getExt: function (filePath) {
229         var startPos = filePath.lastIndexOf(".", filePath.length) + 1;
230         return filePath.substring(startPos, filePath.length);
231     },
232 
233     /**
234      * get value by key from xmlList
235      * @param {String} key
236      * @return {String} xml content
237      */
238     getList: function (key) {
239         if (this._xmlDict != null) {
240             return this._xmlDict[key];
241         }
242         return null;
243     }
244 });
245 
246 /**
247  * get a singleton SAX parser
248  * @function
249  * @return {cc.SAXParser}
250  */
251 cc.SAXParser.getInstance = function () {
252     if (!this._instance) {
253         this._instance = new cc.SAXParser();
254     }
255     return this._instance;
256 };
257 
258 cc.SAXParser._instance = null;