﻿/* Build namespace */
if (NMR == undefined) var NMR = {};
if (NMR.Common == undefined) NMR.Common = {};
if (NMR.Common.Web == undefined) NMR.Common.Web = {};
if (NMR.Common.Web.GenericControls == undefined) NMR.Common.Web.GenericControls = {};

/* Dummy comment format
<summary>
    <example>
        <code>
        </code>
    </example>
</summary>
<param name="myParam"></param>
<returns></returns>
<remarks></remarks>
*/

/*
<summary>
Constructor function, passes arguments to constructor in prototype
</summary>
*/
NMR.Common.Web.GenericControls.GenericBase = function()
{
    // Call constructor
    this.create.apply(this, arguments);
}

/*
<summary>
Defines the clientside JavaScript object for the GenericBase control
</summary>
*/
NMR.Common.Web.GenericControls.GenericBase.prototype = {

    /*
    <summary>
    Prototype properties. NB: populating these with values in the prototype will share
    them across any object that instances the prototype. E.g. myPropArray: newArray()
    will mean the array referenced by myPropArray will be shared across instancing objects. 
    Think: 'static'/'shared'.  
    </summary>
    */
    type: null,                         // String representation of type
    serverObjectId: null,               // Server object instance ID
    clientObjectId: null,               // Client object instance ID
    controlEvents: null,                // Stores prototype level event handlers that will be processed for all prototype instances.
    bindingStack: null,                 // Child control binding stack (of type NMR.Common.BindingStack)
    
    /*
    <summary>
    Constructor. Can be passed an indefinite number of arguments
    via the arguments[] array.
    </summary>
    */
    create: function()
    {
            // Properties prefixed with 'this' when populated are scoped
            // only to the current prototype instance.
            this.type = arguments[0];
            this.serverObjectId = arguments[2];
            this.clientObjectId = arguments[3];
            this.bindingStack = new NMR.Common.BindingStack();
            NMR.Common.Web.GenericControls.ControlRegistrations.registerControl(this.serverObjectId,
                                                                                arguments[1],
                                                                                this.type);
    },

    /*
    <summary>
    
    </summary>
    */
    attachEventHandlers: function(detach)
    {
        var me = this;
        if(!detach)
        {
           // Attach DOM event handlers
            if(document.addEventListener)
            {
                // for Firefox
                window.addEventListener("load", function(){me.bindingStack.resolveBindings(me);}, false);
                window.addEventListener("unload",function(){me.attachEventHandlers(true);}, false); 
            }
            else if(document.attachEvent) 
            {   
                // for IE
                window.attachEvent("onload", function(){me.bindingStack.resolveBindings(me);});
                window.attachEvent("onunload", function(){me.attachEventHandlers(true);});
            }
            // Attach custom event handlers
            // [...]
            
        }
        else
        {
            // Detach DOM event handlers
            if(document.removeEventListener)
            {
                // for Firefox
                window.removeEventListener("load", function(){me.bindingStack.resolveBindings(me);}, false);
                window.removeEventListener("unload",function(){me.attachEventHandlers(true);}, false);
            }
            else if(document.detachEvent)
            {
                // for IE
                window.detachEvent("onload", function(){me.bindingStack.resolveBindings(me);});
                window.detachEvent("onunload", function(){me.attachEventHandlers(true);});
            }
            // Detach custom event handlers
            // [...]
        } 
    },

    /*
    <summary>
    Invokes a callback event to the server control instance.
    </summary>
    <param name="args">Arguments to pass to server control in callback - must be a string</param>
    <param name="context">A function to execute prior to firing the callback</param>
    <remarks>This function is re-created and populated serverside. The method signature
    should remain in sync with the definition below.</remarks>
    */
    initialiseCallback: function(args, context){},
    
    /*
    <summary>
    Receives the result from a asynchronous callback.
    </summary>
    <param name="result">Result of callback</param>
    <param name="context">The object that raised the callback</param>
    */
    returnCallback: function(result, context){},
    
    /*
    <summary>
    Raised if an error occurs in the Microsoft AJAX framework.
    </summary>
    */
    errorCallback: function(){},
    
    /*
    <summary>
    Invokes a postback event. 
    </summary>
    <param name="args">Arguments to pass to the server control</param>
    <remarks>This function is re-created and populated serverside. The method signature
    should remain in sync with the definition below.</remarks>
    */
    initialisePostback: function(args){},
    
    /*
    <summary>
    Gives an elements' position in pixels with respect to the root element (e.g. <html />).    
    </summary>
    <param name="obj">The object that needs locating in x and y pixel co-ordinates on the browser window. </param>
    <returns>A 1D array of 2 objects containing the offsets of obj in top and left with respect to the root element.</returns>    
    */ 
    findPosition: function(obj) 
    {
        var curleft = curtop = 0;
        if (obj.offsetParent) 
        {
	        curleft = obj.offsetLeft;
	        curtop = obj.offsetTop;
	        while (obj = obj.offsetParent) 
	        {
		        curleft += obj.offsetLeft;
		        curtop += obj.offsetTop;
	        }
        }
        // Return values as key:value pairs
        return {"absoluteLeft":curleft,"absoluteTop":curtop};
    },
    
    /*
    <summary>
    Retuns the current style applied to the element for either Mozilla based browser, or IE.
    </summary>
    <param name="el">The element that for which the style is required.</param>
    <remarks>
    This function operates differently depending on the browser calling it. In IE based browsers
    the return from this function is the CSS style that has/will be applied to the element as derived
    from the stylesheet(s)/inline element styling. This means values may be relative rather then absolute
    E.g. width: 30%. In Firefox, the return from this function is the absolute values as computed for the 
    display of the element in the browser, E.g. width: 1422px. There is no cross-browser equivilent of either
    function at this point.
    Additional information: http://erik.eae.net/archives/2007/07/27/18.54.15/
    </remarks>
    */
    getCurrentStyle: function(el)
    {
        if(document.all)
        {
            // For IE
            return(el.currentStyle);
        }
        else
        {
            // For FireFox
            return(document.defaultView.getComputedStyle(el, null));
        }
    },
    
    /*
    <summary>
    Serialises an xml document for transport (typically for ajax callbacks).
    </summary>
    <param name="xmlRoot">The xml fragment to serialize</param>
    <returns>Serialised xml stream</returns>
    */
    serializeForTransport: function(xmlRoot)
    {
        // Serialize the xml document data for transport 
        var rslt;
        if(document.all)
        {
            rslt = xmlRoot.xml;                            // For IE
        }
        else
        {
            var serializer = new XMLSerializer();          // For Mozilla/Firefox
            rslt = serializer.serializeToString(xmlRoot);
        }
        return(rslt);
    },
    
    /*
    <summary>
    </summary>
    */
    setValueForNode: function(node, value)
    {
        if(document.all)
        { 
            node.text = value;
        }
        else
        {
            node.textContent = value;
        }
        return(node);
    },
    
    /*
    <summary>
    </summary>
    */
    setTextForNode: function(node, value)
    {
        if(node.innerText)
        {
            node.innerText = value;
        }
        else
        {
            node.textContent = value;
        }
    },
    
    /*
    <summary>
    </summary>
    */
    getTextForNode: function(node)
    {
        if(node.innerText)
        {
            return(node.innerText);
        }
        else
        {
            return(node.textContent);
        }
    },
    
    /*
    <summary>
    </summary>
    <param name='eventName'>Name of the event - omit any 'on' prefix. E.g. for click event pass 'click' regardless of target platform</param>
    <param name='el'>Element to raise the event on</param>
    <param name='eventType'>Type of event (only applies to Mozilla based) can be: "UIEvents", "MouseEvents", "MutationEvents" or "HTMLEvents".</param>
    */
    fireEvent: function(eventName, el, eventType)
    {
        if(document.all)
        {
            var evt = document.createEventObject();
            return el.fireEvent('on'+eventName,evt)
        }
        else
        {
            var evt = document.createEvent((eventType)?eventType:"HTMLEvents");
            evt.initEvent(eventName, true, true );      // event name (click, mousedown etc), bubbling, cancelable
            return (!el.dispatchEvent(evt));
        }
    },
    
    /*
    <summary>
    Retrieves the source and target elements for event specified by the name type (E.g. "click").
    </summary> 
    <param name="evt">The event object containing the necessary information.</param>
    <param name="type">The type of event to query against (E.g. "click")</param>
    <returns>An object containing the source and target elements</returns>    
    */
    getSourceTargetFromEvent: function(evt,type) 
    {
        if(evt.type.toLowerCase() != type){return({source:null,target:null});}

        var sourceEl, targetEl;
        if(evt.currentTarget || evt.relatedTarget)
        {
            switch(evt.type.toLowerCase())
            {
                case "mouseover": 
                    targetEl = evt.currentTarget;
                    sourceEl = evt.relatedTarget;
                    break;
                    
                case "mousemove": 
                    // If this is a mousemove event, no target will be available - only
                    // the element the mouse pointer is currently positioned over.
                    sourceEl = e.originalTarget;
                    targetEl = null;
                    break;
                
                case "mouseout":
                    targetEl = evt.relatedTarget;
                    sourceEl = evt.currentTarget;
                    break;
                    
                default:    
                    sourceEl = evt.originalTarget;
                    targetEl = null;
            }
            // Ignore #text nodes
            if(sourceEl && sourceEl.nodeName.toLowerCase() == "#text"){sourceEl = sourceEl.parentNode;} 
            if(targetEl && targetEl.nodeName.toLowerCase() == "#text"){targetEl = targetEl.parentNode;}
        }
        else
        {
            switch(evt.type.toLowerCase())
            {    
                case "mouseout":
                case "mouseover":
                    targetEl = evt.toElement;
                    sourceEl = evt.fromElement;               
                    break;
                
                case "mousemove":
                    // If this is a mousemove event, no target will be available - only
                    // the element the mouse pointer is currently positioned over.
                    sourceEl = e.srcElement;
                    targetEl = null;
                
                default:
                    sourceEl = evt.srcElement;
                    targetEl = null;
            }
        }
        return {source:sourceEl, target:targetEl};
    },
    
    /*
    <summary>
    Client agnostic event binding helper method
    </summary>
    <param name='handler'>Element to attach event to</param>
    <param name='el'>Functor to execute on event attachement</param>
    <param name='eventType'>Type of event to bind. Omit the "on" prefix. E.g. pass "click" for "onclick"</param>
    <param name='detach'>Attach or detatch the event. If 'true' the event is detached</param>
    */
    bindEvent: function(el, eventType, handler, detach)
    {
        if(el)
        {
            if(!detach)
            {
                // Attach DOM event handlers
                if(document.addEventListener)
                {
                    // for Firefox
                    el.addEventListener(eventType,handler, false);
                }
                else if(document.attachEvent) 
                {   
                    // for IE
                    el.attachEvent("on"+eventType,handler);
                }
            }
            else
            {
                // Detatch DOM event handlers
                if(document.addEventListener)
                {
                    // for Firefox
                    el.removeEventListener(eventType,handler, false);
                }
                else if(document.attachEvent) 
                {   
                    // for IE
                    el.detachEvent("on"+eventType,handler);
                }
            }
        }
    },
    
    /*
    <summary>
    Retrieves the next non-text sibling or null if the provided element has no siblings.
    </summary>
    <param name='el'>The node to iterate from</param>
    */
    getNextRealSibling: function(el)
    {
        if(document.all)
        {
            return(el.nextSibling);
        }
        else
        {
            var sib = el.nextSibling;
            while(sib)
            {
                if(sib.nodeName.toLowerCase() == "#text"){
                    sib = sib.nextSibling;}
                else{
                    return(sib);}
            } 
        }
    },
    
    /*
    <summary>
    Returns the first current non-text node.
    </summary>
    <param name='el'>The node to iterate from</param>
    */
    getRealNode: function(el)
    {
        if(document.all || el.nodeName.toLowerCase() != "#text")
        {
            return(el);
        }
        else
        {
            return(this.getNextRealSibling(el));
        }
    },
    
    /*
    <summary>
    Tests the provided date to ensure it is valid and formats to a Date
    object.
    </summary>
    <remarks>
    Valid dates conform to one of the following patters: {D}D MMM YYYY, DD\MM\YYYY or YYYY-MM-DD. 
    </remarks>
    */
    dateFormat: function(dateString)
    {
        //debugger;
        var rx = new Array( /^\d{1,2} [A-Z]{3} \d{4}$/im,       // {D}D MMM YYYY
                            /^\d{1,2}\/\d{1,2}\/\d{4}$/im,      // DD\MM\YYYY
                            /^\d{4}-\d{1,2}-\d{1,2}$/im);       // YYYY-MM-DD
        
        var valid = false; 
        for(var x=0;x<rx.length;x++)
        {   
            valid = new RegExp(rx[x]).test(dateString);
            if(valid){break;}
        }
        if(!valid){return(null);}                  
        
        var dt = {"day":null,"month":null,"year":null};
        var dateComp;
        switch(x)
        {
            case 0:
                var c = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
                dateComp = dateString.split(" ");
                
                for(var x=0;x<c.length;x++)
                {
                    if(c[x].toLowerCase() == dateComp[1].toLowerCase()){dateComp[1] = (x + 1);break;}
                }
                if(isNaN(dateComp[1])){return(null);}
               
                
                break;
        
            case 1:
            
                break;
                
            case 2:
            
                break;
                
        
        }



    },
    
    
    /*
    <summary>
    Disables the selected element, and set's the appropriate CSS class name.
    </summary>
    <param name="el">The element to (dis/en)able</param>
    <param name="disable">Boolean. True to disable, false otherwise.</param>
    <param name="cssClass">Class to append/remove "_disabled" from/to</param>
    */
    disableElement: function(el, disable, cssClass)
    {
        if(el != null)
        {
            if(el.className.search(cssClass) > -1)
            {
                if(cssClass != undefined)
                {
                    if(disable && (el.disabled != true))
                    {
                        el.className = el.className.replace(cssClass, cssClass + "_disabled");
                    }
                    else if(!disable && (el.disabled == true))
                    {
                        el.className = el.className.replace(cssClass + "_disabled", cssClass);
                    }
                }
                el.disabled = disable;
            }
        }
    }
};
// Stores prototype level event handlers that will be processed for all prototype instances.
if (NMR.Common.Web.GenericControls.GenericBase.prototype.controlEvents == null) NMR.Common.Web.GenericControls.GenericBase.prototype.controlEvents = new NMR.Common.CustomEvents();

