├── .gitignore ├── .npmignore ├── .travis.yml ├── JsTestDriver.conf ├── LICENSE ├── README.md ├── ant ├── build.properties └── build.xml ├── examples ├── d2forum.html ├── seajs.html ├── weather.html └── weather.js ├── jsTestDriver-yui-adapter ├── yui-adapter.js ├── yui-all.js └── yui-min.js ├── lib ├── JsTestDriver-1.3.2.jar ├── compiler.jar └── yuicompressor-2.4.2.jar ├── package.json ├── src └── jsxml.js └── tests ├── jsxml-test.html └── jsxml-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | node_modules/ 3 | ant/ 4 | build/ 5 | lib/ 6 | jsTestDriver-yui-adapter/ 7 | examples/ 8 | tests/ 9 | JsTestDriver.conf 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 -------------------------------------------------------------------------------- /JsTestDriver.conf: -------------------------------------------------------------------------------- 1 | server: http://localhost:4224 2 | 3 | load: 4 | - jsTestDriver-yui-adapter/yui-min.js 5 | - jsTestDriver-yui-adapter/yui-adapter.js 6 | - src/*.js 7 | - tests/*.js 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010-2011 (c) colorhook, www.colorhook.com. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 14 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 16 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 19 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jsxml 2 | ============== 3 | 4 | jsxml is an XML library for javascript (and node) 5 | 6 | 7 | Install by NPM 8 | -------------- 9 | 10 | ```shell 11 | npm install node-jsxml 12 | ``` 13 | 14 | How to use? 15 | ------------ 16 | After add this library to your project, there will be a global object named jsxml. 17 | 18 | in HTML file, import using <script> elements. 19 | 20 | ```html 21 | 22 | ``` 23 | 24 | in Node, import using `require` function. 25 | 26 | ```javascript 27 | const jsxml = require("node-jsxml"); 28 | ``` 29 | 30 | support AMD, CMD. Big thanks to [TimSchlechter](https://github.com/TimSchlechter). 31 | 32 | ```javascript 33 | seajs.config({ 34 | alias: { 35 | jsxml: '../src/jsxml.js' 36 | } 37 | }); 38 | seajs.use('jsxml', function(jsxml){ 39 | console.log(jsxml); 40 | }); 41 | ``` 42 | 43 | ```javascript 44 | var Namespace = jsxml.Namespace, 45 | QName = jsxml.QName, 46 | XML = jsxml.XML, 47 | XMLList = jsxml.XMLList; 48 | ``` 49 | 50 | Here you go: 51 | 52 | ```javascript 53 | var xml = new XML("" + 54 | "" + 55 | "" + 56 | "" + 57 | "" + 58 | "" + 59 | "" + 60 | "" + 61 | ""); 62 | ``` 63 | 64 | find special childs 65 | 66 | ```javascript 67 | var child = xml.child('list'); 68 | ``` 69 | 70 | find all children by * 71 | 72 | ```javascript 73 | var children = xml.child('*'); 74 | ``` 75 | 76 | print the xml string 77 | 78 | ```javascript 79 | console.log(xml.toXMLString()); 80 | ``` 81 | 82 | modify namespace 83 | 84 | ```javascript 85 | xml.addNamespace(new Namespace("ns", "http://colorhook.com")); 86 | xml.children().addNamespace(new Namespace("prefix", "uri")); 87 | console.log(xml.toXMLString()); 88 | ``` 89 | 90 | find descendants nodes 91 | 92 | ```javascript 93 | var descendants = xml.descendants('element'); 94 | ``` 95 | 96 | get all children 97 | 98 | ```javascript 99 | var children = xml.children(); 100 | //or 101 | var children = xml.child('*'); 102 | ``` 103 | 104 | get text node 105 | 106 | ```javascript 107 | var text = xml.text(); 108 | ``` 109 | 110 | get element node 111 | 112 | ```javascript 113 | var elements = xml.elements(); 114 | ``` 115 | 116 | get comment node 117 | 118 | ```javascript 119 | var comments = xml.comments(); 120 | ``` 121 | 122 | get attribute 123 | 124 | ```javascript 125 | var attribute = xml.attribute("id"); 126 | ``` 127 | 128 | get all attributes 129 | 130 | ```javascript 131 | var attributes = xml.attributes(); 132 | ``` 133 | 134 | All methods above return an XML object or XMLList object, if you want to get the String type content, you should: 135 | 136 | ```javascript 137 | var xml = new XML(xmlContent); 138 | 139 | var attrValue = xml.attribute('attrName').toString(); 140 | //or 141 | var attrValue = xml.attribute('attrName').getValue(); 142 | 143 | var childA = xml.child('a').toString(); 144 | //or 145 | var childA = xml.child('a').getValue(); 146 | ``` 147 | 148 | If you want to modify the value, you should call method setValue: 149 | 150 | ```javascript 151 | var xml = new XML("your xml string"); 152 | 153 | var attr= xml.attribute('attrName'); 154 | attr.setValue("newValue"); 155 | 156 | var childA = xml.child('a'); 157 | childA.setValue("newValue"); 158 | ``` 159 | 160 | You can regenerate the XML Content 161 | 162 | ```javascript 163 | var str = xml.toXMLString(); 164 | ``` 165 | 166 | While dealing with a list of childs in XML tree, you should use XMLList API: 167 | 168 | ```javascript 169 | var list = xml.child("item"); 170 | list.each(function(item, index){ 171 | //item is an XML 172 | }); 173 | ``` 174 | 175 | Advanced topics 176 | ---------------- 177 | 178 | You can also add, retrieve or remove namespaces: 179 | 180 | ```javascript 181 | var xml = new XML("your xml string"); 182 | var ns = xml.namespace("prefix"); 183 | 184 | var nsNew = new Namespace("prefix", 'uri'); 185 | xml.addNamespace(nsNew); 186 | xml.removeNamespace(nsNew); 187 | ``` 188 | 189 | Bugs & Feedback 190 | ---------------- 191 | 192 | Please feel free report bugs or feature requests. 193 | You can send me private message on [github], or send me an email to: [colorhook@gmail.com] 194 | 195 | License 196 | ------- 197 | 198 | jsxml is free to use under MIT license. 199 | -------------------------------------------------------------------------------- /ant/build.properties: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | ### Build settings 3 | ###################################################################### 4 | 5 | build.version = 0.1.0 6 | build.name = jsxml-${build.version} 7 | build.files = jsxml.js 8 | 9 | dir.src = ${basedir}/src 10 | dir.lib = ${basedir}/lib 11 | dir.build = ${basedir}/build 12 | dir.ant = ${basedir}/ant 13 | 14 | compress.yui = ${dir.lib}/yuicompressor-2.4.2.jar 15 | compress.gcc = ${dir.lib}/compiler.jar 16 | 17 | test.jar = ${dir.lib}/JsTestDriver-1.3.2.jar -------------------------------------------------------------------------------- /ant/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ${dir.src} 11 | 12 | 13 | 14 | JavaScript Bundles Done! 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | YUI Compress Done! 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Google Closure Compiler Compress Done! 47 | 48 | 49 | 50 | 51 | 52 | 53 | js test driver server started 54 | 55 | 56 | 57 | 58 | 59 | 60 | js test driver test completed! 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Delete Build Folder 75 | 76 | Create Build Folder 77 | 78 | 79 | 80 | 81 | Create Temp Folder 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /examples/d2forum.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsxml example - D2 forum 6 | 7 | 8 | 74 | 75 |
76 |

第六届 D2前端技术论坛 (7月9日·杭州)

77 |
78 |
79 |
80 | 81 | 82 | 165 | 166 | -------------------------------------------------------------------------------- /examples/seajs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsxml example - seajs 6 | 7 | 9 | 10 | 11 | 21 | 22 | -------------------------------------------------------------------------------- /examples/weather.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsxml example - Yahoo weather 6 | 7 | 29 | 30 |

jsxml example - Yahoo weather

