/**
 * A random assortment of javascript utility routines
 *
 * 
 * 
 * 
 * 
 * @copyright   Silicon Mechanics
 * @license     LGPL
 *
 * @package     SiMech
 * @subpackage  Javascript
/**/

// Wrapper for the various "get element" functions used by different browsers.
    function get_element(id) {
        return $(id);
    } // end get_element

// Add an event handler to obj.  Should just replace calls to this...
    function add_event(obj, event, func) {
        Event.observe(obj, event, func);
    } // end add_event

// Pass in value to change, otherwise it returns the value of the "e" element
    function value(e, new_value) {
        if (typeof e == 'string')
            e = get_element(e);
        if (!e)
            return '';
    // A <select>
        if (e.options) {
            if (new_value != null) {
            // This would scan the options and choose the one that matches
            // new_value.  Not used anywhere yet, so no need to hook it up.
            }
            return value(e.options[e.selectedIndex]);
        }
    // Just an html element?  (or in IE, an option element with no value="" specified)
        else if (e.value == null || e.tagName.toLowerCase() == 'option' && e.value == '') {
            if (new_value != null)
                e.innerHTML = new_value;
            return e.innerHTML;
        }
    // Form field
        if (new_value != null) {
            e.value = new_value;
        }
        return e.value;
    }

// Some enhancements for the basic string object
    String.prototype.trim = function () {
        return this.replace(/^\s*(.*?)\s*$/, '$1');
    };

// Get the end position of the selection cursor in obj
    function get_cursor_end(obj){
        obj.focus();
    // Gecko
        if (typeof obj.selectionEnd != 'undefined') {
            return obj.selectionEnd;
        }
    // IE
        else if (document.selection && document.selection.createRange){
            var r1 = document.selection.createRange();
            try {
                var r2 = r1.duplicate();
                r2.moveToElementText(obj);
            } catch(e) {
                var r2 = obj.createTextRange();
            }
            r2.setEndPoint('EndToEnd', r1);
            if (r2.text.length > obj.value.length)
                return -1;
            return r2.text.length;
        }
    }
    
// Same as the PHP version
    function is_array(input){
        return typeof(input)=='object'&&(input instanceof Array);
    }

    function is_valid_array(input){
        return is_array(input) && (input.size() > 0);
    }

// Get the start position of the cursor in the object
    function get_cursor_start(obj){
        obj.focus();
    // Gecko
        if (typeof obj.selectionStart != 'undefined') {
            return obj.selectionStart;
        }
    // IE
        else if(document.selection && document.selection.createRange){
            var r1 = document.selection.createRange();
            try {
                var r2 = r1.duplicate();
                r2.moveToElementText(obj);
            } catch(e) {
                var r2 = obj.createTextRange();
            }
            r2.setEndPoint('EndToStart', r1);
            if (r2.text.length > obj.value.length)
                return -1;
            return r2.text.length;
        }
    }

// Set the selection range in the object
// (end is optional if you just want to set the cursor)
    function set_selection(obj, start, end) {
        obj.focus();
    // No selection, just a cursor
        if (typeof end == 'undefined')
            end = start;
    // Select to the end of the string?
        else if (end == 'end') {
            end = obj.value.length;
        }
    // Select backwards from the end of the string
        else if (end < 0) {
            end = obj.value.length + (end + 1);
        }
    // Swap ranges, just in case
        if (end < start) {
            var tmp = start;
            start   = end;
            end     = tmp;
        }
    // Gecko
        if (obj.setSelectionRange) {
            obj.setSelectionRange(start, end);
        }
    // IE
        else if (obj.createTextRange) {
            var r = obj.createTextRange();
            r.moveStart('character', start);
            if (start == end)
                r.collapse();
            else
                r.moveEnd('character', end);
            r.select();
        }
    }


// Image Preloader
    var img_on  = new Array();
    var img_off = new Array();
    function preload_image(id, on, off) {
        Event.observe(window,
                  'load',
                  function() {
                      img_on[id]      = new Image();
                      img_on[id].src  = on;
                      if (off) {
                          img_off[id]     = new Image();
                          img_off[id].src = off;
                      }
                  });
    }

// Functions to swap on/off states of images
    function on(which, e) {
        var img = e ? e.target : get_element(which);
        if(!img || !img_on[which])
            return;
        img.src=img_on[which].src;
    }
    function off(which, e) {
        var img = e ? e.target : get_element(which);
        if(!img || !img_off[which])
            return;
        img.src=img_off[which].src;
    }

