/**
 * This class serves as the container for all global variables and methods for the
 * JavaScript application implemented.  
 * This class is agnostic as to the type of application implemented.
 * <br><br>
 * The first custom JavaScript code that executes resides at the 
 * bottom of this file.  It executes when the page begins to load.
 * This code is the only non-prototype-assignment constructor invocation
 * or method
 * invocation that exists outside of a function scope in this entire codebase.
 * <br><br>
 * <a href="mailto:ehudsons@andrew.cmu.edu">Ellen Hudson-Snyder</a>, 
 * <a href="mailto:evedar@andrew.cmu.edu">Elvin Vedar</a>,
 * <a href="mailto:cbalz@andrew.cmu.edu">Christopher M. Balz</a>.
 * <br><br>CVS Version Info:<br>
 *  $Id: GloScope.js,v 1.19 2005/12/27 01:27:00 cbalz Exp $   
 * <br><br>
 * @class-prop <code>Object</code>  <code>hshListenerRegistry</code>  A registry of listeners to events, set by 
 *                                                                     widgets and other 
 *                                                                     non-<code>Controller</code> objects, such as 
 *                                                                     <code>Messenger</code>.
 * @class-prop <code>RegExp<code>  <code>RXP_PNG</code>  A regular expression for replacing 'png' image urls in
 *                                                       html <code>image</code> element <code>src</code> properties.
 * @class-prop <code>RegExp<code>  <code>RXP_CSS_URL_PNG</code>  A regular expression for replacing 'png' image urls in
  *                                                              css url properties. 
 * @class-prop <code>string<code>  <code>STR_GIF</code> The 'gif' string to swap in when replacing 'png' urls in 
 *                                                      html <code>image</code> element <code>src</code> properties.
 * @class-prop <code>string<code>  <code>STR_CSS_URL_GIF</code> The 'gif' string to swap in when replacing 'png' 
 *                                                              urls in css url properties. 
 * @object-prop <code>string</code> <code>strApplication</code> The name of the application currently being run.
 * @object-prop <code>Is</code>  <code>objIs</code>  An object that tells us what the platform (machine/operating 
 *                                                   system/web browser) <b>is</b> that we are running on.  
 *                                                   It is instantiated by the <code>Environment</code> class 
 *                                                   (and used there for some environment setting purposes).  
 *                                                   However, it should be accessed from <code>self.gloScope</code>
 *                                                   or references to that object, since it represents 
 *                                                   platform-specific information as 
 *                                                   opposed to the core infrastructure for the application.  
 * @object-prop <code>Object</code>  <code>domCanvas</code>  The canvas upon which the application will draw itself.
 * @object-prop <code>Object</code> <code>objStrings</code>  An object that contains all the user-interface character strings for the application.
 * @object-prop <code>Object</code>  <code>ctController</code>  The <code>Controller</code> that controls the current application.
 * @object-prop <code>string</code> <code>strLocale</code>  The regional locale (i.e., 'uk', 'us', 'ru') that the current application must run.
 * @object-prop <code>string</code> <code>strObjType</code>  The type of object that this is.
 * @object-prop <code>boolean</code> <code>booDebugMode</code>  Whether the application should run in debugging mode or not.
 * @author Team GigaToasted (Fall-2005-CMU-NASA/Google-Practicum Subteam) 
 * @version 1.0
 * <br><br>
 */


GloScope.RXP_PNG = /\.png$/;
GloScope.RXP_CSS_URL_PNG = /\.png\)/;
GloScope.STR_GIF = ".gif";
GloScope.STR_CSS_URL_GIF = ".gif)";

/**
 * Create a <code>GloScope</code> object.
 * @param strApplication <code>string</code>  The name of the controller for this application.
 */
function GloScope(strApplication) { 
    // Properties:
    this.strObjType = "custom_global_object";
    this.domCanvas = null;
    this.objIs = null;
    this.strApplication = strApplication;
    this.booDebugMode = false;
    this.RXP_PX = new RegExp("px");

    //    --- Locale setting:
    this.strLocale = "us";

    // Methods: 
    this.init = GloScope_init;
    this.registerEvent = GloScope_registerEvent;
    this.getPixelIntVal = GloScope_getPixelIntVal;
    this.getWholeDomain = GloScope_getWholeDomain;
    this.getStyleSheetByTitle = GloScope_getStyleSheetByTitle; 
    this.getStyleClassByName = GloScope_getStyleClassByName;
    this.swapPngForGif = GloScope_swapPngForGif;
    this.unload = GloScope_unload;
}