31 | 32 |
33 |
34 | 35 | 36 | 74 | 75 | -------------------------------------------------------------------------------- /examples/weather.js: -------------------------------------------------------------------------------- 1 | var jsxml = require("node-jsxml"), 2 | XML = jsxml.XML, 3 | http = require("http"); 4 | 5 | var options={ 6 | host: "query.yahooapis.com", 7 | path: "/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20location%3D'CHXX0044'" 8 | }; 9 | http.get(options, function(response){ 10 | var str = ""; 11 | response.on('data', function (chunk) { 12 | str += chunk; 13 | }); 14 | response.on('end', function(){ 15 | var xml = new XML(str); 16 | var condition = xml.descendants("condition").attribute("text").toString(); 17 | console.log("current condiftion is: " +condition); 18 | }); 19 | }); -------------------------------------------------------------------------------- /jsTestDriver-yui-adapter/yui-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010, Yahoo! Inc. All rights reserved. 3 | Code licensed under the BSD License: 4 | http://developer.yahoo.com/yui/license.html 5 | version: 3.3.0 6 | build: 3167 7 | */ 8 | if(typeof YUI!="undefined"){YUI._YUI=YUI;}var YUI=function(){var c=0,f=this,b=arguments,a=b.length,e=function(h,g){return(h&&h.hasOwnProperty&&(h instanceof g));},d=(typeof YUI_config!=="undefined")&&YUI_config;if(!(e(f,YUI))){f=new YUI();}else{f._init();if(YUI.GlobalConfig){f.applyConfig(YUI.GlobalConfig);}if(d){f.applyConfig(d);}if(!a){f._setup();}}if(a){for(;c-1){q="3.2.0";}p={applyConfig:function(D){D=D||l;var y,A,z=this.config,B=z.modules,x=z.groups,C=z.rls,w=this.Env._loader;for(A in D){if(D.hasOwnProperty(A)){y=D[A];if(B&&A=="modules"){o(B,y);}else{if(x&&A=="groups"){o(x,y);}else{if(C&&A=="rls"){o(C,y);}else{if(A=="win"){z[A]=y.contentWindow||y;z.doc=z[A].document;}else{if(A=="_yuid"){}else{z[A]=y;}}}}}}}if(w){w._config(D);}},_config:function(w){this.applyConfig(w);},_init:function(){var y,z=this,w=YUI.Env,x=z.Env,A;z.version=q;if(!x){z.Env={mods:{},versions:{},base:n,cdn:n+q+"/build/",_idx:0,_used:{},_attached:{},_yidx:0,_uidx:0,_guidp:"y",_loaded:{},serviced:{},getBase:w&&w.getBase||function(G,F){var B,C,E,H,D;C=(v&&v.getElementsByTagName("script"))||[];for(E=0;E-1){y=y.substr(0,D);}}D=H.match(F);if(D&&D[3]){B=D[1]+D[3];}break;}}}return B||x.cdn;}};x=z.Env;x._loaded[q]={};if(w&&z!==YUI){x._yidx=++w._yidx;x._guidp=("yui_"+q+"_"+x._yidx+"_"+i).replace(/\./g,"_");}else{if(YUI._YUI){w=YUI._YUI.Env;x._yidx+=w._yidx;x._uidx+=w._uidx;for(A in w){if(!(A in x)){x[A]=w[A];}}delete YUI._YUI;}}z.id=z.stamp(z);c[z.id]=z;}z.constructor=YUI;z.config=z.config||{win:e,doc:v,debug:true,useBrowserConsole:true,throwFail:true,bootstrap:true,cacheUse:true,fetchCSS:true};z.config.base=YUI.config.base||z.Env.getBase(/^(.*)yui\/yui([\.\-].*)js(\?.*)?$/,/^(.*\?)(.*\&)(.*)yui\/yui[\.\-].*js(\?.*)?$/);if(!y||(!("-min.-debug.").indexOf(y))){y="-min.";}z.config.loaderPath=YUI.config.loaderPath||"loader/loader"+(y||"-min.")+"js";},_setup:function(B){var x,A=this,w=[],z=YUI.Env.mods,y=A.config.core||["get","rls","intl-base","loader","yui-log","yui-later","yui-throttle"];for(x=0;xH)?F[H]:true;}return J;};l.indexOf=(u.indexOf)?function(E,F){return u.indexOf.call(E,F);}:function(E,G){for(var F=0;F1)?Array.prototype.join.call(arguments,o):I;if(!(H in E)||(F&&E[H]==F)){E[H]=G.apply(G,arguments);}return E[H];};};var q=function(){},h=function(E){q.prototype=E;return new q();},j=function(F,E){return F&&F.hasOwnProperty&&F.hasOwnProperty(E);},v,d=function(I,H){var G=(H===2),E=(G)?0:[],F;for(F in I){if(j(I,F)){if(G){E++;}else{E.push((H)?I[F]:F);}}}return E;};c.Object=h;h.keys=function(E){return d(E);};h.values=function(E){return d(E,1); 10 | };h.size=Object.size||function(E){return d(E,2);};h.hasKey=j;h.hasValue=function(F,E){return(c.Array.indexOf(h.values(F),E)>-1);};h.owns=j;h.each=function(I,H,J,G){var F=J||c,E;for(E in I){if(G||j(I,E)){H.call(F,I[E],E,I);}}return c;};h.some=function(I,H,J,G){var F=J||c,E;for(E in I){if(G||j(I,E)){if(H.call(F,I[E],E,I)){return true;}}}return false;};h.getValue=function(I,H){if(!c.Lang.isObject(I)){return v;}var F,G=c.Array(H),E=G.length;for(F=0;I!==v&&F=0){for(E=0;F!==v&&E0){c=d(j);if(c){return c;}else{e=j.lastIndexOf("-");if(e>=0){j=j.substring(0,e);if(e>=2&&j.charAt(e-2)==="-"){j=j.substring(0,e-2);}}else{break;}}}}return"";}});},"3.3.0",{requires:["yui-base"]});YUI.add("yui-log",function(d){var c=d,e="yui:log",a="undefined",b={debug:1,info:1,warn:1,error:1};c.log=function(j,s,g,q){var l,p,n,k,o,i=c,r=i.config,h=(i.fire)?i:YUI.Env.globalEvents;if(r.debug){if(g){p=r.logExclude;n=r.logInclude;if(n&&!(g in n)){l=1;}else{if(p&&(g in p)){l=1;}}}if(!l){if(r.useBrowserConsole){k=(g)?g+": "+j:j;if(i.Lang.isFunction(r.logFn)){r.logFn.call(i,j,s,g);}else{if(typeof console!=a&&console.log){o=(s&&console[s]&&(s in b))?s:"log";console[o](k);}else{if(typeof opera!=a){opera.postError(k);}}}}if(h&&!q){if(h==i&&(!h.getEvent(e))){h.publish(e,{broadcast:2});}h.fire(e,{msg:j,cat:s,src:g});}}}return i;};c.message=function(){return c.log.apply(c,arguments);};},"3.3.0",{requires:["yui-base"]});YUI.add("yui-later",function(a){a.later=function(c,i,d,h,g){c=c||0;var b=d,e,j;if(i&&a.Lang.isString(d)){b=i[d];}e=!a.Lang.isUndefined(h)?function(){b.apply(i,a.Array(h));}:function(){b.call(i);};j=(g)?setInterval(e,c):setTimeout(e,c);return{id:j,interval:g,cancel:function(){if(this.interval){clearInterval(j);}else{clearTimeout(j);}}};};a.Lang.later=a.later;},"3.3.0",{requires:["yui-base"]});YUI.add("yui-throttle",function(a){ 12 | /*! Based on work by Simon Willison: http://gist.github.com/292562 */ 13 | a.throttle=function(c,b){b=(b)?b:(a.config.throttleTime||150);if(b===-1){return(function(){c.apply(null,arguments);});}var d=a.Lang.now();return(function(){var e=a.Lang.now();if(e-d>b){d=e;c.apply(null,arguments);}});};},"3.3.0",{requires:["yui-base"]});YUI.add("yui",function(a){},"3.3.0",{use:["yui-base","get","features","rls","intl-base","yui-log","yui-later","yui-throttle"]}); -------------------------------------------------------------------------------- /lib/JsTestDriver-1.3.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorhook/jsxml/db56177528c57c8bfdbd34489eaf435624846dc6/lib/JsTestDriver-1.3.2.jar -------------------------------------------------------------------------------- /lib/compiler.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorhook/jsxml/db56177528c57c8bfdbd34489eaf435624846dc6/lib/compiler.jar -------------------------------------------------------------------------------- /lib/yuicompressor-2.4.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorhook/jsxml/db56177528c57c8bfdbd34489eaf435624846dc6/lib/yuicompressor-2.4.2.jar -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-jsxml", 3 | "version": "0.11.0", 4 | "description": "an XML parser for javascript and node.js", 5 | "keywords": [ 6 | "xml", 7 | "parser" 8 | ], 9 | "main": "./src/jsxml.js", 10 | "engines": { 11 | "node": ">=0.1.9" 12 | }, 13 | "author": "colorhook ", 14 | "maintainers": [ 15 | { 16 | "name": "colorhook", 17 | "email": "colorhook@gmail.com", 18 | "url": "http://colorhook.com" 19 | } 20 | ], 21 | "contributors": [ 22 | { 23 | "name": "Tiago Freitas", 24 | "url": "https://github.com/trmfreitas" 25 | }, 26 | { 27 | "name": "TimSchlechter", 28 | "url": "http://github.com/TimSchlechter" 29 | }, 30 | { 31 | "name": "baconmania", 32 | "url": "http://github.com/baconmania" 33 | }, 34 | { 35 | "name": "Ron Valstar", 36 | "url": "http://github.com/Sjeiti" 37 | }, 38 | { 39 | "name": "bhishp", 40 | "url": "https://github.com/bhishp" 41 | } 42 | ], 43 | "bugs": { 44 | "email": "colorhook@gmail.com", 45 | "url": "http://github.com/colorhook/jsxml/issues" 46 | }, 47 | "dependencies": {}, 48 | "devDependencies": { 49 | "yui": "" 50 | }, 51 | "scripts": { 52 | "test": "node tests/jsxml-test.js" 53 | }, 54 | "homepage": "https://github.com/colorhook/jsxml", 55 | "repository": { 56 | "type": "git", 57 | "url": "git://github.com/colorhook/jsxml.git" 58 | }, 59 | "licenses": [ 60 | { 61 | "type": "MIT", 62 | "url": "http://github.com/colorhook/jsxml/blob/master/LICENSE" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /src/jsxml.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright 2011 http://github.com/colorhook/jsxml 3 | * @author: colorhook 4 | * @version:0.10.0 5 | */ 6 | /** 7 | * @preserve Copyright 2011 http://github.com/colorhook/jsxml 8 | * @author: colorhook 9 | * @version:0.10.0 10 | */ 11 | 12 | (function() { 13 | 14 | /** 15 | * XML parser comes from HTML Parser by John Resig (ejohn.org) 16 | * http://ejohn.org/files/htmlparser.js 17 | * Original code by Erik Arvidsson, Mozilla Public License 18 | * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js 19 | */ 20 | // Regular Expressions for parsing tags and attributes 21 | var startTag = /^<([0-9A-zÀ-ÿ\$_\.\-]+:{0,1}[A-zÀ-ÿ0-9\$\-\._]*)((?:\s+[A-zÀ-ÿ\$_]+:{0,1}[A-zÀ-ÿ0-9\$\-\._]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, 22 | endTag = /^<\/([A-zÀ-ÿ0-9\$\-_\.:]+)[^>]*>/, 23 | attr = /([A-zÀ-ÿ\$_]+:{0,1}[A-zÀ-ÿ0-9\$\-\._]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g, 24 | _parseXML, 25 | trim, 26 | merge, 27 | containsEntity, 28 | replaceToEntity, 29 | replaceFromEntity, 30 | Namespace, 31 | QName, 32 | NodeKind, 33 | _getXMLIfLengthEqualOne, 34 | XMLList, 35 | XML; 36 | 37 | /** 38 | * @description trim whitespace before or after the String 39 | * @param s{String} 40 | * @return String 41 | */ 42 | trim = String.prototype.trim ? function(s) { 43 | return s && s.trim ? s.trim() : s; 44 | } : function(s) { 45 | try { 46 | return s.replace(/^\s+|\s+$/g, ''); 47 | } catch (err) { 48 | return s; 49 | } 50 | }; 51 | /** 52 | * @description merge one object to another, used to extend object. 53 | * because of the toString property is not enumerable in IE browser, 54 | * so we must assign it explicity. 55 | * @param s1{Object} object need to be extended. 56 | * @param s2{Object} extension object 57 | * @return Object 58 | */ 59 | merge = function(s1, s2) { 60 | for (var i in s2) { 61 | if (s2.hasOwnProperty(i)) { 62 | s1[i] = s2[i]; 63 | } 64 | } 65 | }; 66 | /** 67 | * @description find element's index from an array. 68 | * @param array{Array} the array hosting the element 69 | * @param ele{Object} the element in the array 70 | * @return Number 71 | */ 72 | arrayIndexOf = function(array, ele) { 73 | if (array.indexOf) { 74 | return array.indexOf(ele); 75 | } 76 | for (var i = 0, l = array.length; i < l; i++) { 77 | if (ele === array[i]) { 78 | return i; 79 | } 80 | } 81 | return -1; 82 | }; 83 | /** 84 | * @description check if the string contains XML entity format(& < >) or not. 85 | * @param str{String} 86 | * @return Boolean 87 | */ 88 | containsEntity = function(str) { 89 | return str.match(/&(amp|lt|gt);/); 90 | }; 91 | /** 92 | * @description convert special characters (& < >) to XML entity format. 93 | * @param str{String} 94 | * @return String 95 | */ 96 | replaceToEntity = function(str) { 97 | str = str.replace(/&[^(amp;|lt;|gt;)]/g, '&'); 98 | str = str.replace(//g, '>'); 100 | return str; 101 | } 102 | /** 103 | * @description convert from XML entity format to readable characters. 104 | * @param str{String} 105 | * @return String 106 | */ 107 | replaceFromEntity = function(str) { 108 | str = str.replace(/</g, '<'); 109 | str = str.replace(/>/g, '>'); 110 | str = str.replace(/&/g, '&'); 111 | return str; 112 | } 113 | /** 114 | * @description parse XML string 115 | * @param xml{String} XML string 116 | * @param handler{Object} callback handler 117 | * @return void 118 | */ 119 | _parseXML = function(xml, handler) { 120 | var index, chars, match, stack = [], 121 | last = xml; 122 | stack.last = function() { 123 | return this[this.length - 1]; 124 | }; 125 | while (xml) { 126 | chars = true; 127 | 128 | //CDATA 129 | if (xml.indexOf(""); 132 | if (index > 0) { 133 | text = xml.substring(9, index); 134 | 135 | if (handler.chars) 136 | handler.chars(text, true); 137 | 138 | xml = xml.substring(index + 3); 139 | } else { 140 | throw new Error('[XML Parse Error] the CDATA end tag not found: ' + xml); 141 | } 142 | chars = false; 143 | } else if (xml.indexOf(""); 147 | if (index > 0) { 148 | text = xml.substr(2, index - 2); 149 | if (handler.instruction) { 150 | handler.instruction(text); 151 | } 152 | xml = xml.substring(index + 2); 153 | } else { 154 | throw new Error('[XML Parse Error] the Instruction end tag not found: ' + xml); 155 | } 156 | chars = false; 157 | } else if (xml.indexOf(""); 161 | 162 | if (index >= 0) { 163 | if (handler.comment) 164 | handler.comment(xml.substring(4, index)); 165 | xml = xml.substring(index + 3); 166 | chars = false; 167 | } else { 168 | throw new Error('[XML Parse Error] the Comment end tag not found: ' + xml); 169 | } 170 | } else if (xml.indexOf("]*(]*>)*[^<>]*>/i); 174 | if (m && m[0]) { 175 | var doctype = m[0]; 176 | index = doctype.length; 177 | text = xml.substr(2, index - 3); 178 | if (handler.doctype) { 179 | handler.doctype(text); 180 | } 181 | xml = xml.substring(index); 182 | chars = false; 183 | } 184 | } else if (xml.indexOf("= 2) { 286 | this.prefix = String(prefix); 287 | this.uri = String(uri); 288 | } else if (len === 1) { 289 | this.prefix = ""; 290 | this.uri = String(prefix); 291 | } else { 292 | this.prefix = ""; 293 | this.uri = ""; 294 | } 295 | } 296 | 297 | Namespace.prototype = { 298 | constructor: Namespace, 299 | 300 | /** 301 | * @description to string 302 | * @access public 303 | * @return String 304 | */ 305 | toString: function() { 306 | return this.uri; 307 | }, 308 | /** 309 | * @description return a Namespace copy object 310 | * @access public 311 | * @return Namespace 312 | */ 313 | copy: function() { 314 | var ns = new Namespace(); 315 | ns.prefix = this.prefix; 316 | ns.uri = this.uri; 317 | return ns; 318 | }, 319 | /** 320 | * @description check if the two Namespace are equivalent 321 | * @access public 322 | * @return Boolean 323 | */ 324 | equals: function(ns) { 325 | return this.prefix === ns.prefix && this.uri === ns.uri; 326 | } 327 | } 328 | /** 329 | * @description QName class 330 | * @access public 331 | * @param uri {Namespace | String} 332 | * @param localName {String} 333 | */ 334 | QName = function(uri, localName) { 335 | var len = arguments.length; 336 | if (len >= 2) { 337 | if (uri && uri.constructor === Namespace) { 338 | this.uri = uri.uri ? uri.uri : '' 339 | this._ns = uri 340 | } else { 341 | this.uri = String(uri) 342 | this._ns = new Namespace(uri) 343 | } 344 | this.localName = String(localName); 345 | } else if (len === 1) { 346 | this.uri = ""; 347 | this._ns = new Namespace(); 348 | this.localName = String(uri); 349 | } else { 350 | this.uri = ""; 351 | this._ns = new Namespace(); 352 | this.localName = ""; 353 | } 354 | } 355 | QName.prototype = { 356 | constructor: QName, 357 | 358 | /** 359 | * @description to string 360 | * @return String 361 | */ 362 | toString: function() { 363 | var r = this.uri ? this.uri + "::" : ""; 364 | return r + this.localName; 365 | }, 366 | /** 367 | * @description return a QName copy object 368 | * @return QName 369 | */ 370 | copy: function() { 371 | var qn = new QName(); 372 | qn.uri = this.uri; 373 | qn.localName = this.localName; 374 | qn._ns = this._ns.copy(); 375 | return qn; 376 | }, 377 | /** 378 | * @description check if the two QName are equivalent 379 | * @access public 380 | * @return Boolean 381 | */ 382 | equals: function(qname) { 383 | return this.localName === qname.localName && this._ns.equals(qname._ns); 384 | } 385 | } 386 | /** 387 | * @description parse a name & value to a QName 388 | * @access internal 389 | * @static 390 | * @param name{String} namespace declaration name 391 | * @param value{String} namespace declaration value 392 | * @return QName 393 | */ 394 | QName._format = function(name, value) { 395 | var temp = name.split(":"), 396 | prefix, localName; 397 | if (temp.length === 2) { 398 | prefix = temp[0]; 399 | localName = temp[1]; 400 | } else { 401 | prefix = ''; 402 | localName = name; 403 | } 404 | return new QName(new Namespace(prefix, value), localName); 405 | } 406 | /** 407 | * @description NodeKind class 408 | * @static 409 | */ 410 | NodeKind = { 411 | 'DOCTYPE': 'doctype', 412 | 'ELEMENT': 'element', 413 | 'COMMENT': 'comment', 414 | 'PROCESSING_INSTRUCTIONS': 'processing-instructions', 415 | 'TEXT': 'text', 416 | 'ATTRIBUTE': 'attribute' 417 | } 418 | 419 | /** 420 | * @description get a suitable class for XML operations by the XMLList. 421 | * if the list contains only one child, then return the child XML. 422 | * @return XML|XMLList 423 | */ 424 | _getXMLIfLengthEqualOne = function(list) { 425 | if (list._list && list.length() === 1) { 426 | return _getXMLIfLengthEqualOne(list._list[0]); 427 | } 428 | return list; 429 | }; 430 | /** 431 | * @description XMLList is a XML collection 432 | * @param null 433 | */ 434 | XMLList = function() { 435 | this._list = []; 436 | } 437 | merge(XMLList.prototype, { 438 | constructor: XMLList, 439 | 440 | /** 441 | * @description add a xml to the list 442 | * @access private 443 | * @return XMLList 444 | */ 445 | _addXML: function(xml) { 446 | if (xml.constructor === XML) { 447 | this._list.push(xml); 448 | } else if (xml.constructor === XMLList) { 449 | this._list = this._list.concat(xml._list); 450 | } 451 | return this; 452 | }, 453 | /** 454 | * @description get the list length 455 | * @access public 456 | * @return int 457 | */ 458 | length: function() { 459 | return this._list.length; 460 | }, 461 | /** 462 | * @description you cannot call this method in a list, only XML owns childIndex method. 463 | * @access none 464 | * @return void 465 | */ 466 | childIndex: function() { 467 | throw new Error("this method only availabe in single list XML"); 468 | }, 469 | /** 470 | * @description return all the child by localName in this xml list. 471 | * @access public 472 | * @return XMLList | XML 473 | */ 474 | child: function(p) { 475 | var list = new XMLList(); 476 | this.each(function(item) { 477 | var child = item.child(p); 478 | if (child.length()) { 479 | list._addXML(child); 480 | } 481 | }); 482 | return _getXMLIfLengthEqualOne(list); 483 | }, 484 | /** 485 | * @description return all the child in this xml list. 486 | * @access public 487 | * @return XMLList | XML 488 | */ 489 | children: function() { 490 | return this.child('*'); 491 | }, 492 | /** 493 | * @description remove the element or attribute from parent node 494 | * @access public 495 | */ 496 | remove: function() { 497 | this.each(function(item) { 498 | item.remove(); 499 | }); 500 | }, 501 | /** 502 | * @description return a special attribute type child by parameter 503 | * @access public 504 | * @param p{String} if the p is '*', return all attributes 505 | * @return XML | XMLList 506 | * @see attributes 507 | */ 508 | attribute: function(p) { 509 | var list = new XMLList(); 510 | this.each(function(item) { 511 | list._addXML(item.attribute(p)); 512 | }); 513 | return _getXMLIfLengthEqualOne(list); 514 | }, 515 | /** 516 | * @description return all attribute type child. 517 | * @access public 518 | * @return XML | XMLList 519 | * @see attribute 520 | */ 521 | attributes: function() { 522 | return this.attribute('*'); 523 | }, 524 | /** 525 | * @description find special elements child from the XMLList tree top. 526 | * @access public 527 | * @param p{String} the element localName, if the localName is '*', return all the element child. 528 | * @return XML | XMLList 529 | */ 530 | elements: function(p) { 531 | var list = new XMLList(); 532 | this.each(function(item) { 533 | list._addXML(item.elements(p)); 534 | }); 535 | return _getXMLIfLengthEqualOne(list); 536 | }, 537 | /** 538 | * @description find descendants child from the XMLList tree top. 539 | * @access public 540 | * @param p{String} the descendant localName, if the localName is '*', return all the descendants. 541 | * @return XML | XMLList 542 | */ 543 | descendants: function(p) { 544 | var list = new XMLList(); 545 | this.each(function(item) { 546 | list._addXML(item.descendants(p)); 547 | }); 548 | return _getXMLIfLengthEqualOne(list); 549 | }, 550 | /** 551 | * @description merge multi text childs nearby to one text child of each list item. 552 | * @access public 553 | * @return void 554 | */ 555 | normalize: function() { 556 | this.each(function(item) { 557 | item.normalize(); 558 | }); 559 | }, 560 | /** 561 | * @description check if all the list item owns simple content only 562 | * @access public 563 | * @return Boolean 564 | * @see hasComplexContent 565 | */ 566 | hasSimpleContent: function() { 567 | for (var i = 0, l = this._list.length; i < l; i++) { 568 | var item = this._list[i]; 569 | if (item.constructor === XMLList || 570 | item.hasComplexContent()) { 571 | return false; 572 | } 573 | } 574 | return true; 575 | }, 576 | /** 577 | * @description check if all the list item owns simple content only 578 | * @access public 579 | * @return Boolean 580 | * @see hasSimpleContent 581 | */ 582 | hasComplexContent: function() { 583 | return !this.hasSimpleContent(); 584 | }, 585 | /** 586 | * @description return the text type child 587 | * @access public 588 | * @return XML 589 | */ 590 | text: function() { 591 | var xml = new XMLList(); 592 | this.each(function(item) { 593 | if (item.constructor === XML) { 594 | var t = item.text(); 595 | if (t._text !== "") { 596 | xml._addXML(t); 597 | } 598 | } 599 | }); 600 | return _getXMLIfLengthEqualOne(xml); 601 | }, 602 | /** 603 | * @description return the comment type child 604 | * @access public 605 | * @return XML | XMLList 606 | */ 607 | comments: function() { 608 | var xml = new XMLList(); 609 | this.each(function(item) { 610 | if (item.constructor === XML) { 611 | xml._addXML(item.comments()); 612 | } 613 | }); 614 | return _getXMLIfLengthEqualOne(xml); 615 | }, 616 | /** 617 | * @description return the XML processing-instructions. 618 | * @access public 619 | * @return XMLList | XML 620 | */ 621 | processingInstructions: function() { 622 | var xml = new XMLList(); 623 | this.each(function(item) { 624 | if (item.constructor === XML) { 625 | xml._addXML(item.processingInstructions()); 626 | } 627 | }); 628 | return _getXMLIfLengthEqualOne(xml); 629 | }, 630 | /** 631 | * @description return an XMLList copy object. 632 | * @access public 633 | * @return XMLList 634 | */ 635 | copy: function() { 636 | var list = new XMLList(); 637 | this.each(function(item, i) { 638 | list._list[i] = item.copy(); 639 | }); 640 | return list; 641 | }, 642 | /** 643 | * @description return toXMLString of all the list item 644 | * @access public 645 | * @return String 646 | */ 647 | toXMLString: function() { 648 | var s = []; 649 | this.each(function(item) { 650 | s.push(item.toXMLString()); 651 | }); 652 | return s.join("\n"); 653 | }, 654 | /** 655 | * @description return toString of all the list item. 656 | * @access public 657 | * @return String 658 | */ 659 | toString: function() { 660 | var s = ""; 661 | this.each(function(item) { 662 | s += item.toString(); 663 | }); 664 | return s; 665 | }, 666 | //===================extension for javascript================= 667 | /** 668 | * @description return a special item by item index. 669 | * @access public 670 | * @param n{int} the item index 671 | * @return XML 672 | */ 673 | item: function(n) { 674 | return this._list[n]; 675 | }, 676 | /** 677 | * @description for loop the list item. 678 | * @access public 679 | * @param func{Function} the callback handler 680 | * @return void 681 | */ 682 | each: function(func) { 683 | for (var i = 0, l = this._list.length; i < l; i++) { 684 | func(this._list[i], i, this); 685 | } 686 | } 687 | }); 688 | /** 689 | * @description XML class 690 | * @param str{String} 691 | */ 692 | XML = function(str, debug) { 693 | this._children = []; 694 | this._attributes = []; 695 | this._namespaces = []; 696 | this._doctypes = []; 697 | this._nodeKind = NodeKind.ELEMENT; 698 | this._qname = null; 699 | this._parent = null; 700 | this._text = null; 701 | this._useCDATA = false; 702 | 703 | var current; 704 | var self = this; 705 | 706 | _parseXML(str, { 707 | start: function(tag, attrs, unary) { 708 | var xml; 709 | if (!current) { 710 | if (XML.createMainDocument) { 711 | xml = new XML(); 712 | xml._parent = self; 713 | } else { 714 | xml = self; 715 | } 716 | } else { 717 | xml = new XML(); 718 | xml._parent = current; 719 | } 720 | xml._qname = QName._format(tag); 721 | for (var i in attrs) { 722 | var attr = new XML(); 723 | attr._parent = xml; 724 | attr._nodeKind = NodeKind.ATTRIBUTE; 725 | var _qname; 726 | if (attrs[i].name === 'xmlns') { 727 | _qname = new QName(new Namespace('xmlns', attrs[i].value), ''); 728 | } else { 729 | _qname = QName._format(attrs[i].name, attrs[i].value); 730 | } 731 | var prefix = _qname._ns.prefix || ""; 732 | 733 | if (prefix === 'xmlns') { 734 | var ns = new Namespace(_qname.localName, _qname.uri); 735 | xml.addNamespace(ns); 736 | if (_qname.localName === xml._qname._ns.prefix) { 737 | xml.setNamespace(ns); 738 | } 739 | } else { 740 | attr._qname = _qname; 741 | attr._text = attrs[i].value; 742 | xml._attributes.push(attr); 743 | } 744 | } 745 | current = xml; 746 | if (unary) { 747 | this.end(tag); 748 | } 749 | }, 750 | chars: function(text, useCDATA) { 751 | var trimed = trim(text); 752 | if (trimed === "" && XML.ignoreWhitespace) { 753 | return; 754 | } 755 | var el = new XML(); 756 | el._nodeKind = NodeKind.TEXT; 757 | el._text = trimed; 758 | //el._originalText = text; 759 | el._useCDATA = useCDATA; 760 | current._children.push(el); 761 | }, 762 | end: function(tag) { 763 | if (current && current._parent) { 764 | current._parent._children.push(current); 765 | current = current._parent; 766 | } else if (current === self) { 767 | current = null; 768 | } 769 | }, 770 | comment: function(value) { 771 | if (!current && XML.createMainDocument) { 772 | current = self; 773 | } 774 | var el = new XML(); 775 | el._nodeKind = NodeKind.COMMENT; 776 | el._text = value; 777 | current && current._children.push(el); 778 | }, 779 | instruction: function(value) { 780 | if (!current && XML.createMainDocument) { 781 | current = self; 782 | } 783 | 784 | var el = new XML(); 785 | el._nodeKind = NodeKind.PROCESSING_INSTRUCTIONS; 786 | el._text = value; 787 | current && current._children.push(el); 788 | }, 789 | doctype: function(value) { 790 | var el = new XML(); 791 | el._nodeKind = NodeKind.DOCTYPE; 792 | el._text = value; 793 | if (current) { 794 | current._children.push(el); 795 | } else { 796 | self._doctypes.push(el); 797 | } 798 | } 799 | }); 800 | 801 | } 802 | 803 | merge(XML.prototype, { 804 | /** 805 | * @description add new namespace to this XML 806 | * @access public 807 | * @param ns{Namespace} 808 | * @return void 809 | */ 810 | addNamespace: function(ns) { 811 | if (ns.prefix !== undefined) { 812 | this.removeNamespace(ns); 813 | this._namespaces.push(ns); 814 | } 815 | }, 816 | /** 817 | * @description remove a namespace from this XML 818 | * @access public 819 | * @param ns{Namespace} 820 | * @return void 821 | */ 822 | removeNamespace: function(ns) { 823 | for (var i = 0, l = this._namespaces.length; i < l; i++) { 824 | if (ns.prefix === this._namespaces[i].prefix) { 825 | this._namespaces.splice(i, 1); 826 | break; 827 | } 828 | } 829 | }, 830 | /** 831 | * @description reture a namespace by prefix 832 | * @access public 833 | * @param prefix{String} 834 | * @return Namespace 835 | */ 836 | namespace: function(prefix) { 837 | if (!prefix) { 838 | return new Namespace(); 839 | } 840 | for (var i = 0, l = this._namespaces.length; i < l; i++) { 841 | if (prefix === this._namespaces[i].prefix) { 842 | return this._namespaces[i]; 843 | } 844 | } 845 | return undefined; 846 | }, 847 | /** 848 | * @description set the namespace for this XML 849 | * @access public 850 | * @param ns{Namespace} 851 | * @return void 852 | */ 853 | setNamespace: function(ns) { 854 | if (ns && ns.constructor === Namespace) { 855 | this.addNamespace(ns); 856 | if (this._qname) { 857 | this._qname.uri = ns.uri; 858 | this._qname._ns = ns; 859 | } 860 | } 861 | }, 862 | /** 863 | * @description return declarated namespace of this XML 864 | * @access public 865 | * @return Array 866 | */ 867 | namespaceDeclarations: function() { 868 | return this._namespaces; 869 | }, 870 | /** 871 | * @description return declarated namespace of this XML and all parent XML. 872 | * @access public 873 | * @return Array 874 | */ 875 | inScopeNamespaces: function() { 876 | var array = this._namespaces; 877 | var chain = this._parent; 878 | while (chain) { 879 | array = chain.inScopeNamespaces().concat(array); 880 | chain = chain._parent; 881 | } 882 | return array; 883 | }, 884 | /** 885 | * @description return the nodekind of this element 886 | * @access public 887 | * @return String 888 | * @see NodeKind 889 | */ 890 | nodeKind: function() { 891 | return this._nodeKind; 892 | }, 893 | /** 894 | * @description return the full name (with declarated namespace) of this xml 895 | * @access public 896 | * @return String 897 | * @see localName 898 | */ 899 | name: function() { 900 | if (!this._qname) { 901 | return null; 902 | } 903 | if (this._qname.uri) { 904 | return this._qname.uri + ":" + this._qname.localName; 905 | } 906 | return this._qname.localName; 907 | }, 908 | /** 909 | * @description return the local name (without declarated namespace) of this xml 910 | * @access public 911 | * @return String 912 | * @see name 913 | */ 914 | localName: function() { 915 | if (!this._qname) { 916 | return null; 917 | } 918 | return this._qname.localName; 919 | }, 920 | /** 921 | * @description set the full name (with declarated namespace) of this xml 922 | * @access public 923 | * @return void 924 | * @see name 925 | */ 926 | setName: function(name) { 927 | if (this._qname == null) { 928 | return; 929 | } 930 | var regexp = /^[a-zA-Z\$_]+[\:a-zA-Z0-9\$\-_\.]*$/ 931 | if (regexp.test(name) && name.split(':').length <= 2) { 932 | this._qname = QName._format(name) 933 | } else { 934 | throw new Error("invalid value for XML name"); 935 | } 936 | }, 937 | /** 938 | * @description set the local name (without declarated namespace) of this xml 939 | * @access public 940 | * @return void 941 | * @see localName 942 | */ 943 | setLocalName: function(name) { 944 | if (this._qname == null) { 945 | return; 946 | } 947 | if (/^[a-zA-Z\$_]+[a-zA-Z0-9\$\-_\.]*$/.test(name)) { 948 | this._qname.localName = name; 949 | } else { 950 | throw new Error("invalid value for XML localName"); 951 | } 952 | }, 953 | /** 954 | * @description get the length of this xml tree, return 1 always for XML. 955 | * @access public 956 | * @return int 957 | */ 958 | length: function() { 959 | return 1; 960 | }, 961 | /** 962 | * @description return a special attribute type child by param 963 | * @access public 964 | * @param p{String} if the p is '*', return all attributes 965 | * @return XML | XMLList 966 | * @see attributes 967 | */ 968 | attribute: function(p) { 969 | var attributes = this._attributes, 970 | i, 971 | l, 972 | item, 973 | list = new XMLList(); 974 | for (i = 0, l = attributes.length; i < l; i++) { 975 | item = attributes[i]; 976 | if (item._qname.localName === p || p === '*') { 977 | list._addXML(item); 978 | } 979 | } 980 | return _getXMLIfLengthEqualOne(list); 981 | }, 982 | /** 983 | * @description return all attribute type child. 984 | * @access public 985 | * @return XML | XMLList 986 | * @see attribute 987 | */ 988 | attributes: function() { 989 | return this.attribute('*'); 990 | }, 991 | /** 992 | * @description create a text type XML node by the text parameter, 993 | * if the text contains special characters, use CDATA tag. 994 | * @access private 995 | * @param text{String} 996 | * @return XML 997 | */ 998 | _createTextNode: function(text) { 999 | var el = new XML(); 1000 | el._nodeKind = NodeKind.TEXT; 1001 | el._text = text; 1002 | el._useCDATA = /['"<>&]/.test(text); 1003 | return el; 1004 | }, 1005 | /** 1006 | * @description append child to the children list 1007 | * @access public 1008 | * @param child{XML | XMLList | String} the child need to be add 1009 | * @return XML 1010 | * @see prependChild 1011 | */ 1012 | appendChild: function(child) { 1013 | var cc = child.constructor; 1014 | if (cc === XML) { 1015 | child._parent = this; 1016 | this._children.push(child); 1017 | } else if (cc === XMLList) { 1018 | child.each(function(item) { 1019 | item._parent = this; 1020 | }); 1021 | this._children = this._children.concat(child._list); 1022 | } else if (cc === String) { 1023 | var c = this._createTextNode(child); 1024 | c._parent = this; 1025 | this._children.push(c); 1026 | } 1027 | return this; 1028 | }, 1029 | /** 1030 | * @description remove from parent node 1031 | * @access public 1032 | */ 1033 | remove: function() { 1034 | if (!this._parent) { 1035 | return; 1036 | } 1037 | var nk = this._nodeKind; 1038 | var list; 1039 | if (nk === NodeKind.ATTRIBUTE) { 1040 | list = this._parent._attributes; 1041 | } else { 1042 | list = this._parent._children; 1043 | } 1044 | var index = list.indexOf(this); 1045 | if (index !== -1) { 1046 | list.splice(index, 1) 1047 | } 1048 | }, 1049 | /** 1050 | * @description prepend child to the children list 1051 | * @access public 1052 | * @param child{XML | XMLList | String} the child need to be add 1053 | * @return XML 1054 | * @see appendChild 1055 | */ 1056 | prependChild: function(child) { 1057 | var cc = child.constructor; 1058 | if (cc === XML) { 1059 | child._parent = this; 1060 | this._children.unshift(child); 1061 | } else if (cc === XMLList) { 1062 | child.each(function(item) { 1063 | item._parent = this; 1064 | }); 1065 | this._children = this._list.concat(this._children); 1066 | } else if (cc === String) { 1067 | var c = this._createTextNode(child); 1068 | c._parent = this; 1069 | this._children.unshift(c); 1070 | } 1071 | return this; 1072 | }, 1073 | /** 1074 | * @description merge multi text childs nearby to one text child. 1075 | * @access public 1076 | * @return void 1077 | */ 1078 | normalize: function() { 1079 | var i, 1080 | l, 1081 | preTextEl, 1082 | _c = this._children, 1083 | newChildren = []; 1084 | 1085 | for (i = 0, l = _c.length; i < l; i++) { 1086 | var item = _c[i], 1087 | nk = item.nodeKind(); 1088 | if (nk === NodeKind.TEXT) { 1089 | if (preTextEl) { 1090 | item._text = preTextEl._text + item._text; 1091 | _c[i - 1] = null; 1092 | } 1093 | preTextEl = item; 1094 | } else if (nk === NodeKind.ELEMENT) { 1095 | item.normalize(); 1096 | preTextEl = null; 1097 | } 1098 | } 1099 | for (i = 0, l = _c.length; i < l; i++) { 1100 | _c[i] && newChildren.push(_c[i]); 1101 | } 1102 | this._children = newChildren; 1103 | }, 1104 | /** 1105 | * @description return a filter children list, if the ignoreComments is true, the list will 1106 | * not contains any comment child, same as processing-instructions child. 1107 | * @access private 1108 | * @return Array 1109 | */ 1110 | _getFilterChildren: function() { 1111 | var i, 1112 | l, 1113 | c = [], 1114 | _c = this._children; 1115 | 1116 | for (i = 0, l = _c.length; i < l; i++) { 1117 | var item = _c[i], 1118 | nk = item.nodeKind(); 1119 | if (nk === NodeKind.ELEMENT || nk === NodeKind.TEXT || 1120 | (nk === NodeKind.COMMENT && !XML.ignoreComments) || 1121 | (nk === NodeKind.PROCESSING_INSTRUCTIONS && !XML.ignoreProcessingInstructions)) { 1122 | c.push(item); 1123 | } 1124 | } 1125 | return c; 1126 | }, 1127 | /** 1128 | * @description find a child by localName 1129 | * @access public 1130 | * @param p{String} the child localName, if the localName is '*', return all the child. 1131 | * @return XML | XMLList 1132 | */ 1133 | child: function(p) { 1134 | var list = new XMLList(), 1135 | i, 1136 | l, 1137 | c = this._getFilterChildren(); 1138 | if (typeof p === 'number') { 1139 | if (c.length !== 0 && c[p]) { 1140 | list._addXML(c[p]); 1141 | } 1142 | } else { 1143 | for (i = 0, l = c.length; i < l; i++) { 1144 | var xml = c[i]; 1145 | if (xml.localName() === p || p === "*") { 1146 | list._addXML(xml); 1147 | } 1148 | } 1149 | } 1150 | 1151 | return _getXMLIfLengthEqualOne(list); 1152 | }, 1153 | /** 1154 | * @description return the child index in its parent child list, return -1 if it is a root XML. 1155 | * @access public 1156 | * @return int 1157 | */ 1158 | childIndex: function(p) { 1159 | if (this._parent) { 1160 | return arrayIndexOf(this._parent._getFilterChildren(), this); 1161 | } 1162 | return -1; 1163 | }, 1164 | /** 1165 | * @description return all the child 1166 | * @access public 1167 | * @return XML | XMLList 1168 | */ 1169 | children: function() { 1170 | return this.child("*"); 1171 | }, 1172 | /** 1173 | * @description set the child content 1174 | * @access public 1175 | * @param child{string | XML | XMLList} the child need to be replaced 1176 | * @return XML 1177 | */ 1178 | setChildren: function(child) { 1179 | this._children = []; 1180 | return this.appendChild(child); 1181 | }, 1182 | /** 1183 | * @description replace a child by new content. 1184 | * @access public 1185 | * @param a{string} the child need to be replaced 1186 | * @param b{String | XML | XMLList | null} the new content 1187 | * @return XML 1188 | */ 1189 | replace: function(a, b) { 1190 | var replacedIndex = -1, 1191 | i, 1192 | l, 1193 | c = this._children, 1194 | newChildren = []; 1195 | 1196 | for (i = 0, l = c.length; i < l; i++) { 1197 | var xml = c[i], 1198 | nk = xml.nodeKind(); 1199 | if ((xml.localName() === a || a === "*") && nk === NodeKind.ELEMENT) { 1200 | if (replacedIndex === -1) { 1201 | replacedIndex = i; 1202 | if (b) { 1203 | var cc = b.constructor; 1204 | if (cc === XML) { 1205 | b._parent = this; 1206 | newChildren.push(b); 1207 | } else if (cc === XMLList) { 1208 | b.each(function(item) { 1209 | item._parent = this; 1210 | }); 1211 | newChildren = newChildren.concat(b._list); 1212 | } else if (cc === String) { 1213 | var t = this._createTextNode(b); 1214 | t._parent = this; 1215 | newChildren.push(t); 1216 | } 1217 | } 1218 | } 1219 | } else { 1220 | newChildren.push(xml); 1221 | } 1222 | } 1223 | if (replacedIndex !== -1) { 1224 | this._children = newChildren; 1225 | this.normalize(); 1226 | } 1227 | return this; 1228 | }, 1229 | /** 1230 | * @description find element type child. 1231 | * @access public 1232 | * @param p{String} the child localName, if the localName is '*', return all the element type child. 1233 | * @return XML | XMLList 1234 | */ 1235 | elements: function(p) { 1236 | if (arguments.length === 0) { 1237 | p = '*'; 1238 | } 1239 | var list = new XMLList(), 1240 | i, 1241 | l, 1242 | c = this._children; 1243 | 1244 | for (i = 0, l = c.length; i < l; i++) { 1245 | var xml = c[i]; 1246 | if ((xml.localName() === p || p === '*') && xml.nodeKind() === NodeKind.ELEMENT) { 1247 | list._addXML(xml); 1248 | } 1249 | } 1250 | return _getXMLIfLengthEqualOne(list); 1251 | }, 1252 | /** 1253 | * @description find descendants child from the XML tree top. 1254 | * @access public 1255 | * @param p{String} the descendant localName, if the localName is '*', return all the descendants. 1256 | * @return XML | XMLList 1257 | */ 1258 | descendants: function(p) { 1259 | if (arguments.length === 0) { 1260 | p = '*'; 1261 | } 1262 | var list = new XMLList(), 1263 | i, 1264 | l, 1265 | c = this._children; 1266 | 1267 | for (i = 0, l = c.length; i < l; i++) { 1268 | var xml = c[i], 1269 | nk = xml.nodeKind(); 1270 | if ((xml.localName() === p || p === '*') && (nk === NodeKind.ELEMENT || nk === NodeKind.TEXT)) { 1271 | list._addXML(xml); 1272 | } 1273 | if (xml._nodeKind === NodeKind.ELEMENT) { 1274 | list._addXML(xml.descendants(p)); 1275 | } 1276 | } 1277 | return _getXMLIfLengthEqualOne(list); 1278 | }, 1279 | /** 1280 | * @description insert a child after the special child. 1281 | * @access public 1282 | * @param child1{XML | XMLList} the child in XML 1283 | * @param child2{XML | XMLList} the child need to be inserted. 1284 | * @return XML 1285 | * @see insertChildAfter 1286 | */ 1287 | insertChildBefore: function(child1, child2) { 1288 | if (child1 == null) { 1289 | return this.appendChild(child2); 1290 | } 1291 | if (child1.constructor !== XML) { 1292 | return undefined; 1293 | } 1294 | var cc = child1.childIndex(); 1295 | if (child1._parent === this && cc !== -1) { 1296 | if (child2.constructor === XML) { 1297 | child2._parent = this; 1298 | this._children.splice(cc, 0, child2); 1299 | } else if (child2.constructor === XMLList) { 1300 | for (var i = 0, l = child2._list.length; i < l; i++) { 1301 | child2._list[i]._parent = this; 1302 | this._children.splice(cc + i, 0, child2._list[i]); 1303 | } 1304 | } else { 1305 | return undefined; 1306 | } 1307 | return this; 1308 | } else { 1309 | return undefined; 1310 | } 1311 | }, 1312 | /** 1313 | * @description insert a child before the special child. 1314 | * @access public 1315 | * @param child1{XML | XMLList} the child in XML 1316 | * @param child2{XML | XMLList} the child need to be inserted. 1317 | * @return XML 1318 | * @see insertChildBefore 1319 | */ 1320 | insertChildAfter: function(child1, child2) { 1321 | if (child1 == null) { 1322 | return this.prependChild(child2); 1323 | } 1324 | if (child1.constructor !== XML) { 1325 | return undefined; 1326 | } 1327 | var cc = child1.childIndex(); 1328 | if (child1._parent === this && cc !== -1) { 1329 | if (child2.constructor === XML) { 1330 | child2._parent = this; 1331 | this._children.splice(cc + 1, 0, child2); 1332 | } else if (child2.constructor === XMLList) { 1333 | for (var i = 0, l = child2._list.length; i < l; i++) { 1334 | child2._list[i]._parent = this; 1335 | this._children.splice(cc + 1 + i, 0, child2._list[i]); 1336 | } 1337 | } else { 1338 | return undefined; 1339 | } 1340 | return this; 1341 | } else { 1342 | return undefined; 1343 | } 1344 | }, 1345 | /** 1346 | * @description return the parent XML. 1347 | * @access public 1348 | * @return XML 1349 | */ 1350 | parent: function() { 1351 | return this._parent; 1352 | }, 1353 | /** 1354 | * @description check if the XML contains element type child, If has return false. 1355 | * @acess public 1356 | * @return Boolean 1357 | * @see hasComplexContent 1358 | */ 1359 | hasSimpleContent: function() { 1360 | var c = this._children; 1361 | for (var i = 0, l = c.length; i < l; i++) { 1362 | var nk = c[i].nodeKind(); 1363 | if (nk === NodeKind.ELEMENT) { 1364 | return false; 1365 | } 1366 | } 1367 | return true; 1368 | }, 1369 | /** 1370 | * @description check if the XML contains element type child, If has return true. 1371 | * @access public 1372 | * @return Boolean 1373 | * @see hasSimpleContent 1374 | */ 1375 | hasComplexContent: function() { 1376 | return !this.hasSimpleContent(); 1377 | }, 1378 | /** 1379 | * @description return the comment type child 1380 | * @access public 1381 | * @return XML | XMLList 1382 | */ 1383 | comments: function() { 1384 | var list = new XMLList(), 1385 | i, 1386 | l, 1387 | c = this._getFilterChildren(); 1388 | 1389 | for (i = 0, l = c.length; i < l; i++) { 1390 | var xml = c[i]; 1391 | if (xml.nodeKind() === NodeKind.COMMENT) { 1392 | list._addXML(xml); 1393 | } 1394 | } 1395 | return _getXMLIfLengthEqualOne(list); 1396 | }, 1397 | /** 1398 | * @description return the text type child 1399 | * @access public 1400 | * @return XML 1401 | */ 1402 | text: function() { 1403 | var c = this._children, 1404 | r = ""; 1405 | for (var i = 0, l = c.length; i < l; i++) { 1406 | var nk = c[i].nodeKind(); 1407 | if (nk === NodeKind.TEXT) { 1408 | if (c[i]._useCDATA) { 1409 | r += c[i]._text; 1410 | } else { 1411 | r += replaceFromEntity(c[i]._text); 1412 | } 1413 | } 1414 | } 1415 | return this._createTextNode(r); 1416 | }, 1417 | /** 1418 | * @description return the XML comments. 1419 | * @access public 1420 | * @return XMLList | XML 1421 | */ 1422 | comments: function() { 1423 | var list = new XMLList(), 1424 | i, 1425 | l, 1426 | c = this._getFilterChildren(); 1427 | 1428 | for (i = 0, l = c.length; i < l; i++) { 1429 | 1430 | var xml = c[i]; 1431 | if (xml.nodeKind && xml.nodeKind() === NodeKind.COMMENT) { 1432 | list._addXML(xml); 1433 | } 1434 | } 1435 | return _getXMLIfLengthEqualOne(list); 1436 | }, 1437 | /** 1438 | * @description return the XML processing-instructions. 1439 | * @access public 1440 | * @return XMLList | XML 1441 | */ 1442 | processingInstructions: function() { 1443 | var list = new XMLList(), 1444 | i, 1445 | l, 1446 | c = this._getFilterChildren(); 1447 | 1448 | for (i = 0, l = c.length; i < l; i++) { 1449 | 1450 | var xml = c[i]; 1451 | if (xml.nodeKind && xml.nodeKind() === NodeKind.PROCESSING_INSTRUCTIONS) { 1452 | list._addXML(xml); 1453 | } 1454 | } 1455 | return _getXMLIfLengthEqualOne(list); 1456 | }, 1457 | /** 1458 | * @description return an XML copy 1459 | * @access public 1460 | * @return XML 1461 | */ 1462 | copy: function() { 1463 | var xml = new XML(), 1464 | i, 1465 | l; 1466 | xml._nodeKind = this._nodeKind; 1467 | xml._text = this._text; 1468 | xml._useCDATA = this._useCDATA; 1469 | if (this._qname) { 1470 | xml._qname = this._qname.copy(); 1471 | } 1472 | for (i = 0, l = this._namespaces.length; i < l; i++) { 1473 | xml._namespaces[i] = this._namespaces[i].copy(); 1474 | } 1475 | for (i = 0, l = this._attributes.length; i < l; i++) { 1476 | xml._attributes[i] = this._attributes[i].copy(); 1477 | xml._attributes[i]._parent = xml; 1478 | } 1479 | for (i = 0, l = this._children.length; i < l; i++) { 1480 | xml._children[i] = this._children[i].copy(); 1481 | xml._children[i]._parent = xml; 1482 | } 1483 | return xml; 1484 | }, 1485 | /** 1486 | * @description format the XML to a String 1487 | * @param indent{int} the whitespace indent 1488 | * @access private 1489 | */ 1490 | _toXMLString: function(indent, scopeNamespace) { 1491 | var s = "", 1492 | tag, 1493 | i, 1494 | l, 1495 | nk = this._nodeKind, 1496 | ns = scopeNamespace ? this.inScopeNamespaces() : this._namespaces, 1497 | attrs = this._attributes, 1498 | children = this._children, 1499 | prettyPrinting = XML.prettyPrinting, 1500 | p = []; 1501 | indent = indent || 0; 1502 | 1503 | if (prettyPrinting) { 1504 | for (i = 0; i < indent; i++) { 1505 | s += XML.prettyTab?"\t":" "; 1506 | } 1507 | } 1508 | for (i = 0, l = this._doctypes.length; i < l; i++) { 1509 | s += this._doctypes[i]._toXMLString(indent) + "\n"; 1510 | } 1511 | 1512 | if (nk === NodeKind.ATTRIBUTE) { 1513 | return s + this._text; 1514 | } else if (nk === NodeKind.TEXT) { 1515 | if (this._useCDATA) { 1516 | return s + ""; 1517 | } else { 1518 | return s + replaceToEntity(this._text); 1519 | } 1520 | } else if (nk === NodeKind.COMMENT) { 1521 | return s + ""; 1522 | } else if (nk === NodeKind.PROCESSING_INSTRUCTIONS) { 1523 | return s + ""; 1524 | } else if (nk === NodeKind.DOCTYPE) { 1525 | return s + ""; 1526 | } 1527 | 1528 | if (this._qname) { 1529 | // _qname was defined so this is not the "self" document 1530 | if (this._qname._ns.prefix) { 1531 | tag = this._qname._ns.prefix + ":" + this.localName(); 1532 | } else { 1533 | tag = this.localName(); 1534 | } 1535 | 1536 | s += "<" + tag; 1537 | for (i = 0, l = ns.length; i < l; i++) { 1538 | var prefix = ns[i].prefix ? 'xmlns:' + ns[i].prefix : 'xmlns'; 1539 | p.push({ 1540 | label: prefix, 1541 | value: ns[i].uri 1542 | }); 1543 | } 1544 | for (i = 0, l = attrs.length; i < l; i++) { 1545 | var q = attrs[i]._qname, 1546 | prefix = q._ns.prefix, 1547 | label; 1548 | if (prefix) { 1549 | label = prefix + ':' + q.localName; 1550 | } else { 1551 | label = q.localName; 1552 | } 1553 | p.push({ 1554 | label: label, 1555 | value: attrs[i]._text 1556 | }); 1557 | } 1558 | if (p.length > 0) { 1559 | for (i = 0, l = p.length; i < l; i++) { 1560 | s += " " + p[i].label + "=\"" + p[i].value + "\""; 1561 | } 1562 | } 1563 | } 1564 | p = []; 1565 | for (i = 0, l = children.length; i < l; i++) { 1566 | var el = children[i], 1567 | enk = el.nodeKind(); 1568 | 1569 | if (enk === NodeKind.ELEMENT) { 1570 | p.push(el); 1571 | } else if (enk === NodeKind.COMMENT && !XML.ignoreComments) { 1572 | p.push(el); 1573 | } else if (enk === NodeKind.PROCESSING_INSTRUCTIONS && !XML.ignoreProcessingInstructions) { 1574 | p.push(el); 1575 | } else if (enk === NodeKind.TEXT) { 1576 | p.push(el); 1577 | } else if (enk === NodeKind.DOCTYPE) { 1578 | p.push(el); 1579 | } 1580 | } 1581 | if (p.length === 0) { 1582 | if (s.length>0) 1583 | s += "/>"; 1584 | } else if (p.length === 1 && p[0].nodeKind() === NodeKind.TEXT) { 1585 | s += ">"; 1586 | s += p[0]._toXMLString(0); 1587 | s += ""; 1588 | } else { 1589 | if (this._qname) { 1590 | //only add this if there was a _qname -> meaning this is not the "self" document 1591 | s += ">"; 1592 | } 1593 | for (i = 0, l = p.length; i < l; i++) { 1594 | if (prettyPrinting && s!="") { 1595 | s += "\n"; 1596 | } 1597 | s += p[i]._toXMLString(indent + (XML.prettyTab?1:XML.prettyIndent) - (!this._qname?(XML.prettyTab?1:XML.prettyIndent):0)); 1598 | } 1599 | if (prettyPrinting) { 1600 | s += "\n"; 1601 | for (i = 0; i < indent; i++) { 1602 | s += XML.prettyTab?"\t":" "; 1603 | } 1604 | } 1605 | if (tag) { 1606 | s += ""; 1607 | } 1608 | } 1609 | return s; 1610 | }, 1611 | /** 1612 | * @description return the XML string 1613 | * @access public 1614 | * @return String 1615 | */ 1616 | toXMLString: function() { 1617 | return this._toXMLString(0, true); 1618 | }, 1619 | /** 1620 | * @description return a string representation. If it contains complex content, return the toXMLString. 1621 | * @access public 1622 | * @return String 1623 | */ 1624 | toString: function() { 1625 | if (this.hasComplexContent()) { 1626 | return this.toXMLString(); 1627 | } 1628 | if (this.nodeKind() === NodeKind.TEXT || this.nodeKind() === NodeKind.ATTRIBUTE) { 1629 | return this._text; 1630 | } 1631 | var s = ""; 1632 | for (var i = 0, l = this._children.length; i < l; i++) { 1633 | var el = this._children[i]; 1634 | if (el._nodeKind === NodeKind.TEXT) { 1635 | if (el._useCDATA) { 1636 | s += el._text; 1637 | } else { 1638 | s += replaceFromEntity(el._text); 1639 | } 1640 | } 1641 | } 1642 | return s; 1643 | }, 1644 | //================extension for javascript========================= 1645 | /** 1646 | * @description compatible to XMLList 1647 | * @access public 1648 | * @param func{Function} callback function 1649 | * @return void 1650 | * @see XMLList.prototype.each 1651 | */ 1652 | each: function(func) { 1653 | func(this, 0, this); 1654 | }, 1655 | /** 1656 | * @description get the XML simple node value 1657 | * @access public 1658 | * @return String 1659 | * @see setValue 1660 | */ 1661 | getValue: function() { 1662 | var nk = this._nodeKind; 1663 | if (nk === NodeKind.TEXT) { 1664 | if (!this._useCDATA && containsEntity(this._text)) { 1665 | return replaceFromEntity(this._text); 1666 | } 1667 | return this._text; 1668 | } else if (nk === NodeKind.ATTRIBUTE) { 1669 | return this._text; 1670 | } else if (nk === NodeKind.ELEMENT && this.hasSimpleContent()) { 1671 | var t = this.text(); 1672 | if (t.getValue) { 1673 | return t.getValue(); 1674 | } 1675 | } 1676 | return undefined; 1677 | }, 1678 | /** 1679 | * @description set the XML simple node value 1680 | * @access public 1681 | * @param val{String} the new node value 1682 | * @return void 1683 | * @see getValue 1684 | */ 1685 | setValue: function(val) { 1686 | var nk = this._nodeKind; 1687 | if (nk === NodeKind.TEXT || nk === NodeKind.ATTRIBUTE || nk === NodeKind.COMMENT || nk === NodeKind.PROCESSING_INSTRUCTIONS) { 1688 | this._text = val; 1689 | } else if (nk === NodeKind.ELEMENT && this.hasSimpleContent()) { 1690 | var c = [], 1691 | newText = this._createTextNode(val); 1692 | newText._parent = this; 1693 | c.push(newText); 1694 | for (var i = 0, l = this._children.length; i < l; i++) { 1695 | var item = this._children[i]; 1696 | if (item._nodeKind !== NodeKind.TEXT) { 1697 | c.push(item); 1698 | } 1699 | } 1700 | this._children = c; 1701 | } 1702 | return this; 1703 | } 1704 | }); 1705 | 1706 | 1707 | /** 1708 | * @description static properties and methods. 1709 | */ 1710 | merge(XML, { 1711 | 1712 | //create first object as the main document containing comments, processing instructions and first level nodes 1713 | createMainDocument: false, 1714 | ignoreComments: true, 1715 | ignoreProcessingInstructions: true, 1716 | ignoreWhitespace: true, 1717 | prettyIndent: 2, 1718 | prettyPrinting: true, 1719 | prettyTab: false, 1720 | htmlMode: false, 1721 | 1722 | /** 1723 | * @description get an object copy indicating the XML setting. 1724 | * @return Object 1725 | */ 1726 | settings: function() { 1727 | return { 1728 | createMainDocument: this.createMainDocument, 1729 | ignoreComments: this.ignoreComments, 1730 | ignoreProcessingInstructions: this.ignoreProcessingInstructions, 1731 | ignoreWhitespace: this.ignoreWhitespace, 1732 | prettyIndent: this.prettyIndent, 1733 | prettyPrinting: this.prettyPrinting, 1734 | prettyTab: this.prettyTab, 1735 | htmlMode: this.htmlMode 1736 | } 1737 | }, 1738 | /** 1739 | * @description set the XML setting 1740 | * @param sett{Object} 1741 | * @return void 1742 | */ 1743 | setSettings: function(sett) { 1744 | if (!sett) { 1745 | return 1746 | } 1747 | var assign = function(p) { 1748 | if (sett.hasOwnProperty(p)) { 1749 | XML[p] = sett[p]; 1750 | } 1751 | } 1752 | assign("createMainDocument"); 1753 | assign("ignoreComments"); 1754 | assign("ignoreProcessingInstructions"); 1755 | assign("ignoreWhitespace"); 1756 | assign("prettyIndent"); 1757 | assign("prettyPrinting"); 1758 | assign("prettyTab"); 1759 | assign("htmlMode"); 1760 | } 1761 | }); 1762 | 1763 | /** 1764 | * @description return global object jsxml 1765 | * @return Object 1766 | */ 1767 | var jsxml = { 1768 | containsEntity: containsEntity, 1769 | replaceToEntity: replaceToEntity, 1770 | replaceFromEntity: replaceFromEntity, 1771 | parseXML: _parseXML, 1772 | Namespace: Namespace, 1773 | QName: QName, 1774 | NodeKind: NodeKind, 1775 | XMLList: XMLList, 1776 | XML: XML 1777 | }; 1778 | 1779 | //support for nodejs 1780 | if (typeof exports !== "undefined") { 1781 | for (var i in jsxml) { 1782 | exports[i] = jsxml[i]; 1783 | } 1784 | } else if (typeof define === 'function') { 1785 | // Publish as AMD module 1786 | define(function() { 1787 | return jsxml; 1788 | }); 1789 | } else { 1790 | // Publish as global (in browsers) 1791 | this.jsxml = jsxml; 1792 | } 1793 | 1794 | }).call(this); 1795 | -------------------------------------------------------------------------------- /tests/jsxml-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsxml Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/jsxml-test.js: -------------------------------------------------------------------------------- 1 | if (typeof exports !== "undefined") { 2 | var YUI = require("yui").YUI; 3 | var jsxml = require("../src/jsxml.js"); 4 | } 5 | YUI().use('test', function (Y) { 6 | 7 | var Assert = Y.Assert, 8 | XML = jsxml.XML, 9 | XMLList = jsxml.XMLList, 10 | QName = jsxml.QName, 11 | NodeKind = jsxml.NodeKind, 12 | Namespace = jsxml.Namespace, 13 | example = "" 14 | + "sss" 15 | + "a" 16 | + "e" 17 | + "" 18 | + "" 19 | + "b" 20 | + "" 21 | + "

