1 /*
  2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5 
  6 /**
  7  * @fileOverview Defines the {@link CKEDITOR.xml} class, which represents a
  8  *		loaded XML document.
  9  */
 10 
 11 (function()
 12 {
 13 	CKEDITOR.plugins.add( 'xml', {});
 14 
 15 	/**
 16 	 * Represents a loaded XML document.
 17 	 * @constructor
 18 	 * @param {object|string} xmlObjectOrData A native XML (DOM document) object or
 19 	 *		a string containing the XML definition to be loaded.
 20 	 * @example
 21 	 * var xml = <b>new CKEDITOR.xml( '<books><book title="My Book" /></books>' )</b>;
 22 	 */
 23 	CKEDITOR.xml = function( xmlObjectOrData )
 24 	{
 25 		var baseXml = null;
 26 
 27 		if ( typeof xmlObjectOrData == 'object' )
 28 			baseXml = xmlObjectOrData;
 29 		else
 30 		{
 31 			var data = ( xmlObjectOrData || '' ).replace( / /g, '\xA0' );
 32 			if ( window.DOMParser )
 33 				baseXml = (new DOMParser()).parseFromString( data, 'text/xml' );
 34 			else if ( window.ActiveXObject )
 35 			{
 36 				try { baseXml = new ActiveXObject( 'MSXML2.DOMDocument' ); }
 37 				catch(e)
 38 				{
 39 					try { baseXml = new ActiveXObject( 'Microsoft.XmlDom' ); } catch(e) {}
 40 				}
 41 
 42 				if ( baseXml )
 43 				{
 44 					baseXml.async = false;
 45 					baseXml.resolveExternals = false;
 46 					baseXml.validateOnParse = false;
 47 					baseXml.loadXML( data );
 48 				}
 49 			}
 50 		}
 51 
 52 		/**
 53 		 * The native XML (DOM document) used by the class instance.
 54 		 * @type object
 55 		 * @example
 56 		 */
 57 		this.baseXml = baseXml;
 58 	};
 59 
 60 	CKEDITOR.xml.prototype =
 61 	{
 62 		/**
 63 		 * Get a single node from the XML document, based on a XPath query.
 64 		 * @param {String} xpath The XPath query to execute.
 65 		 * @param {Object} [contextNode] The XML DOM node to be used as the context
 66 		 *		for the XPath query. The document root is used by default.
 67 		 * @returns {Object} A XML node element or null if the query has no results.
 68 		 * @example
 69 		 * // Create the XML instance.
 70 		 * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
 71 		 * // Get the first <item> node.
 72 		 * var itemNode = <b>xml.selectSingleNode( 'list/item' )</b>;
 73 		 * // Alert "item".
 74 		 * alert( itemNode.nodeName );
 75 		 */
 76 		selectSingleNode : function( xpath, contextNode )
 77 		{
 78 			var baseXml = this.baseXml;
 79 
 80 			if ( contextNode || ( contextNode = baseXml ) )
 81 			{
 82 				if ( CKEDITOR.env.ie || contextNode.selectSingleNode )	// IE
 83 					return contextNode.selectSingleNode( xpath );
 84 				else if ( baseXml.evaluate )							// Others
 85 				{
 86 					var result = baseXml.evaluate( xpath, contextNode, null, 9, null);
 87 					return ( result && result.singleNodeValue ) || null;
 88 				}
 89 			}
 90 
 91 			return null;
 92 		},
 93 
 94 		/**
 95 		 * Gets a list node from the XML document, based on a XPath query.
 96 		 * @param {String} xpath The XPath query to execute.
 97 		 * @param {Object} [contextNode] The XML DOM node to be used as the context
 98 		 *		for the XPath query. The document root is used by default.
 99 		 * @returns {ArrayLike} An array containing all matched nodes. The array will
100 		 *		be empty if the query has no results.
101 		 * @example
102 		 * // Create the XML instance.
103 		 * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
104 		 * // Get the first <item> node.
105 		 * var itemNodes = xml.selectSingleNode( 'list/item' );
106 		 * // Alert "item" twice, one for each <item>.
107 		 * for ( var i = 0 ; i < itemNodes.length ; i++ )
108 		 *     alert( itemNodes[i].nodeName );
109 		 */
110 		selectNodes : function( xpath, contextNode )
111 		{
112 			var baseXml = this.baseXml,
113 				nodes = [];
114 
115 			if ( contextNode || ( contextNode = baseXml ) )
116 			{
117 				if ( CKEDITOR.env.ie || contextNode.selectNodes )		// IE
118 					return contextNode.selectNodes( xpath );
119 				else if ( baseXml.evaluate )							// Others
120 				{
121 					var result = baseXml.evaluate( xpath, contextNode, null, 5, null);
122 
123 					if ( result )
124 					{
125 						var node;
126 						while ( ( node = result.iterateNext() ) )
127 							nodes.push( node );
128 					}
129 				}
130 			}
131 
132 			return nodes;
133 		},
134 
135 		/**
136 		 * Gets the string representation of hte inner contents of a XML node,
137 		 * based on a XPath query.
138 		 * @param {String} xpath The XPath query to execute.
139 		 * @param {Object} [contextNode] The XML DOM node to be used as the context
140 		 *		for the XPath query. The document root is used by default.
141 		 * @returns {String} The textual representation of the inner contents of
142 		 *		the node or null if the query has no results.
143 		 * @example
144 		 * // Create the XML instance.
145 		 * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
146 		 * // Alert "<item id="test1" /><item id="test2" />".
147 		 * alert( xml.getInnerXml( 'list' ) );
148 		 */
149 		getInnerXml : function( xpath, contextNode )
150 		{
151 			var node = this.selectSingleNode( xpath, contextNode ),
152 				xml = [];
153 			if ( node )
154 			{
155 				node = node.firstChild;
156 				while ( node )
157 				{
158 					if ( node.xml )				// IE
159 						xml.push( node.xml );
160 					else if ( window.XMLSerializer )	// Others
161 						xml.push( ( new XMLSerializer() ).serializeToString( node ) );
162 
163 					node = node.nextSibling;
164 				}
165 			}
166 
167 			return xml.length ? xml.join( '' ) : null;
168 		}
169 	};
170 })();
171