/**
 * This method searches for a loaded css style sheet specified by the parameter to this method.
 * @param strTitle <code>string</code> title 
 * @return <code>Object</code>  MAY BE NULL The document object model object that represents the style sheet.
 */
function GloScope_getStyleSheetByTitle(strTitle) { // Note: On Opera 8.5, <code>window.document.styleSheets</code> is still <code>undefined</code>.
    var intI = 0, 
        arrStyleSheets = window.document.styleSheets ? window.document.styleSheets : [], 
        intL = arrStyleSheets.length;

    for (; intI < intL; intI++ ) {
        if (arrStyleSheets[intI].title == strTitle) {
            return arrStyleSheets[intI];
        }
    }

    return null;
}

 
/**
 * This method searches for a css style class specified by the parameter <code>strName</code> 
 * to this method in the loaded css style sheet represented by the parameter <code>domStyleSheet</code>
 * to this method.
 * @param domStyleSheet <code>Object</code> 
 * @param strName <code>string</code>  
 * @return <code>Object</code>  MAY BE NULL The document object model object that represents the css style class.
 */
function GloScope_getStyleClassByName(domStyleSheet, strName) {
    var intI = 0, arrCssRules = domStyleSheet.rules ? domStyleSheet.rules : domStyleSheet.cssRules, 
        intL = arrCssRules.length;

    for (; intI < intL; intI++ ) {
        if ( arrCssRules[intI].selectorText == strName ) {
            return arrCssRules[intI];
        }
    }
    
    return null;
}


/**
 * This method initializes the application-specific global scope, including the 
 * application controller itself.
 * @param pObjIs  <code>object</code> Tells us what the platform (machine/operating 
 *                system/web browser) <b>is</b> that we are running on.  This object 
 *                is passed in by <code>Environment</code>.
 */
function GloScope_init(pObjIs) {
    this.objIs = pObjIs;
    GloScope.hshListenerRegistry = {};
    this.domCanvas = window.document.getElementById("mainBody");
    this.objStrings = new Strings(this.strLocale);
    this.ctController = eval("new "+this.strApplication+"();");
}


/**
 * This is a static method.  It builds the event registry.  All objects wishing to receive events must register here.
 * This method is static because it needs to be separated from the JavaScript logic that holds references to the 
 * document object model elements.  This architecture avoids a circular reference situation. 
 * <br>
 * The underlying implementation of the event registry leverages the structure of JavaScript
 * for speed in distributing events to listening objects.  
 * <br><br>
 * Specifically, it uses nested hashes, moving from the i.d. of the 
 * event emitting object (i.e., a document object model element or custom JavaScript object)
 * to event type
 * to a linked list of objects that listen to the specified type of event coming from the specified type 
 * of event emitter.
 * @param pStrListeningObjectId  <code>string</code>  The i.d. of the listening object.
 * @param pObjListeningObject    <code>object</code>  A reference to the listening object.
 * @param pStrEventSourceElementId  <code>string</code>  The i.d. of the document object model element, or an 
 *                                          equivalent object
 *                                          (such as a custom js object that manages other, true dom objects)
 *                                          from which the event will come.
 * @param pStrEventType <code>string</code>  The type of event.  If you do not see the event you want listed below, 
 *                              please list it.  The event may come from a true document object 
 *                              element or from some other object.  The system will work with your event.
 *                              The event specified must match one element of the following list (just add your 
 *                              event to this list if needed):
 * <ul style="margin-left: 10px;">
 * <li>
 * <code>onclick</code>
 * </li>
 * <li>
 * <code>onresize</code>
 * </li>
 * <li>
 * <code>onmousedown</code>
 * </li>
 * <li>
 * <code>onmouseover</code>
 * </li> 
 * <li>
 * <code>onmouseup</code>
 * </li> 
 * <li>
 * <code>ondrag</code>
 * </li>
 * <li>
 * <code>onkeypress</code>
 * </li>
 * <li>
 * <code>onkeydown</code>
 * </li>
 * <li>
 * <code>onkeyup</code>
 * </li>
 * <li>
 * <code>onmoveup</code>
 * </li> 
 * <li>
 * <code>onmovedown</code>
 * </li> 
 * <li>
 * <code>onmoveleft</code>
 * </li> 
 * <li>
 * <code>onmoveright</code>
 * </li> 
 * <li>
 * <code>onmovecenter</code>
 * </li>
 * <li>
 * <code>onafterreceive</code>
 * </li>
 * </ul>
 */