;'

]]>

" 22 | + "
"; 23 | 24 | //QName TestCase 25 | var qnameTestCase = new Y.Test.Case({ 26 | name:"jsxml.QName Test Case", 27 | testConstructor: function(){ 28 | Assert.isFunction(QName); 29 | Assert.areEqual(QName, (new QName()).constructor); 30 | }, 31 | testParams: function(){ 32 | 33 | var qn = new QName(); 34 | Assert.areEqual(qn.uri, ""); 35 | Assert.areEqual(qn.localName, ""); 36 | 37 | qn = new QName('A'); 38 | Assert.areEqual(qn.uri, ""); 39 | Assert.areEqual(qn.localName, "A"); 40 | 41 | qn = new QName(undefined, undefined); 42 | Assert.areEqual(qn.uri, "undefined"); 43 | Assert.areEqual(qn.localName, "undefined"); 44 | 45 | qn = new QName(false, null); 46 | Assert.areEqual(qn.uri, "false"); 47 | Assert.areEqual(qn.localName, "null"); 48 | 49 | }, 50 | testToString: function(){ 51 | var qn = new QName(); 52 | Assert.areEqual(qn.toString(), ""); 53 | 54 | qn = new QName("localName"); 55 | Assert.areEqual(qn.toString(), "localName"); 56 | 57 | qn = new QName("uri", "localName"); 58 | Assert.areEqual(qn.toString(), "uri::localName"); 59 | 60 | return 61 | qn = new QName(new Namespace("prefix", "http://uri"), "localName"); 62 | Assert.areEqual(qn.toString(), "http://uri::localName"); 63 | }, 64 | testCopy: function(){ 65 | var qn = new QName("uri", "localName"); 66 | var qn2 = qn.copy(); 67 | Assert.areEqual(qn2.toString(), "uri::localName"); 68 | }, 69 | testEquals: function(){ 70 | var qn = new QName("uri", "localName"); 71 | var qn2 = qn.copy(); 72 | Assert.isTrue(qn.equals(qn2)); 73 | qn2 = new QName("uri", "localName"); 74 | Assert.isTrue(qn.equals(qn2)); 75 | } 76 | }); 77 | //NodeKind TestCase 78 | var nodekindTestCase = new Y.Test.Case({ 79 | name: "jsxml NodeKind Test Case", 80 | testType: function(){ 81 | Assert.isObject(NodeKind); 82 | }, 83 | testValue: function(){ 84 | Assert.areEqual(NodeKind.ELEMENT, 'element'); 85 | Assert.areEqual(NodeKind.COMMENT, 'comment'); 86 | Assert.areEqual(NodeKind.PROCESSING_INSTRUCTIONS, 'processing-instructions'); 87 | Assert.areEqual(NodeKind.TEXT, 'text'); 88 | Assert.areEqual(NodeKind.ATTRIBUTE, 'attribute'); 89 | } 90 | }); 91 | 92 | //Namespace TestCase 93 | var namespaceTestCase = new Y.Test.Case({ 94 | name: "jsxml.Namespace Test Case", 95 | 96 | testConstructor: function(){ 97 | Assert.isFunction(Namespace); 98 | Assert.areEqual(Namespace, (new Namespace()).constructor); 99 | }, 100 | testParams: function(){ 101 | var ns = new Namespace(); 102 | Assert.areEqual(ns.prefix, ""); 103 | Assert.areEqual(ns.uri, ""); 104 | 105 | ns = new Namespace(undefined); 106 | Assert.areEqual(ns.prefix, ""); 107 | Assert.areEqual(ns.uri, "undefined"); 108 | 109 | ns = new Namespace(undefined, undefined); 110 | Assert.areEqual(ns.prefix, "undefined"); 111 | Assert.areEqual(ns.uri, "undefined"); 112 | 113 | ns = new Namespace(false, null); 114 | Assert.areEqual(ns.prefix, "false"); 115 | Assert.areEqual(ns.uri, "null"); 116 | 117 | ns = new Namespace("http://uri"); 118 | Assert.areEqual(ns.prefix, ""); 119 | Assert.areEqual(ns.uri, "http://uri"); 120 | 121 | ns = new Namespace("prefix", "http://uri"); 122 | Assert.areEqual(ns.prefix, "prefix"); 123 | Assert.areEqual(ns.uri, "http://uri"); 124 | }, 125 | testToString: function(){ 126 | var ns = new Namespace(); 127 | Assert.areEqual(ns.uri, ns.toString()); 128 | }, 129 | testCopy: function(){ 130 | var ns = new Namespace("prefix", "http://uri"); 131 | var ns2 = ns.copy(); 132 | Assert.areEqual(ns2.prefix, "prefix"); 133 | Assert.areEqual(ns2.uri, "http://uri"); 134 | }, 135 | testEquals: function(){ 136 | var ns = new Namespace("prefix", "http://uri"); 137 | var ns2 = ns.copy(); 138 | Assert.isTrue(ns.equals(ns2)); 139 | qn2 = new Namespace("prefix", "http://uri"); 140 | Assert.isTrue(ns.equals(ns2)); 141 | } 142 | }); 143 | 144 | //XMLList TestCase 145 | var xmllistTestCase = new Y.Test.Case({ 146 | name: "jsxml.XMLList TestCase", 147 | testConstructor: function(){ 148 | Assert.isFunction(XMLList); 149 | Assert.areEqual(XMLList, (new XMLList()).constructor); 150 | 151 | var list = new XMLList(); 152 | Assert.isArray(list._list); 153 | Assert.areEqual(list._list.length, 0); 154 | }, 155 | test_addXML: function(){ 156 | var list = new XMLList(); 157 | var xml = new XML(); 158 | list._addXML(xml); 159 | Assert.areEqual(list._list.length, 1); 160 | Assert.areEqual(xml, list._list[0]); 161 | }, 162 | testLength: function(){ 163 | var xml =new XML(""); 164 | Assert.areEqual(2, xml.child('b').length()); 165 | Assert.areEqual(0, xml.child('c').length()); 166 | }, 167 | 168 | testChildren: function(){ 169 | var xml = new XML("12"); 170 | var list = xml.child('b'); 171 | Assert.areEqual("12", list.children().toString()); 172 | Assert.areEqual("1\n2", list.children().toXMLString()); 173 | }, 174 | testChild: function(){ 175 | var xml = new XML("123"); 176 | var list = xml.child('b'); 177 | var cList = list.child('c'); 178 | Assert.areEqual("12", cList.children().toString()); 179 | Assert.areEqual("1\n2", cList.children().toXMLString()); 180 | }, 181 | testElements: function(){ 182 | var xml = new XML("123"); 183 | var list = xml.child('b'); 184 | var cList = list.elements('c'); 185 | Assert.areEqual("12", cList.children().toString()); 186 | Assert.areEqual("1\n2", cList.children().toXMLString()); 187 | }, 188 | testDescendants: function(){ 189 | var xml = new XML("123"); 190 | var list = xml.child('b'); 191 | var dList = list.descendants('d'); 192 | Assert.areEqual("123", dList.children().toString()); 193 | Assert.areEqual("1\n2\n3", dList.children().toXMLString()); 194 | }, 195 | testAttribute: function(){ 196 | var xml = new XML("123"); 197 | var list = xml.child('b'); 198 | var attList = list.attribute('a'); 199 | Assert.areEqual(2, attList.length()); 200 | Assert.areEqual('a1', attList.item(0).getValue()); 201 | Assert.areEqual('a2', attList.item(1).getValue()); 202 | }, 203 | testAttributes: function(){ 204 | var xml = new XML("123"); 205 | var list = xml.child('b'); 206 | var attList = list.attributes(); 207 | Assert.areEqual(4, attList.length()); 208 | Assert.areEqual('a1', attList.item(0).getValue()); 209 | Assert.areEqual('b1', attList.item(1).getValue()); 210 | Assert.areEqual('a2', attList.item(2).getValue()); 211 | Assert.areEqual('b2', attList.item(3).getValue()); 212 | }, 213 | testNormalize: function(){ 214 | var xml =new XML(" "); 215 | var i1 = xml.child('b').item(0); 216 | i1.appendChild("hello"); 217 | Assert.areEqual(1, xml.child('b').item(0)._children.length, 'after append "hello" the children length should be 1'); 218 | i1.appendChild(" world"); 219 | Assert.areEqual(2, xml.child('b').item(0)._children.length, 'after append "word" the children length should be 2'); 220 | xml.normalize(); 221 | Assert.areEqual(1, xml.child('b').item(0)._children.length, 'after normalize the children length should be 1'); 222 | }, 223 | testComments: function(){ 224 | XML.ignoreComments = false; 225 | var xml = new XML("12"); 226 | var c = xml.child('b').comments(); 227 | XML.ignoreComments = true; 228 | Assert.areEqual("\n", c.toXMLString()); 229 | }, 230 | testText: function(){ 231 | var xml = new XML("01234"); 232 | var list = xml.child('b'); 233 | var newList = list.text(); 234 | Assert.areEqual(2, newList.length()); 235 | Assert.areEqual("12", newList.toString()); 236 | }, 237 | testMethodProcessingInstructions: function(){ 238 | XML.ignoreProcessingInstructions = false; 239 | var xml = new XML("12"); 240 | var pi = xml.child('b').processingInstructions(); 241 | XML.ignoreProcessingInstructions = true; 242 | Assert.areEqual("\n", pi.toXMLString()); 243 | }, 244 | testHasSimpleContent: function(){ 245 | var xml = new XML('1'); 246 | var list = xml.child('b'); 247 | Assert.isTrue(list.hasSimpleContent()); 248 | }, 249 | testHasComplexContent: function(){ 250 | var xml = new XML(''); 251 | var list = xml.child('b'); 252 | Assert.isTrue(list.hasComplexContent()); 253 | }, 254 | testToString: function(){ 255 | var xml = new XML("12"); 256 | var list = xml.child('b'); 257 | Assert.areEqual( "12", list.toString()); 258 | }, 259 | testToXMLString: function(){ 260 | var xml = new XML("12"); 261 | var list = xml.child('b'); 262 | Assert.areEqual("1\n2", list.toXMLString()); 263 | 264 | xml = new XML(''); 265 | Assert.areEqual(2, xml._children.length, 'xml should has 2 child'); 266 | list = xml.child('b') 267 | Assert.areEqual("\n \n\n", list.toXMLString()); 268 | 269 | xml = new XML("12"); 270 | list = xml.child('b'); 271 | Assert.areEqual(2, list.length()); 272 | Assert.areEqual(XMLList, list.constructor); 273 | Assert.areEqual("\n 1\n \n \n \n\n2", list.toXMLString()); 274 | }, 275 | testCopy: function(){ 276 | var xml = new XML(''); 277 | var list = xml.child('a'); 278 | var listCopy = list.copy(); 279 | Assert.areEqual(list.toString(), listCopy.toString(), 'listCopy should has a same toString() value'); 280 | Assert.areEqual(list.toXMLString(), listCopy.toXMLString(), 'listCopy should has a same toXMLString() value'); 281 | }, 282 | testRemove: function() { 283 | var xml = new XML(''); 284 | var list = xml.child('a'); 285 | list.remove(); 286 | Assert.areEqual(xml.toXMLString(), '\n \n', 'XMLList instance remove method should remove the node from original parent xml'); 287 | xml = new XML(''); 288 | xml.attribute("m").remove(); 289 | Assert.areEqual(xml.toXMLString(), '', 'XMLList instance remove method should remove the attribute from original parent xml'); 290 | }, 291 | //extension for javascript 292 | testItem: function(){ 293 | var xml = new XML("12"); 294 | var item1 = xml.child('b').item(0); 295 | var item2 = xml.child('b').item(1); 296 | var item3 = xml.child('b').item(2); 297 | Assert.areEqual("1", item1.toXMLString()); 298 | Assert.areEqual("2", item2.toXMLString()); 299 | Assert.isUndefined(item3, 'item3 shoud be undefined'); 300 | }, 301 | testEach: function(){ 302 | var count = 0; 303 | var arr1 = []; 304 | var arr2 = []; 305 | var xml = new XML("12"); 306 | xml.child('b').each(function(item, index){ 307 | count++; 308 | arr1.push(item); 309 | arr2.push(index); 310 | }); 311 | Assert.areEqual(2, count, 'the each function should execute 2 times'); 312 | Assert.areEqual(0, arr2[0]); 313 | Assert.areEqual(1, arr2[1]); 314 | Assert.areEqual('1', arr1[0].toXMLString()); 315 | Assert.areEqual('2', arr1[1].toXMLString()); 316 | 317 | }, 318 | testDoctype: function(){ 319 | var str = "\n"; 320 | var xml = new XML(str); 321 | Assert.areEqual(str, xml.toXMLString()); 322 | }, 323 | testDoctype2: function(){ 324 | var str = '' + 325 | '' + 327 | '' + 328 | ']>' + 329 | 'xml content'; 330 | var passed = true; 331 | try{ 332 | new XML(str); 333 | }catch(err){ 334 | passed = false; 335 | } 336 | Assert.isTrue(passed); 337 | } 338 | 339 | }); 340 | var xml = new XML(); 341 | //XML TestCase 342 | var xmlTestCase = new Y.Test.Case({ 343 | 344 | name: "jsxml.XML TestCase", 345 | 346 | 347 | testConstructor: function(){ 348 | Assert.isFunction(XML); 349 | Assert.areEqual(XML, (new XML()).constructor); 350 | 351 | var xml = new XML(); 352 | Assert.isArray(xml._attributes); 353 | Assert.isArray(xml._children); 354 | Assert.isNull(xml._parent); 355 | Assert.areEqual(NodeKind.ELEMENT, xml._nodeKind); 356 | Assert.areEqual("",xml.toString()); 357 | Assert.areEqual("",xml.toXMLString()); 358 | }, 359 | testNodeKind: function(){ 360 | var list = new XML(); 361 | list._nodeKind = NodeKind.ATTRIBUTE; 362 | Assert.areEqual(list.nodeKind(), NodeKind.ATTRIBUTE); 363 | list._nodeKind = NodeKind.TEXT; 364 | Assert.areEqual(list.nodeKind(), NodeKind.TEXT); 365 | list._nodeKind = NodeKind.COMMENT; 366 | Assert.areEqual(list.nodeKind(), NodeKind.COMMENT); 367 | list._nodeKind = NodeKind.PROCESSING_INSTRUCTION; 368 | Assert.areEqual(list.nodeKind(), NodeKind.PROCESSING_INSTRUCTION); 369 | }, 370 | testLength: function(){ 371 | var xml = new XML(); 372 | Assert.areEqual(xml.length(), 1); 373 | }, 374 | 375 | testParseError: function(){ 376 | var xml, error = false; 377 | 378 | var cases = [ 379 | "", 380 | "", 381 | "", 382 | "", 383 | "", 384 | "a", 385 | "", 386 | "bcc2"); 602 | Assert.areEqual(3, xml._getFilterChildren().length); 603 | 604 | XML.ignoreComments = false; 605 | xml = new XML("bcc2"); 606 | var newIndex = xml.child('b').childIndex(); 607 | XML.ignoreComments = true; 608 | Assert.areEqual(1, newIndex); 609 | 610 | xml = new XML("1"); 611 | Assert.areEqual('1', xml.children().toXMLString(), "xml' s toXMLString should be 1"); 612 | Assert.areEqual('1', xml.children().toString(), "xml' s toString should be 1"); 613 | 614 | }, 615 | testChildren: function(child){ 616 | var xml = new XML("bcc2"); 617 | Assert.areEqual(3, xml.children().length()); 618 | 619 | XML.ignoreComments = false; 620 | var l2 = xml.children().length(); 621 | XML.ignoreComments = true; 622 | Assert.areEqual(4, l2); 623 | 624 | XML.ignoreComments = false; 625 | XML.ignoreProcessingInstructions = false; 626 | var l3 = xml.children().length(); 627 | XML.ignoreComments = true; 628 | XML.ignoreProcessingInstructions = true; 629 | Assert.areEqual(5, l3); 630 | }, 631 | testInsertChildBefore: function(){ 632 | var child = new XML(""); 633 | var xml = new XML(""); 634 | var f1 = new XML(""); 635 | var f2 = new XML(""); 636 | var f3 = new XML(""); 637 | 638 | var result = xml.insertChildBefore(xml.child('c'), child); 639 | Assert.areSame(result, xml); 640 | Assert.areEqual(f1.toXMLString(), result.toXMLString()); 641 | 642 | xml = new XML(""); 643 | result = xml.insertChildBefore(xml.child('d'), child); 644 | Assert.areSame(result, xml); 645 | Assert.areEqual(f2.toXMLString(), result.toXMLString()); 646 | 647 | xml = new XML(""); 648 | child = new XML(""); 649 | var f4 = new XML(""); 650 | result = xml.insertChildBefore(xml.child('c'), child.child('e')); 651 | Assert.areEqual(f4.toXMLString(), result.toXMLString()); 652 | 653 | xml = new XML(""); 654 | child = new XML(""); 655 | result = xml.insertChildBefore(null, child); 656 | Assert.areSame(result, xml); 657 | Assert.areEqual(f3.toXMLString(), result.toXMLString()); 658 | 659 | xml = new XML(""); 660 | var xmlCopy = new XML("what"); 661 | result = xml.insertChildBefore(null, "what"); 662 | Assert.isNotUndefined(result, "result should not be undefined"); 663 | Assert.areEqual(xmlCopy.toXMLString(), xml.toXMLString()); 664 | }, 665 | 666 | testInsertChildAfter: function(){ 667 | var child = new XML(""); 668 | var xml = new XML(""); 669 | var f1 = new XML(""); 670 | var f2 = new XML(""); 671 | var f3 = new XML(""); 672 | var result = xml.insertChildAfter(xml.child('c'), child); 673 | Assert.areSame(result, xml); 674 | Assert.areEqual(f1.toXMLString(), result.toXMLString()); 675 | 676 | xml = new XML(""); 677 | result = xml.insertChildAfter(xml.child('d'), child); 678 | Assert.areSame(result, xml); 679 | Assert.areEqual(f2.toXMLString(), result.toXMLString()); 680 | 681 | xml = new XML(""); 682 | child = new XML(""); 683 | var f4 = new XML(""); 684 | result = xml.insertChildAfter(xml.child('c'), child.child('e')); 685 | Assert.isNotUndefined(result); 686 | Assert.areEqual(f4.toXMLString(), result.toXMLString()); 687 | 688 | xml = new XML(""); 689 | child = new XML(""); 690 | result = xml.insertChildAfter(null, child); 691 | Assert.areSame(result, xml); 692 | Assert.areEqual(f3.toXMLString(), result.toXMLString()); 693 | 694 | xml = new XML(""); 695 | var xmlCopy = new XML("what"); 696 | result = xml.insertChildAfter(null, "what"); 697 | Assert.isNotUndefined(result, "result should not be undefined"); 698 | Assert.areEqual(xmlCopy.toXMLString(), xml.toXMLString()) 699 | }, 700 | testRemove: function(){ 701 | var xml = new XML(""); 702 | xml.child('b').remove(); 703 | Assert.areEqual(xml.toXMLString(), new XML("").toXMLString()); 704 | 705 | xml = new XML(""); 706 | xml.attribute('*').remove(); 707 | Assert.areEqual(xml.toXMLString(), new XML("").toXMLString()); 708 | }, 709 | testParent: function(){ 710 | var xml = new XML(""); 711 | var child = xml.child('b'); 712 | Assert.areEqual(xml.toXMLString(), child.parent().toXMLString()); 713 | 714 | xml = new XML(""); 715 | var child1 = xml.child('b'); 716 | var child2 = child1.child('c'); 717 | Assert.areEqual(child1.toXMLString(), child2.parent().toXMLString()); 718 | Assert.areEqual(xml.toXMLString(), child1.parent().toXMLString()) 719 | }, 720 | testText: function(){ 721 | var xml = new XML("123"); 722 | Assert.areEqual("13", xml.text().toString()); 723 | }, 724 | testSetChildren: function(){ 725 | var xml = new XML("ab"); 726 | xml.setChildren("new"); 727 | Assert.areEqual("new", xml.toXMLString()); 728 | 729 | xml.setChildren(new XML("b")); 730 | Assert.areEqual("\n b\n", xml.toString()); 731 | 732 | var list = (new XML("12")).child("b"); 733 | xml.setChildren(list); 734 | Assert.areEqual(2, list.length()); 735 | Assert.areEqual("\n 1\n 2\n", xml.toString()); 736 | }, 737 | testReplace: function(){ 738 | var xml = new XML("beforebafterb"); 739 | xml.replace("b", "__replace__"); 740 | var compareXML = new XML("before__replace__after"); 741 | Assert.areEqual(compareXML.toXMLString(), xml.toXMLString()); 742 | 743 | }, 744 | testElements: function(){ 745 | var xml = new XML("abc"); 746 | Assert.areEqual(2, xml.elements().length()); 747 | Assert.areEqual(1, xml.elements('c').length()); 748 | Assert.areEqual(0, xml.elements('d').length()); 749 | }, 750 | testDescendents: function(){ 751 | var xml =new XML( "b23" ); 752 | 753 | var p = xml.child("null"); 754 | p._addXML(new XML("b2")); 755 | p._addXML(new XML("2")); 756 | p._addXML(new XML("3")); 757 | Assert.areEqual(p.toXMLString(), xml.descendants("b").toXMLString(), "b descendents shoud be equal:"); 758 | 759 | p = xml.child("null"); 760 | p._addXML(xml.child("b")); 761 | p._addXML(xml.child("b").text()); 762 | p._addXML(xml.child("b").child("b")); 763 | p._addXML(xml.child("b").child("b").text()); 764 | p._addXML(xml.child("c")); 765 | p._addXML(xml.child("c").child("b")); 766 | p._addXML(xml.child("c").child("b").text()); 767 | Assert.areEqual(p.toXMLString(), xml.descendants().toXMLString(), "* descendants should be equal"); 768 | }, 769 | testAttribute: function(){ 770 | var xml = new XML(""); 771 | var attr = xml.attribute('b'); 772 | Assert.areEqual(NodeKind.ATTRIBUTE, attr.nodeKind()); 773 | Assert.areEqual('1', attr._text, 'attr._text should be 1'); 774 | Assert.areEqual('1', attr.toString(), 'toString value should be 1'); 775 | Assert.areEqual('1', attr.toXMLString()); 776 | attr = xml.attribute('c'); 777 | Assert.areEqual('2', attr.toString()); 778 | Assert.areEqual('2', attr.toXMLString()); 779 | attr = xml.attribute('d.f'); 780 | Assert.areEqual('3', attr.toString()); 781 | Assert.areEqual('3', attr.toXMLString()); 782 | }, 783 | testAttributes: function(){ 784 | var xml = new XML(""); 785 | var attrs = xml.attributes(); 786 | Assert.areEqual(XMLList, attrs.constructor); 787 | Assert.areEqual('12', attrs.toString()); 788 | Assert.areEqual('1\n2', attrs.toXMLString()); 789 | Assert.areEqual(2, attrs.length()); 790 | Assert.areEqual(0, attrs.children().length()); 791 | }, 792 | testComments: function(){ 793 | XML.ignoreComments = false; 794 | var xml = new XML(""); 795 | var s = xml.comments(); 796 | XML.ignoreComments = true; 797 | 798 | Assert.areEqual(XMLList, s.constructor); 799 | Assert.areEqual(2, s.length()); 800 | 801 | }, 802 | testHasSimpleContent: function(){ 803 | var xml = new XML("hello, jsxml"); 804 | Assert.isTrue(xml.hasSimpleContent()); 805 | Assert.isFalse(xml.hasComplexContent()); 806 | 807 | xml = new XML("hello, jsxml"); 808 | Assert.isTrue(xml.hasSimpleContent()); 809 | Assert.isFalse(xml.hasComplexContent()); 810 | 811 | xml = new XML(""); 812 | Assert.isFalse(xml.hasSimpleContent()); 813 | Assert.isTrue(xml.hasComplexContent()); 814 | 815 | xml = new XML("hello, jsxml"); 816 | Assert.isFalse(xml.hasSimpleContent()); 817 | Assert.isTrue(xml.hasComplexContent()); 818 | 819 | }, 820 | testToXMLString: function(){ 821 | XML.ignoreComments = true; 822 | XML.ignoreProcessingInstructions = true; 823 | 824 | var xml = new XML("hello, jsxml"); 825 | Assert.areEqual("hello, jsxml", xml.toXMLString()); 826 | 827 | xml = new XML("hello, jsxml"); 828 | Assert.areEqual("\n hello, jsxml\n", xml.toXMLString()); 829 | 830 | xml = new XML("hello, jsxml"); 831 | Assert.areEqual("\n hello, jsxml\n", xml.toXMLString()); 832 | 833 | XML.prettyPrinting = false; 834 | var xmlstr = xml.toXMLString(); 835 | XML.prettyPrinting = true; 836 | Assert.areEqual("hello, jsxml", xmlstr); 837 | Assert.areEqual("\n hello, jsxml\n", xml.toXMLString()); 838 | 839 | 840 | xml = new XML("hello, jsxml"); 841 | Assert.areEqual("\n hello, jsxml\n", xml.toXMLString()); 842 | 843 | 844 | XML.ignoreComments = false; 845 | xml = new XML("hello, jsxml"); 846 | Assert.areEqual("\n \n hello, jsxml\n \n \n", xml.toXMLString()); 847 | XML.ignoreComments = true; 848 | 849 | XML.ignoreProcessingInstructions = false; 850 | xml = new XML("hello, jsxml"); 851 | Assert.areEqual("\n \n hello, jsxml\n \n \n", xml.toXMLString()); 852 | XML.ignoreProcessingInstructions = true; 853 | 854 | xml = new XML(""); 855 | xml.appendChild("hello"); 856 | Assert.areEqual(xml._children.length, 1); 857 | Assert.areEqual('hello', xml.toXMLString()); 858 | xml.appendChild(",world"); 859 | Assert.areEqual(xml._children.length, 2); 860 | Assert.areEqual(xml.toXMLString(), '\n hello\n ,world\n'); 861 | 862 | xml = new XML("has attribute"); 863 | Assert.areEqual("a", xml.localName()); 864 | Assert.areEqual("uri:a", xml.name()); 865 | Assert.areEqual("has attribute", xml.toXMLString()); 866 | 867 | }, 868 | testToXMLStringWithIgnoreWhitespace: function(){ 869 | 870 | var originalContent = 871 | '\n' + 872 | ' value\n' + 873 | ''; 874 | 875 | XML.setSettings({ignoreWhitespace: false}); 876 | var xml = new XML(originalContent); 877 | var content = xml.toString(); 878 | XML.setSettings({ignoreWhitespace: true}); 879 | Assert.isFalse(content === originalContent); 880 | 881 | 882 | var xml2 = new XML(originalContent); 883 | var content2 = xml2.toString(); 884 | Assert.areEqual(originalContent, content2); 885 | 886 | 887 | 888 | }, 889 | testToString: function(){ 890 | var xml = new XML("hello"); 891 | Assert.areEqual("hello", xml.toString()); 892 | 893 | xml.appendChild(",jsxml"); 894 | Assert.areEqual("hello,jsxml", xml.toString()); 895 | 896 | xml = new XML("hello, jsxml"); 897 | Assert.areEqual("\n hello, jsxml\n", xml.toString()); 898 | 899 | XML.prettyPrinting = false; 900 | var xmlstr = xml.toString(); 901 | XML.prettyPrinting = true; 902 | Assert.areEqual("hello, jsxml", xmlstr); 903 | Assert.areEqual("\n hello, jsxml\n", xml.toString()); 904 | 905 | }, 906 | testCopy: function(){ 907 | var xml = new XML(''); 908 | var xmlCopy = xml.copy(); 909 | Assert.areEqual(xml.toString(), xmlCopy.toString(), 'xmlCopy should has a same toString() value'); 910 | Assert.areEqual(xml.toXMLString(), xmlCopy.toXMLString(), 'xmlCopy should has a same toXMLString() value'); 911 | }, 912 | 913 | testExample: function(){ 914 | var xml = new XML(example); 915 | Assert.areEqual("a", xml.child('a').text().getValue()); 916 | Assert.areEqual("e", xml.child('a').child('e').getValue()); 917 | Assert.areEqual("b", xml.child('b').getValue()); 918 | Assert.areEqual("1", xml.child('c').attribute('att').getValue()); 919 | Assert.areEqual("inner tag: @#?&<>;'

