/*
Script: xmodify.jquery.js
	Stitch - Markup language everyone can use.

License:
	Apache 2.0 License.

Stitch Copyright:
	copyright (c) 2007 Nexaweb Technologies, Inc., <http://www.nexaweb.com>

Stitch Credits:

Overview:
xModify is an extension of the dojo library that processes requests
and update the HTML DOM using the xModify syntax.  Included in xModify is
a series of instructions that can be used to update html.

- append
- add-class
- insert-after
- insert-at
- insert-before
- remove-attribute
- remove-class
- remove-element
- replace
- replace-children
- set-attribute
- set-innerhtml
- set-style


*/

/**
 * We need to add the stitch object to jQuery. This allows developers to 
 * use the jQuery.stitch object to execute the stitch functionality.
 * 
 * Developers can also use var stitch = new Stitch();
 * 
 */
var stitch = new Stitch();

/**
 * Set the stitch object as a property of the jQuery object
 */
jQuery.stitch = stitch;

jQuery.extend(stitch, {
    
	/*
	 * Function: extend
	 * 
	 * This function allows developers to create addition modules
	 * for the Stitch library.
	 */
	extend: function (){
		if (arguments.length == 1)
			return jQuery.extend(stitch, arguments[0]);	
		else 
			return jQuery.extend(arguments[0], arguments[1]);
	},
	
	/*
	 * Function: loadMarkup
	 * 
     * Main function to load a markup file from the server.
     * The results of the request will be automatically
     * processed.
     * 
     * Look at the documenation for the jQuery.ajax function
     * for information on the parameters.
     *
     * http://docs.jquery.com/Ajax
     *
     */
    loadMarkup: function(ajaxProperties){
        var complete = ajaxProperties.complete, thisVar = this;
        ajaxProperties.complete = function(result, request){
            if (result.responseXML == null) {
				if (ajaxProperties.error)
					ajaxProperties.error.call(this, result, request);
			}else {
				thisVar._processDoc(result.responseXML);
				if (complete) 
					complete.call(this, result, request);
			}
        }
        return jQuery.ajax(ajaxProperties);
    },
	
	/*
	 * Function: blendHtml
	 * 
	 * This method provides a shortcut for blending html into the 
	 * current html application. Check the jQuery documentation 
	 * specifics on the format and functionality of jQuery's selectors
	 * 
     * Return value:
     * none
     * 
	 * Parameters:
	 * 	requestProperties - See request properties for the xModify function.
	 * 	select - Location in the HTML dom that the resulting value of the
	 * 	request will be placed.
	 * 	blendMode - Mode can be any of the following append, replace, replace-children
	 * 	insert-after, insert-before
	 */
	blendHtml: function(ajaxProperties, select, blendMode){
        var complete = ajaxProperties.complete, variables = {}, thisVar = this;
        ajaxProperties.complete = function(result, request){
            if (result.responseText == null)
                ajaxProperties.error.call(this, result, request);
            else{
				var node = document.createElement(blendMode);
				node.setAttribute("select", select);
				node.appendChild(document.createTextNode(result.responseText));
                thisVar._xModifyProcessElement(node, variables);
                if (complete)
                    complete.call(this, result, request);               
            }
        }
        return jQuery.ajax(ajaxProperties);
	},
        
	
	//PRIVATE METHODS AND VARIABLES
			
    /**
     * Will return the modification element.
     */
    _xModifyProcessDoc: function(modElement){
		var variables = {},
			arry = modElement.childNodes, 
			len = arry.length;
	    for (var i = 0; i < len; i++)
			if (arry[i].nodeType == 1)
				this._xModifyProcessElement(arry[i], variables);           
    },

    /**
     * Will return the modification element.
     *
     * Assumes the modifications elements are under XAL
     *
     * MAIN ELEMENT NAMES     
     * insert-after         //This one is roughed out.
     * insert-before        //This one is roughed out.
     * append               //This one is roughed out.
     * set-attribute        //This one is roughed out.
     * set-style            //This one is roughed out.
     * add-class            //This one is roughed out.
     * remove-class         //This one is roughed out.
     * set-innerhtml        //This one is roughed out.
     * replace-children     //This one is roughed out.
     * remove-element       //This one is roughed out.
     * remove-attribute     //This one is roughed out.
     * replace              //This one is roughed out. 
     * 
     * insert-at            //This one is roughed out.  
     *   
     * clone                //This one is roughed out.  
     * create-document      //NOT IMPLEMENTED
     * 
     * variable             //This one is roughed out.
     * attribute            //This one is roughed out.       
     * value-of             //This one is roughed out.
     *
     * TODOs
     *
     * Look into events and styles. Events and styles work
     * as long as the programmer types them in correctly
     *
     * Need to add the must exist attribute
     * Need to add events.
     */
    _xModifyProcessElement: function(modElement, variables, select){
       	var thisVar = this, nodeName = thisVar._getNodeName(modElement.nodeName);
	   	if ((typeof select) == "undefined")
			select = modElement.getAttribute("select");
			
        if (nodeName == "append")
            thisVar._xProcIn(modElement.childNodes, function(nodeValue){    
                thisVar._xSelect(select).append(nodeValue);        
			}, variables);
        else if (nodeName == "set-attribute")
            jQuery.each(modElement.childNodes, function(){
            	if (this.nodeType == 1)
                    thisVar._xSelect(select).attr(this.getAttribute("name"), this.getAttribute("value"));
            });
        else if (nodeName == "set-style")
            jQuery.each(modElement.childNodes, function(){
            	if (this.nodeType == 1)
                    thisVar._xSelect(select).css(this.getAttribute("name"), this.getAttribute("value"));
            });
        else if (nodeName == "add-class")
            thisVar._xSelect(select).each(function(){
	            jQuery.className.add(this, modElement.getAttribute("name"));
			});
        else if (nodeName == "remove-class")
            thisVar._xSelect(select).each(function(){
	            jQuery.className.remove(this, modElement.getAttribute("name"));
			});
        else if (nodeName == "insert-before")
            thisVar._xProcIn(modElement.childNodes, function(nodeValue){
                thisVar._xSelect(select).before(nodeValue);        
            }, variables);
        else if (nodeName == "insert-after")
            thisVar._xProcIn(modElement.childNodes, function(nodeValue){
                thisVar._xSelect(select).after(nodeValue);        
            }, variables);
        else if (nodeName == "replace-children" ||
            nodeName == "set-innerhtml"){
            thisVar._xSelect(select).empty();        
           	thisVar._xProcIn(modElement.childNodes, function(nodeValue){
                thisVar._xSelect(select).append(nodeValue);        
            }, variables);
        }else if (nodeName == "remove-element")
            thisVar._xSelect(select).remove();        
        else if (nodeName == "remove-attribute"){
            jQuery.each(modElement.childNodes, function(){
            		if (this.nodeType == 1)
    				    thisVar._xSelect(select).removeAttr(this.getAttribute("name"));
                });
        }else if (nodeName == "replace"){
            thisVar._xSelect(select).each(function(){
                    //if the next sibling doesn't exist then place it at the end
                    //other wise place it before the next sibling.
                    if (this.nextSibling){
                        var node = this.nextSibling;
                        jQuery(this).remove();
                        thisVar._xProcIn(modElement.childNodes, function(nodeValue){
                            jQuery(node).before(nodeValue);        
                        }, variables);
                    }else{
                        var node = this.parentNode;
                        jQuery(this).remove();
                        thisVar._xProcIn(modElement.childNodes, function(nodeValue){
                            jQuery(node).append(nodeValue);        
                        }, variables);
                    }
                });       
        }else if (nodeName == "insert-at"){
            var index = modElement.getAttribute("index");
            thisVar._xSelect(select).each(function(){
                    var beforeNode = null;
                    var node = this;
                    
                    //if the index is more than the number
                    //of children nodes append it.
                    if (this.childNodes.length > index)
                        beforeNode = this.childNodes[index];                    
                    
                    thisVar._xProcIn(modElement.childNodes, function(nodeValue){
                            if (beforeNode)
                                jQuery(beforeNode).before(nodeValue); 
                            else
                                jQuery(node).append(nodeValue); 
                                 
                        }, variables);
            	});	
        }else if (nodeName == "variable"){
            var name = modElement.getAttribute("name");
            var deep = modElement.getAttribute("deep");
            variables[name] = thisVar._xSelect(select).clone(deep);
        }
    },     
    
    /**
     * Called to process the an instruction will loop over each of the
     * instruction's children and fire a callback
     */
    _xProcIn: function(nodes, callback, variables){
		var thisVar = this;
        jQuery(nodes).each(function(){
            //make sure there is a nodeValue and this isn't an empty string
            if ((this.nodeType == 3 || this.nodeType == 4) && jQuery.trim(this.nodeValue) != "")
                callback.call(this, this.nodeValue);
    		else if (this.nodeType == 1 && thisVar._getNodeName(this.nodeName) == "value-of")
                callback.call(this, variables[this.getAttribute("name")]);
            else if (this.nodeType == 1 && thisVar._getNodeName(this.nodeName) == "clone"){
                var select = this.getAttribute("select");
                var deep = this.getAttribute("deep");
                callback.call(this, thisVar._xSelect(select).clone(deep));
    		}else if (this.nodeType == 1)
    		    //got an XML node need to convert it into an HTML node
                callback.call(this, thisVar._convertToString(this));
     		
		});
    }, 
       	
	/**
	 * Wrapper for the CSS selectors.  Make sure to query the right
	 * value if the _context is valid or the select is valid
	 * 
	 * @param {Object} select
	 */
	_xSelect: function(select){
		if (select == null || select == "") 
			return jQuery(this._context);
		else {
			var obj = jQuery(select, this._context);
			return jQuery(jQuery.makeArray(obj));
		}
	},	
	
	/**
	 * Uses the browser xml parsser to create
	 * @param {Object} xmlContent
	 */
	_parseXML: function(xmlContent){
		if (jQuery.browser.msie){ //IE
			var nativeDoc=new ActiveXObject("Microsoft.XMLDOM");
			nativeDoc.async="false";
			nativeDoc.loadXML(xmlContent);
			return nativeDoc;
		}else if (document.implementation && //MOZ variants
					document.implementation.createDocument){
			var parser=new DOMParser(), 
				nativeDoc=parser.parseFromString(xmlContent,"text/xml");
			return nativeDoc;
		}
	}

});

/**
 * Add the processor for the "modifications" elements in the document.
 * This is a necessary becuase the stitch client just process the document
 */
stitch._modules["modifications"] = stitch._xModifyProcessDoc;
stitch._parserFunctions["modifications"] = stitch._xModifyParserFunction;