function GloScope_registerEvent(pStrListeningObjectId, pObjListeningObject, pStrEventSourceElementId, pStrEventType) {
    var hshDocumentElements, hshEventType; // Line above must be on one line for jsdoc builder.
    
    if (typeof self.GloScope.hshListenerRegistry[ pStrEventSourceElementId ] == "undefined") {
        self.GloScope.hshListenerRegistry[ pStrEventSourceElementId ] = {}; 
    } 
    hshDocumentElements = self.GloScope.hshListenerRegistry[ pStrEventSourceElementId ];   

    if (typeof hshDocumentElements[ pStrEventType ] == "undefined") {
         hshDocumentElements[ pStrEventType ] =
             {
                 objListenerIds : new FastLinkedList(),
                 objListeners   : new FastLinkedList() 
             }; 
    }
    hshEventType = hshDocumentElements[ pStrEventType ];
    
    hshEventType.objListenerIds.add(pStrListeningObjectId);
    hshEventType.objListeners.add(pObjListeningObject);
}


/**
 * This method removes the "px" suffix from a string, such as 
 * a css style property, and converts the remaining 
 * string value to an integer.  
 * @return <code>int</code> The integer value of the pixel-suffixed expression. 
 */
function GloScope_getPixelIntVal(pStrNumberWithPxSuffix) {
    return parseInt(pStrNumberWithPxSuffix.replace(this.RXP_PX, ""));
}


/**
 * This method extracts the whole, non-downgraded domain (i.e., 'fred.cs.cmu.edu') from a complete url that
 * has the 'http://' in it.  
 * @param pStrUrl  <code>string</code> The complete url.
 * @return <code>string</code> A whole, non-downgraded domain, if any.  If not any, the value returned is the
 * empty string.
 */