", xml.child('p').getValue()); 920 | 921 | xml.child('p').setChildren('

'); 922 | Assert.isTrue(xml.child('p').text()._useCDATA, 'should auto use CDATA tag for special characters'); 923 | xml.child('p').setChildren('ppppppp'); 924 | Assert.isFalse(xml.child('p').text()._useCDATA, 'should not use CDATA tag for normal characters'); 925 | }, 926 | 927 | testSetSettings: function(){ 928 | 929 | var sett = XML.settings(); 930 | Assert.isTrue(sett.ignoreComments); 931 | Assert.isTrue(sett.ignoreProcessingInstructions); 932 | Assert.isTrue(sett.ignoreWhitespace); 933 | Assert.areEqual(2, sett.prettyIndent); 934 | Assert.isTrue(sett.prettyPrinting); 935 | 936 | sett.prettyIndex = 4; 937 | Assert.areEqual(2, XML.settings().prettyIndent); 938 | 939 | XML.setSettings({ 940 | ignoreComments: false, 941 | ignoreProcessingInstructions: false, 942 | ignoreWhitespace: false, 943 | prettyIndent: 4 944 | }); 945 | 946 | Assert.isFalse(XML.ignoreComments); 947 | Assert.isFalse(XML.ignoreProcessingInstructions); 948 | Assert.isFalse(XML.ignoreWhitespace); 949 | Assert.areEqual(4, XML.prettyIndent); 950 | Assert.isTrue(XML.prettyPrinting); 951 | 952 | XML.ignoreComments = true; 953 | XML.ignoreProcessingInstructions = true; 954 | XML.ignoreWhitespace = true; 955 | XML.prettyIndent = 2; 956 | XML.prettyPrinting = true; 957 | }, 958 | testIgnoreComments: function(){ 959 | Assert.isTrue(XML.ignoreComments); 960 | }, 961 | testIgnoreProcessingInstructions: function(){ 962 | Assert.isTrue(XML.ignoreProcessingInstructions); 963 | }, 964 | testIgnoreWhitespace: function(){ 965 | Assert.isTrue(XML.ignoreWhitespace); 966 | }, 967 | testPrettyIndent: function(){ 968 | Assert.areEqual(2, XML.prettyIndent); 969 | }, 970 | testPrettyPrinting: function(){ 971 | Assert.isTrue(XML.prettyPrinting); 972 | }, 973 | //extension for javascript 974 | testGetValue: function(){ 975 | var xml = new XML("xml"); 976 | var val = xml.getValue(); 977 | Assert.areEqual('xml', val); 978 | }, 979 | testSetValue: function(){ 980 | var xml = new XML("xml"); 981 | xml.setValue("new Value"); 982 | Assert.areEqual("new Value", xml.getValue()); 983 | }, 984 | testEach: function(){ 985 | var xml = new XML("xml"); 986 | var count = 0; 987 | xml.each(function(item, index, host){ 988 | count++; 989 | Assert.areSame(xml, item); 990 | Assert.areEqual(xml, host); 991 | Assert.areEqual(0, index); 992 | }); 993 | Assert.areEqual(1, count); 994 | }, 995 | testCreateMainDocumentAndExport: function(){ 996 | jsxml.XML.setSettings({ignoreComments : false, ignoreProcessingInstructions : false, createMainDocument: true}); 997 | var xml = new XML("xml\n"); 998 | xml.child('a').setValue("new Value"); 999 | Assert.areEqual("new Value", xml.child('a').getValue()); 1000 | Assert.areEqual("new Value\n", xml.toXMLString()); 1001 | 1002 | xml = new XML("xml\n"); 1003 | xml.child('b').child('a').setValue("new Value"); 1004 | Assert.areEqual("new Value", xml.child('b').child('a').getValue()); 1005 | Assert.areEqual("\n new Value\n\n", xml.toXMLString()); 1006 | 1007 | xml = new XML("xml\n"); 1008 | xml.child('b').child('a').setValue("new Value"); 1009 | Assert.areEqual("new Value", xml.child('b').child('a').getValue()); 1010 | Assert.areEqual("\n\n\n new Value\n\n", xml.toXMLString()); 1011 | 1012 | xml = new XML("xml\n"); 1013 | xml.child('b').child('a').setValue("new Value"); 1014 | Assert.areEqual("new Value", xml.child('b').child('a').getValue()); 1015 | Assert.areEqual("\n\n\n\n new Value\n\n", xml.toXMLString()); 1016 | 1017 | jsxml.XML.setSettings({ignoreComments : true, ignoreProcessingInstructions : true, createMainDocument: false}); 1018 | }, 1019 | testA: function() { 1020 | var s = '' 1021 | var xml = new XML(s) 1022 | } 1023 | }); 1024 | 1025 | //Runner 1026 | Y.Test.Runner.add(qnameTestCase); 1027 | Y.Test.Runner.add(nodekindTestCase); 1028 | Y.Test.Runner.add(namespaceTestCase); 1029 | Y.Test.Runner.add(xmllistTestCase); 1030 | Y.Test.Runner.add(xmlTestCase); 1031 | Y.Test.Runner.run(); 1032 | 1033 | }); --------------------------------------------------------------------------------