// Window status changer
    function wstatus(str) {
        window.status = str ? str : '';
        return true;
    }

// Confirm deleting something?
    function confirm_task(what, field, val, form, type) {
        if (!field) {
            field = 'delete';
            val   = true;
        }
        if (!type) {
            type = 'delete';
        }
        if (confirm("Are you sure you want to " + type + " this " + what + "?  This cannot be undone."))
            submit_form(field, val, form);
    }

// Submit a form
    function submit_form(newvar, val, form, confirm_str) {
    // Confirm?
        if (confirm_str && !confirm(confirm_str))
            return;
    // Find the form we want to submit
        form = get_element(form ? form : 'form');
        if (!form)
            form = document.form ? document.form : document.forms[0];
    // Create a new variable?
        if (newvar) {
            var hidden = document.createElement('INPUT');
            hidden.type  = 'hidden';
            hidden.name  = newvar;
            hidden.value = val ? val : 1;
            form.appendChild(hidden);
        }
    // Submit
        form.submit();
    }

// Reset a form
    function reset_form(form, confirm_str) {
    // Confirm?
        if (confirm_str && !confirm(confirm_str))
            return;
    // Find the form we want to submit
        form = get_element(form ? form : 'form');
        if (!form)
            form = document.form ? document.form : document.forms[0];
    // reset
        form.reset();
    }

// Add a css class to a specified element
    function add_class(id, classname) {
        return $(id).addClassName(classname);
    } // end add_class

// Remove a css class from a particular element
    function remove_class(id, classname) {
        return $(id).removeClassName(classname);
    } // end remove_class

// Check whether a particular element possess a selected class
    function has_class(id, classname) {
        return $(id).hasClassName(classname);
    } // end has_class

// Check/uncheck a checkbox
    function checkbox(id, check) {
        var e = get_element(id);
        if (check)
            e.checked = true;
        else if (check != null)
            e.checked = false;
        else
            e.checked = e.checked ? false : true;
    }

// Pop open a window to a new URL
    function popup_window(url, name, width, height, center) {
        var options = 'menubar=false,status=false,toolbar=false,scrollbars,resizable';
    // Center the new window?
        if (center) {
            var left    = 0;
            var top     = 0;
        // Figure out where this window should be drawn
            if (document.documentElement.clientWidth || document.documentElement.clientHeight) {
                left = parseInt((document.documentElement.clientWidth  - width)  / 2);
                top  = parseInt((document.documentElement.clientHeight - height) / 2);
            }
            else if (document.body.clientWidth || document.body.clientHeight) {
                left = parseInt((document.body.clientWidth  - width)  / 2);
                top  = parseInt((document.body.clientHeight - height) / 2);
            }
            else {
                left = parseInt((window.innerWidth  - width)  / 2);
                top  = parseInt((window.innerHeight - height) / 2);
            }
            if (window.screenLeft || window.screenTop) {
                left += parseInt(window.screenLeft);
                top  += parseInt(window.screenTop);
            }
            else if (window.screenX || window.screenY) {
                left += parseInt(window.screenX);
                top  += parseInt(window.screenY) + parseInt(window.outerHeight) - parseInt(window.innerHeight);
            }
        // Do some error checking - Safari has issues with height/width stuff and multiple monitors
            if (top > 200 && height > 0)
                options += ',top=' + top;
            if (left > 200 && width > 0)
                options += ',left=' + left;
        }
    // Make sure the window isn't too tall/wide
        if (width > screen.availWidth)
            width = screen.availWidth;
        if (height > screen.availHeight)
            height = screen.availHeight;
    // Add the height and width
        if (width > 0)
            options += ',width=' + width;
        if (height > 0)
            options += ',height=' + height;
    // Open a new window, with the new dimensions
        var win = window.open(url, name, options);
        win.focus();
    }

// Round a number to a specified number of decimal places
    function round(num, places, strip) {
    // Default to 2 decimal places, round the number
        places = (!isNaN(places) ? parseInt(places) : 2);
        num = '' + Math.round(num * Math.pow(10, places)) / Math.pow(10, places);
    // No trailing zeroes?
        if (strip || places == 0)
            return num;
    // Need to add trailing zeroes?
        var i = num.indexOf('.');
        if (i < 0) {
            num = num + '.';
            i = num.length - 1;
        }
        i = num.length - i - 1;
        for(var j=i;j<places;j++) {
            num = num + '0';
        }
        return num;
    }

