├── .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 |

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("") === 0) {
144 |
145 | //Instruction
146 | index = 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("") === 0) {
185 |
186 | // end tag
187 | match = xml.match(endTag);
188 |
189 | if (match) {
190 | xml = xml.substring(match[0].length);
191 | match[0].replace(endTag, parseEndTag);
192 | chars = false;
193 | } else {
194 | throw new Error('[XML Parse Error] the end tag is invalid: ' + xml);
195 | }
196 | } else if (xml.indexOf("<") === 0) {
197 |
198 | // start tag
199 | match = xml.match(startTag);
200 | if (match) {
201 | xml = xml.substring(match[0].length);
202 | match[0].replace(startTag, parseStartTag);
203 | chars = false;
204 | } else {
205 | throw new Error('[XML Parse Error] the start tag is invalid: ' + xml.substring(0,100));
206 | }
207 | }
208 |
209 | if (chars) {
210 | index = xml.indexOf("<");
211 |
212 | var text = index < 0 ? xml : xml.substring(0, index);
213 | xml = index < 0 ? "" : xml.substring(index);
214 |
215 | if (handler.chars)
216 | handler.chars(text);
217 | }
218 |
219 |
220 | if (xml === last)
221 | throw new Error("[XML Parse Error] " + xml)
222 | last = xml;
223 | }
224 |
225 | // Check the tag closed
226 | if (stack.length && !XML.htmlMode) {
227 | throw new Error("[XML Parse Error] There is tag not closed " + stack.last());
228 | }
229 |
230 | function parseStartTag(tag, tagName, rest, unary) {
231 |
232 | unary = !! unary;
233 |
234 | if (!unary)
235 | stack.push(tagName);
236 |
237 | if (handler.start) {
238 | var attrs = [];
239 |
240 | rest.replace(attr, function(match, name) {
241 | var value = arguments[2] ? arguments[2] :
242 | arguments[3] ? arguments[3] :
243 | arguments[4] ? arguments[4] : "";
244 |
245 | attrs.push({
246 | name: name,
247 | value: value,
248 | escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
249 | });
250 | });
251 |
252 | if (handler.start)
253 | handler.start(tagName, attrs, unary);
254 | }
255 | }
256 |
257 | function parseEndTag(tag, tagName) {
258 | if (!tagName) {
259 | stack.length = 0;
260 | return;
261 | }
262 | if (!stack.length && !XML.htmlMode) {
263 | throw new Error("[XML Parse Error] Cannot find the start tag for the end tag " + tagName);
264 | }
265 | var startTag = stack.last();
266 | if (startTag !== tagName && !XML.htmlMode) {
267 | throw new Error("[XML Parse Error] End tag " + tagName + " is not match the start tag " + startTag);
268 | }
269 | stack.length = stack.length - 1;
270 | if (handler.end) {
271 | handler.end(startTag);
272 | }
273 | return;
274 | }
275 | };
276 |
277 | /**
278 | * @description Namespace class
279 | * @access public
280 | * @param prefix{String} the namespace prefix
281 | * @param uri{String} the namespace uri.
282 | */
283 | Namespace = function(prefix, uri) {
284 | var len = arguments.length;
285 | if (len >= 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 + "" + this._text + "?>";
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 += "" + tag + ">";
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 += "" + tag + ">";
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 | + " instruction ?>"
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("0 ?>11 ?>22 ?>3 ?>4 ?>");
240 | var pi = xml.child('b').processingInstructions();
241 | XML.ignoreProcessingInstructions = true;
242 | Assert.areEqual("1 ?>\n2 ?>", 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 | });
--------------------------------------------------------------------------------