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