// Resize a the specified <textarea>
    function resize_textarea(id, rows, cols) {
        var text = get_element(id);
        text.rows = rows != null ? rows : value(id + '_rows');
        text.cols = cols != null ? cols : value(id + '_cols');
    }

// Generate and return a random id for an element that doesn't have one
    var random_id_seed = Math.random();
    function generate_random_id() {
        while (get_element('random_id_' + random_id_seed))
            random_id_seed++;
        return 'random_id_' + random_id_seed;
    }

// Print out all of a variable's contents, except functions (unless show_func
// is passed in as a true value).
    function debug(what, show_func) {
        if(!window.dev_domain) return;
        var str = typeof what + ':\n\n';
        if (typeof what == 'object') {
            for (data in what) {
                try {
                    if (typeof what[data] == 'function' && !show_func)
                        continue;
                    str += data + ':  ' + what[data] + "\n";
                }
                catch (e) {
                    continue;
                }
            }
        }
        else {
            str = what;
        }
    // Create a popup window, and print the debug content
        var win = window.open('', 'debug', 'menubar=false,status=false,toolbar=false,scrollbars,resizable,width=800,height=600');
        win.document.write('<html><body><h3>javascript debug:</h3><pre>');
        win.document.write(str);
        win.document.write('</pre></body></html>');
        win.focus();
    }

// Returns the parent of item, where parent is of type type
    function get_parent(obj, type) {
        // Doing it this way is much faster than using $(obj).up(type);
        while (obj) {
            if (obj.nodeType == 1 && obj.tagName.toLowerCase() == type.toLowerCase())
                return obj;
            obj = obj.parentNode;
        }
        return null;
    }

// Implimentation of php's urlencode
    function urlencode(string) {
        return escape(string).replace('+', '%2B');
    }

// Implimentation of php's urldecode
    function urldecode(string) {
        return unescape(string.replace('%2B', '+'));
    }

// Php's htmlentities
// quote_style and charset do nothing right now
    function htmlentities(string, quote_style, charset) {
        string = string.replace('&', '&amp;');
        string = string.replace('>', '&gt;');
        string = string.replace('<', '&lt;');
        string = string.replace('"', '&quot;');
        return string;
    }

// Php's html_entity_decode
// quote_style and charset do nothing right now
    function html_entity_decode(string, quote_style, charset) {
        string = string.replace('&quot;', '"');
        string = string.replace('&lt;', '<');
        string = string.replace('&gt;', '>');
        string = string.replace('&amp;', '&');
        return string;
    }

// Make an ajaxy request to log something that should be in an assert()
    function js_assert(string) {
        new Ajax.Request('/assert.php?' + urlencode(string), {
                method: 'GET',
                onSuccess: function(t){},
                onComplete: function(t){},
                onError: function(t){}
            }
        );
    } // end js_assert

// A little Prototype-inspired Cookienation.  http://wiki.script.aculo.us/scriptaculous/show/cookie
    var Cookie = {
      set: function(name, value, daysToExpire) {
        var expire = '';
        if (daysToExpire != undefined) {
          var d = new Date();
          d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
          expire = '; expires=' + d.toGMTString();
        }
        return (document.cookie = escape(name) + '=' + escape(value || '') + expire);
      },
      get: function(name) {
        var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
        return (cookie ? unescape(cookie[2]) : null);
      },
      erase: function(name) {
        var cookie = Cookie.get(name) || true;
        Cookie.set(name, '', -1);
        return cookie;
      },
      accept: function() {
        if (typeof navigator.cookieEnabled == 'boolean') {
          return navigator.cookieEnabled;
        }
        Cookie.set('_test', '1');
        return (Cookie.erase('_test') === '1');
      }
    };

// And finally, set up some dummy console stuff in place of Firebug.
    if (!("console" in window) || !("firebug" in console)) {
        var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
        "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];

        window.console = {};
        for (var i = 0; i < names.length; ++i)
            window.console[names[i]] = function() {}
    } // end if

// The menus totally don't belong here, but there's nowhere else to put them,
// and the idea of yet another file JUST for the menus is silly, especially
// considering the code is so small.
    var menu_collapses = $H();
    var menu_styles_defined = $H();
    var submenus = $A();
    var subsubmenus = $A();

// Call for a menu collapse
    function delayed_menu_collapse(element) {
        var timeout = setTimeout(function(){
            element.hide();
            element.select('ul.subsubmenu').invoke('hide');
            menu_collapses.unset(element.id);
        }, 500);
        menu_collapses.set(element.id, timeout);
    } // end delayed_menu_collapse

