1  
     2  /**
     3   * com.sekati.data.XMLSA
     4   * @version 0.1.1
     5   * @author jason m horwitz | sekati.com
     6   * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
     7   * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
     8   * 
     9   * Source from ca.nectere.data.XMLSA - XMLSA 0.1.0  -  2006-07-10
    10   * based on XMLSA 1.4 by Max Ziebell @ http://proto.layer51.com/d.aspx?f=957
    11   */
    12   
    13  /**
    14   * XMLSA converts xml to simple object array
    15   * 
    16   * {@code Usage:
    17   *	_global.$c.onLoad = function(ok){
    18   *		if (ok) loadLanguage();
    19   *	};
    20   * -or-
    21   * 	_global.$c.load(fv_configFile);
    22   * 	onEnterFrame = function(){
    23   * 		trace("config : " + _global.$c.getPercent() );
    24   * 		if (_global.$c.getPercent() == 100){
    25   * 			delete onEnterFrame;
    26   * 			loadLanguage();
    27   * 		}
    28   * 	};
    29   * }
    30   */
    31  class com.sekati.data.XMLSA {	
    32  
    33  	private var xmlobj:Object;
    34  	private var $xml:Object;
    35  	private var $root:Object;
    36  	private var $parent:Object;
    37  	private var $version:String;
    38  	private var attributes:Object;
    39  	private var _noCache:Boolean = false;
    40  
    41  	// Constructor
    42  	public function XMLSA(watchXML:Object) {
    43  		
    44  		this.xmlobj = watchXML;
    45  		
    46  		this.$xml = new Object( );
    47  		this.$root = new Object( );
    48  		this.$parent = new Object( );
    49  		
    50  		_global.ASSetPropFlags( this, null, 1, 1 );
    51  		if (watchXML != undefined) {
    52  			this._parse.apply( this, arguments );
    53  		}
    54  		;
    55  		
    56  		// hidde all functions to for..in loops
    57  		_global.ASSetPropFlags( XMLSA.prototype, null, 1, 1 );
    58  	}
    59  
    60  	// load
    61  	public function load():Void {
    62  		this._cleanup( );
    63  		var loader:Object = this._makeLoader( this );
    64  		
    65  		arguments[0] = this._makeURL( arguments[0] );
    66  		loader.load.apply( loader, arguments );
    67  		
    68  		//keep ref
    69  		xmlobj = loader;
    70  	}
    71  
    72  	// send
    73  	public function send():Void {
    74  		this._cleanup( );
    75  		arguments[0] = this._makeURL( arguments[0] );
    76  		if(arguments.length == 2) {
    77  			this.$root.send.apply( this.$root, arguments );
    78  		} else {
    79  			this.$root.sendAndLoad.apply( this.$root, arguments[0], new XML( ) );
    80  		}
    81  	}
    82  
    83  	// toString
    84  	public function toString():String {
    85  		return this.$xml.toString( );
    86  	}
    87  
    88  	// sendAndLoad
    89  	public function sendAndLoad(host:String, target:Object, method:Function):Void {
    90  		this._cleanup( );
    91  		var loader:Object = this._makeLoader( target );
    92  		this.$root.sendAndLoad( this._makeURL( host ), loader, method );
    93  	}
    94  
    95  	// search
    96  	public function search(criteria:Object, recursive:Object):Object {
    97  		XMLNode.prototype.$criteria = criteria;
    98  		arguments.shift( );
    99  		var result:Object = this._search.apply( this, arguments );
   100  		delete (XMLNode.prototype.$criteria);
   101  		return result;
   102  	}
   103  
   104  	//return a reference to XML
   105  	public function getXML():Object {
   106  		return  this.$xml;
   107  	}
   108  
   109  	// return the value of the firstChild if its a textElement
   110  	public function getValue():Object {
   111  		return  (this.$xml.firstChild.nodeType == 3 ? this.$xml.firstChild.nodeValue : undefined);
   112  	}
   113  
   114  	// set a textNode
   115  	public function setValue( text:Object ):Boolean {
   116  		// check if the node has a textElement?
   117  		if (this.$xml.firstChild.nodeType == undefined) {
   118  			// seems like we have to create one...
   119  			var tmp_xml:Object = new XML( );
   120  			this.$xml.appendChild( tmp_xml.createTextNode( text ) );
   121  			return true;
   122  		// else check if firstChild is a textElement and set it...
   123  		}else if (this.$xml.firstChild.nodeType == 3) {
   124  			this.$xml.firstChild.nodeValue = text;
   125  			return true;  // retrun success on setValue
   126  		// seams like it ain't possible
   127  		} else {
   128  			return false; // retrun failed on setValue
   129  		}
   130  	}
   131  
   132  	//return the nodeName
   133  	public function getNodeName():Object {
   134  		return  this.$xml.nodeName;
   135  	}
   136  
   137  	// append a child
   138  	public function appendChild(element:Object):Void {
   139  		if (element instanceof XML) {
   140  			element = element.firstChild;
   141  		}
   142  		this.$xml.appendChild( element );
   143  		//XML
   144  		this._reParse( );//XMLSA
   145  	}
   146  
   147  	// return a XML object with cloneNode
   148  	public function cloneNode(rekursiv:Object):Object {
   149  		return this.$xml.cloneNode( rekursiv );//XML
   150  	}
   151  
   152  	// this one I added as it's sometimes simpler just want
   153  	// to add a new member...
   154  	public function appendElement(name:Object,value:Object,attribs:Object):Void {
   155  		var temp:Object = new XML( );
   156  		this.$xml.appendChild( temp.createElement( name ) );
   157  		if (value != null) {
   158  			this.$xml.lastChild.appendChild( temp.createTextNode( value ) );
   159  		}
   160  		if (typeof(attribs) == "object" ) {
   161  			// there is a bug in Flash MX so we got to do this...
   162  			// a direct assignment would add __proto__ and constructor
   163  			// to the attributes...
   164  			for (var key in attribs ) {
   165  				this.$xml.lastChild.attributes[key] = attribs[key];
   166  			}
   167  		}
   168  		this._reParse( );//XMLSA
   169  	}
   170  
   171  	// remove child by index
   172  	public function removeChildByIndex(name:Object,idx:Object):Void {
   173  		this[name][idx].$xml.removeNode( );
   174  		this[name].splice( idx, 1 );
   175  	}
   176  
   177  	// remove child by index
   178  	public function removeNode():Void {
   179  		this.$xml.removeNode( );
   180  		this.$parent._reParse( );
   181  	}
   182  
   183  	// insert before
   184  	public function insertBefore(node:Object):Void {
   185  		this.$parent.$xml.insertBefore( node, this.$xml );
   186  		this.$parent._reParse( );
   187  	}
   188  
   189  	//==================================================================
   190  	//added 2006-06-05 by nectere
   191  	public function getPercent():Number {
   192  		var total:Number = xmlobj.getBytesTotal( );
   193  		if (total == null) return 0;
   194  		return Math.round( xmlobj.getBytesLoaded( ) / total * 100 );
   195  	}
   196  
   197  	//==================================================================
   198  	//added 2006-06-08 by nectere
   199  	public function $(attName:Object, attVal:Object):Object {
   200  		//find by attributeIndex!
   201  		
   202  		//ex;    _c.call.index("id", "getPatchesByID").attributes.group;
   203  		//ex;    _c.call.index("id", "getPatchesByID");
   204  		//ex;    _c.call.$("id", name).end.$("id", "jwt_dev").getValue();
   205  		
   206  		//the attributeName used as index should be unique inside that node
   207  		
   208  		//this should reference where we are right now in the structure.
   209  		//So this can be called from any node
   210  		for (var i:Object in this) {
   211  			if (this[i].attributes[attName] == attVal) {
   212  				return this[i];
   213  				break;
   214  			}
   215  		}
   216  		
   217  		//not found
   218  		return false;
   219  	}
   220  
   221  	//==================================================================
   222  	//mod 2006-06-08 by nectere
   223  	private function _makeURL(host:String):String {
   224  		//made the cache system as in option in the constructor
   225  		if (this._noCache && this._online( )) {
   226  			
   227  			var c:Number = (random( 100000 ) + 100000);
   228  			if (_global.sessionID != undefined) {
   229  				return host + "?sid=" + _global.sessionID + "&nocache=" + c;
   230  			} else {
   231  				return host + "?nocache=" + c;
   232  			}	
   233  		} else {
   234  			return host;
   235  		}
   236  	}
   237  
   238  	/*************************************************/
   239  	// Private
   240  	/*************************************************/
   241  	// Parser
   242  	// called by the constructor and by reParse
   243  	private function _parse(node:Object, parent:Object):Void {
   244  		this.$parent = parent;
   245  		// make shure we work with XMLNode
   246  		if (node instanceof XML) {
   247  			this.$version = "XMLSA 1.4";
   248  			this.$root = node;
   249  			node = node.firstChild;
   250  		} else {
   251  			this.$root = this.$parent.$root;
   252  		}
   253  		// store a reference to $xml
   254  		this.$xml = node;
   255  		this.attributes = node.attributes;
   256  		if (node.nodeType == 1 && node.firstChild.nodeType != 3) {
   257  			//if (node.nodeType == 1 and node.firstChild.nodeType != 3) {		
   258  			for (var childCounter:Number = 0; childCounter < node.childNodes.length ; childCounter++) {
   259  				var tempName:Object = node.childNodes[ childCounter ].nodeName;
   260  				if (this[ tempName ] == undefined) {
   261  					this[ tempName ] = new Array( );
   262  					this[ tempName ].__resolve = XMLSA.prototype.mixed__resolve;
   263  					_global.ASSetPropFlags( this[ tempName ], null, 1, 1 );
   264  				}
   265  				this[ tempName ].push( new XMLSA( node.childNodes[childCounter], xmlobj ) );
   266  			}
   267  		}
   268  	}
   269  
   270  	// reParse
   271  	// free a brach and reparse it
   272  	private function _reParse():Void {
   273  		this._cleanup( );
   274  		// parse it again...
   275  		this._parse( this.$xml, this.$parent );
   276  	}
   277  
   278  	private function _cleanup():Void {
   279  		// delete all
   280  		for (var found in this) {
   281  			if (found != "onLoad") {
   282  				trace( found );
   283  				delete (this[found]);
   284  			}
   285  		}
   286  	}
   287  
   288  	private function _online():Boolean {
   289  		// are we online?
   290  		return (_root._url.substr( 0, 7 ) == "http://");
   291  	}
   292  
   293  	// used by send, load and sendAndLoad
   294  	private function _makeLoader(target:Object):Object {
   295  		var loader:Object = new XML( );
   296  		loader.ignoreWhite = true;
   297  		loader.link = target;
   298  		loader.onLoad = function(ok:Boolean):Void {
   299  			if (ok) {
   300  				_global.ASSetPropFlags( this.link, [ "onLoad" ], 1, 1 );
   301  				this.link._cleanup( );
   302  				_global.ASSetPropFlags( this.link, [ "onLoad" ], 0, 1 );
   303  				this.link._parse( this );
   304  				this.link.onLoad( true );
   305  				// Experimental Session Support
   306  				//---------------------------------------------------
   307  				// use the attribute 'session' in the root tag to
   308  				// submit a sessionID if you transmit the word
   309  				// 'timeout' or 'end' the session gets deleted
   310  				var header:Object = this.link.attributes;
   311  				if (header.session != undefined) {
   312  					switch (header.session) {
   313  						case "timeout":
   314  						case "end":
   315  							if (_global.session != undefined) { 
   316  								delete(_global.session);
   317  								_global.onSessionEnd( header );
   318  							}
   319  							break;
   320  						default:
   321  							_global.session = new Object( );
   322  							_global.session.id = header.session;
   323  							_global.onSessionStart( header );
   324  							break;
   325  					}
   326  				}
   327  			} else {
   328  				this.link.onLoad( false );
   329  			}
   330  		};
   331  		return loader;
   332  	}
   333  
   334  	private function _search(recursive:Object):Array {
   335  		var result:Array = new Array( );
   336  		for (var found in this) {
   337  			for (var node in this[found]) {
   338  				if (this[found][node].$xml != undefined) {
   339  					if (this[found][node].$xml.$criteria( ))result.push( this[found][node] );
   340  					if (recursive) result = result.concat( this[found][node]._search.apply( this[found][node], arguments ) );
   341  				}
   342  			}
   343  		}
   344  		return result;
   345  	}
   346  
   347  	// new since 1.4 allows notations without a nodenumber because
   348  	// it defaults them to 0 in that case! (redirection)
   349  	private function mixed__resolve(found:Object):Object {
   350  		return this[0][found];
   351  	}
   352  }