function GloScope_getWholeDomain(pStrUrl) {
    var strHttpDomain = pStrUrl.match(/http:\/\/[^\/]+/);
    
    if (!strHttpDomain) {
        return "";
    }

    return strHttpDomain.toString().replace(/http:\/\//, ""); 
}



/**
 * This method cleans out any 'png' (Portable Network Graphics) images and replaces them with 
 * 'gif' images.  This is useful for situations where an image that supports transparency must be
 * displayed, but not all supported web browsers display the transparency in the newer and much 
 * prettier type of image
 * format, 'png'.  Where transparency is not needed, 'jpg' is a great image format.
 * <br><br>For example, IE 6 does not support transparency in 'png's.  Yet one would hate to 
 * force Firefox to display an ugly 'gif' to the user when it could, and should, show a nice 'png'.
 * This method goes through all the <code>window</code> style sheet rules, the image collection 
 * of <code>pDomObj</code>, and 
 * all document object model <code>style</code> <code>backgroundImage</code> properties of the 
 * <code>div</code> hierarchy (plus top-level <code>span</code> objects in <code>div</code>s in 
 * that hierarchy) specified by <code>pDomObjToTraverse</code>, and replaces '.png' with '.gif'.<br>
 * <b>Compatibility:</b> Compatible with all browsers that support either the IE-specific style rule
 * nomenclature or the w3c consortium standard nomenclature for style rules.
 * @param pDomObj <code>Object</code> The document object from which to collect the (non-background) images and then 
 *                                    process them.
 * @param pDomObjToTraverse <code>Object</code> The document object to traverse the <code>div</code> elements of,
 *                                             replacing all 'png' image source values in the <code>style</code> 
 *                                             document object model object of all <code>div</code> elements and 
 *                                             all top-level <code>span</code> elements with 'gif' equivalents. 
 *                                             (Note:  <code>span</code> element hierarchies are not traversed;
 *                                             only the <code>div</code> element hierarchies are traversed).
 *                                             
 * 
 */
function GloScope_swapPngForGif(pDomObj, pDomObjToTraverse) {
    var intI = 0, objImage = null, arrImages = pDomObj.document.images, intL = arrImages.length, intRecurse = 0;
   

    /**
     * Replaces a css-format 'png' url with one calling for a 'gif' image.
     * @param strCssUrl <code>string</code> The <code>style.backgroundImage</code> / <code>background-image</code>
     *                                      value.
     * @return <code>string</code>  The modified string.
     */
    function _swapPngForGif_doCssUrlSwap(strCssUrl) {
        return strCssUrl.replace(GloScope.RXP_CSS_URL_PNG, GloScope.STR_CSS_URL_GIF);
    }

     
    /**
     * This method goes through all the <code>window</code> style sheet rules  and replaces '.png' with '.gif'.
     */
    function _swapPngForGif_replaceInCssText() { // Note: On Opera 8.5, <code>window.document.styleSheets</code> is still <code>undefined</code>.
        var intI = 0, intJ, domStyleSheet, arrRules, intLR, 
            arrStyleSheets = window.document.styleSheets ? window.document.styleSheets : [], 
            intL = arrStyleSheets.length;

        for (; intI < intL; intI++ ) {
            domStyleSheet = arrStyleSheets[intI];
            if (typeof domStyleSheet.cssText != "undefined" && domStyleSheet.cssText) { // MSIE browser
                domStyleSheet.cssText = _swapPngForGif_doCssUrlSwap(domStyleSheet.cssText);
            } else if (domStyleSheet.cssRules) { // w3c-standard browsers           
                arrRules = domStyleSheet.cssRules;
                intLR = arrRules.length;
                intJ = 0;
                for (; intJ < intLR; intJ++) {
                    if (arrRules[intJ].cssText) {
                        arrRules[intJ].cssText = _swapPngForGif_doCssUrlSwap(arrRules[intJ].cssText);
                    }
                }
            }
        }
    }


    /**
     * This method traverses the <code>div</code> element hierarchy specified by its parameter.  
     * It replaces all 'png' image source values in the <code>style</code> document object model object of 
     * all <code>div</code> elements and all top-level <code>span</code> elements with 'gif' equivalents. 
     * (Note:  <code>span</code> element hierarchies are not traversed;
     * only the <code>div</code> element hierarchies are traversed).
     * @param pDomTraverse <code>Object</code> The document object to traverse.
     */
    function _swapPngForGif_swapCssBackgroundImages(pDomTraverse) {        
         var domI = null, domObj = null, docEl = null, arrIter, intZ = 0, intL;

        function _swapCssBackgroundImages_tagElementArray(pArrTagElements) {
            var intI = 0, intL; domTagEl = null, domTagElStyle = null; 

            if (typeof pArrTagElements == "undefined" || !pArrTagElements) {
                return; 
            }
            intL = pArrTagElements.length;

            for (; intI < intL; intI++ ) {        
                domTagEl = pArrTagElements[ intI ];
                if (typeof domTagEl.style != "undefined" && domTagEl.style) {                    
                    domTagElStyle = domTagEl.style; 
                    if (typeof domTagElStyle.backgroundImage != "undefined" && domTagElStyle.backgroundImage) {                        
                        domTagElStyle.backgroundImage = 
                            domTagElStyle.backgroundImage = _swapPngForGif_doCssUrlSwap(domTagElStyle.backgroundImage);
                    }
                } 
            }
        }


        /*-
         * On IE, 'body' elements form a difficult-to-break circular reference on IE with 'body.document'.
         */
        arrIter = pDomTraverse.getElementsByTagName("div");               
        intL = arrIter.length;
        for (; intZ < intL; intZ++ ) {            
            domObj = arrIter[ intZ ];
            if (typeof domObj.getElementsByTagName != "undefined" && domObj.getElementsByTagName) {                    
                _swapCssBackgroundImages_tagElementArray( [ domObj ] );
                _swapCssBackgroundImages_tagElementArray(domObj.getElementsByTagName("span"));                                               
                if (intRecurse > 99) { // Defend against borkage.
                    break;
                }
                intRecurse++;
                _swapPngForGif_swapCssBackgroundImages(domObj); // 'parentNode' elements can be null.
            }
        }            
        return;
    }


    for (; intI < intL; intI ++) {        
        objImage = arrImages[ intI ];
        objImage.src = objImage.src.replace(GloScope.RXP_PNG, GloScope.STR_GIF); 
    }
    
    _swapPngForGif_swapCssBackgroundImages(pDomObjToTraverse);
    _swapPngForGif_replaceInCssText();
}


/**
 * This method helps to speed garbage collection by the web browser
 * when the user leaves the current application.
 */
function GloScope_unload() {
    this.domCanvas = null;
}


/*-
 * This is the first code that executes when the page begins to load.
 * This is the only non-prototype-assignment constructor or method
 * invocation outside of a function scope.
 */
self.gloScope = new GloScope("Gigapan");