// Uncall for a menu collapse
    function cancel_menu_collapse(element) {
        var cancelme = menu_collapses.get(element.id);
        if(cancelme)
            clearTimeout(cancelme);
        menu_collapses.unset(element.id);
    } // end cancel_menu_collapse

// Set up menus.  What else did you expect?
    function init_www_menus() {
		if(!submenus.size()) {
			$(document).observe('click', function(event){
				if(event.button == 0 && !(event.ctrlKey || event.metaKey))
					submenus.invoke('hide');
				// console.debug(event);
			});
		} // end if
        $$('table.top_nav td').each(function(el){
            var menu = el.down('ul.submenu');
            if(menu) {
                submenus.push(menu);
            // I forgot how delightful over/out events are.  They fire for not just
            // the element itself, but all children of the event.  We need to take
            // a little extra care to deal with that.
                el.observe('mouseover', function(event){
                    cancel_menu_collapse(menu);
                // Hide all other open first-tier menus.
                    submenus.each(function(submenu){ if(submenu != menu) submenu.hide(); })
                // Only compute styles once
                    if(!menu_styles_defined.get(menu.id)) {
                        var left_bump = -1;
                        var top_bump = 0;
                        var width_bump = 0;
                    // Webkit and Opera need slight bumps...
                        if(Prototype.Browser.WebKit || Prototype.Browser.Opera) {
                            left_bump = 0;
                            top_bump = 1;
                            width_bump = 1;
                        } // end if
                    // IE needs a slight width bump.
                        if(Prototype.Browser.IE) {
                            width_bump = 1;
                        } // end if
                    // IE and FF have small one pixel differences in menu widths,
                    // which I'm pretty sure comes from border calculations not being
                    // included in getWidth, or something like that.  I haven't looked
                    // into it very hard at time of comment writing  Need more research.
                    // Opera and WebKit get it about right.
                        var elpos = el.positionedOffset();
                        menu_styles_defined.set(menu.id, true);
                        menu.setStyle({
                            position: 'absolute',
                            top: (elpos.top + el.getHeight() + top_bump) + 'px',
                            left: (elpos.left + left_bump) + 'px',
                            width: (el.getWidth() + width_bump) + 'px',
                            zIndex: 101, // banner gallery controls + 1
                            display: 'block'
                        });
                    } else {
                    // Yay, already styled
                        menu.show();
                    } // end if
                // You can stop now, thanks.
                    if(event.element() == el)
                        event.stop();
                });
            // Outs are easier.
                el.observe('mouseout', function(event){
                    delayed_menu_collapse(menu);
                    if(event.element() == el)
                        event.stop();
                });
            // Does this menu have submenus?  Don't ask about how they became named
            // subsubmenus, it's a long story and I'm tired.
                el.select('ul.subsubmenu').each(function(subsub){
                    var subsubparent = subsub.up('li');
                    subsubmenus.push(subsub);
                // Same deal here with the child firing issues.  Gotta keep track.
                // In this case, we need to prevent our parent from collapsing as well.
                    subsubparent.observe('mouseover', function(event) {
                        cancel_menu_collapse(menu);
                        cancel_menu_collapse(subsub);
                    // Only permit one submenu to be visible at once.
                        submenus.each(function(submenu){ if(submenu != menu) submenu.hide(); })
                        subsubmenus.each(function(subsubmenu){ if(subsubmenu != subsub) subsubmenu.hide(); })
                    // Cache styles
                        if(!menu_styles_defined.get(subsub.id)) {
                            menu_styles_defined.set(subsub.id, true)
                            var suboffset = subsubparent.positionedOffset();
                            subsub.setStyle({
                                position: 'absolute',
                                top: ( suboffset.top + 1 ) + 'px',
                                left: ( suboffset.left + subsubparent.getWidth() - 3 ) + 'px',
                                width: 175 + 'px', // hard coded here
                                zIndex: 102, // menus + 1
                                display: 'block'
                            });
                        } else {
                        // yay, cached!
                            subsub.show();
                        } // end if
                    // Halt!  Who goes there?!
                        if(event.element() == subsubparent)
                            event.stop();
                    });
                // Out!  Out, I say!
                    subsubparent.observe('mouseout', function(event) {
                        delayed_menu_collapse(subsub);
                        if(event.element() == subsubparent)
                            event.stop();
                    });
                });
            } // end if
        });
    } // end init_www_menus
