/*************************************************************************
    This code is from Dynamic Web Coding at dyn-web.com
    Copyright 2003-2008 by Sharon Paine 
    See Terms of Use at www.dyn-web.com/business/terms.php
    regarding conditions under which you may use this code.
    This notice must be retained in the code as is!
    
    version date: April 20, 2008
    requires: dw_event.js (april 2008 version) 
        and dw_viewport.js (march 2008 version)
*************************************************************************/

var dw_Tooltip = {
    offX: 8,
    offY: 8,
    showDelay: 100,
    hideDelay: 100,
    hoverDelay: 500, // for hover tip
    tipID: "tipDiv",
    actuatorClass: "showTip",
/////////////////////////////////////////////////////////////////////
// Edit below at your own risk!  Modifications are not supported 
/////////////////////////////////////////////////////////////////////
    tip: null, shim:null, timer: 0, hoverTimer: 0,
    active: false, actuator: null, resetFlag: false, restored: true,
    on_show: function() {}, on_position: function() {}, on_hide: function() {},
    
    init: function() {
        var _this = dw_Tooltip;
        if ( document.createElement && document.body && typeof document.body.appendChild != "undefined" ) {
            var el = document.createElement("DIV");
            el.id = _this.tipID; el.style.position = 'absolute';
            el.style.visibility = 'hidden'; el.style.zIndex = 10000;
            document.body.appendChild(el);
            _this.tip = document.getElementById( _this.tipID);
            _this.setDefaults();
            if ( _this.checkOverlaySupport() ) { _this.prepOverlay(); }
            _this.setPosition(0, 0);
        }
    },
    
    setDefaults: function() { // called when props changed (resetFlag set)
        if ( !this.defaultProps ) this.defaultProps = {};
        // prop name, type, default
        var list = [  ['followMouse', 'boolean', true], ['sticky', 'boolean', false], ['klass', 'string', ''],
            ['hoverable', 'boolean', false], ['duration', 'number', 0], ['adjustVert', 'boolean', false],
            ['positionFn', 'function', this.positionRelEvent], 
            ['wrapFn', 'function', function(str) { return str; } ]  ];
        
        for (var i=0; list[i]; i++) {
            this[ list[i][0] ] = ( typeof this.defaultProps[ list[i][0] ] == list[i][1] )? 
                this.defaultProps[ list[i][0] ]: list[i][2];
        }
        
        this.tip.className = this.klass;
        this.coordinateOptions();
    },
    
    activate: function(e, tgt, msg, id) {
        var _this = dw_Tooltip; if (!_this.tip) return;
        _this.clearTimer('timer');  _this.clearTimer('hoverTimer');
        if ( !_this.restored ) _this.handleRestore();
        if (tgt) _this.tipActuator(e, tgt); 
        dw_Viewport.getAll();  _this.handleContent(e, tgt, msg, id); 
        _this.restored = false;
        if ( !_this.tip.innerHTML ) return; _this.active = true;
        _this.handleOptions(e);  _this.positionFn(e); _this.adjust();
        _this.timer = setTimeout(_this.show, _this.showDelay);
    },

    handleContent: function(e, tgt, msg, id) {
        msg = msg || '';  tgt = tgt || this.tipActuator(e);
        if (!msg) {
            var source = this.defaultProps['content_source'] || 'content_vars';
             do { // if tgt image inside link, etc
                switch (source) {
                    case 'content_vars': msg = this.getFromContentVars(tgt, id); break;
                    case 'class_id' : msg = this.getContentViaId(tgt); break;
                }
            } while ( !msg && (tgt = tgt.parentNode) );
        }        
        this.writeTip(msg);
    },
    
    getFromContentVars: function(tgt, id) {
        id = id || this.getTipClass(tgt.className);
        var obj = (id && this.content_vars && this.content_vars[id])? this.content_vars[id]: null;
        if ( !obj ) return ''; var msg = '';
        
        if ( typeof obj == 'string' ) {
            msg = obj;
        } else if ( typeof obj == 'object' ) {
            this.checkForProps( obj ); // Check object for property settings 
            
            if ( obj['content'] ) { // if it has a content property that would contain the message 
                msg = obj['content'];
            } else if ( obj['html_id'] ) { // id of page element
                var el = document.getElementById( obj['html_id'] ); 
                if (el) msg = el.innerHTML;
            } else { 
                msg = obj;  // wrapFn will obtain props from obj (image, text, caption, etc)
            }
        }
        return msg;
    },
    
    writeTip: function(msg) {
        msg = this.wrapFn(msg); 
        this.tip.innerHTML = msg;
    },
    
    tipActuator: function(e, tgt) {
        tgt = tgt || null;
        if (tgt) { // passed when using event delegation
            this.actuator = tgt;
        } else if ( this.actuator ) { // saved to get when no e 
            tgt = this.actuator;
        } else if (e) {
            e = dw_Event.DOMit(e);
            tgt = e.target; 
            if (tgt.nodeType != 1) tgt = tgt.parentNode;
            this.actuator = tgt;
        }
        return tgt;
    },
    
    positionRelEvent: function(e) {
        var _this = dw_Tooltip; 
        if (typeof e == 'object') { // event 
            if ( e.type == 'mouseover' || e.type == 'mousemove' ) {
                x = _this.evX = _this.getMouseEventX(e);
                y = _this.evY = _this.getMouseEventY(e);
            } else { // focus
                if (!e.target) e = dw_Event.DOMit(e);
                var pos = dw_getPageOffsets( e.target );
                x = _this.evX = pos.x + e.target.offsetWidth; y =_this.evY = pos.y
            }
            
        } else { // if called after delay
            x = _this.evX; y = _this.evY; 
        }
        var maxX = _this.getMaxX(); var maxY = _this.getMaxY(); // get width/height
        if ( _this.adjustVert ) {
            x = ( x + _this.offX > maxX )? maxX: x + _this.offX;
            y = ( y + _this.offY > maxY )? y - ( _this.height + _this.offY ): y + _this.offY;    
        } else {
            x = ( x + _this.offX > maxX )? x - ( _this.width + _this.offX ): x + _this.offX;
            y = ( y + _this.offY > maxY )? maxY: y + _this.offY;            
        }
        _this.setPosition(x, y);
    },
    
    adjust: function() {
        var _this = dw_Tooltip;
        var imgs = _this.tip.getElementsByTagName('img');
        var img = imgs.length? imgs[imgs.length - 1]: null;
        checkComplete();
        
        function checkComplete() {
            if ( !_this.active ) return;
             _this.positionFn();
            if (img && !img.complete) {
                setTimeout( checkComplete, 50);
            }
        }
    },
    
    setPosition: function(x, y) {
        this.tip.style.left = x + 'px';
        this.tip.style.top = y + 'px';
        this.setOverlay(); this.on_position();
    },

    show: function() {
        var _this = dw_Tooltip;
        _this.tip.style.visibility = 'visible';
        if ( _this.shim ) _this.shim.style.visibility = 'visible';
        _this.on_show();
    },

    deactivate: function(e) {
        var _this = dw_Tooltip; if (!_this.tip) return;
        if (e.type && e.type == 'mouseout' && !dw_mouseleave(e, _this.actuator) ) return;
        if ( _this.sticky ) return;
        _this.clearTimer('timer');  _this.clearTimer('hoverTimer');
        
        if ( _this.hoverable ) { // delayed call to hide (time to check if hovered over tip)
            _this.hoverTimer = setTimeout( _this.hide, _this.hoverDelay );
            return;
        }
        if ( _this.duration ) {
            _this.timer = setTimeout( _this.hide, _this.duration );
            return;
        }
        _this.timer = setTimeout( _this.hide, _this.hideDelay );
    },
    
    hide: function() {
        var _this = dw_Tooltip; if (!_this.tip) return;
        _this.tip.style.visibility = 'hidden';
        if ( _this.shim ) _this.shim.style.visibility = 'hidden';
        _this.handleRestore(); _this.on_hide();
    },
    
    handleOptions: function(e) {
        this.coordinateOptions();
        if ( this.klass ) { this.tip.className = this.klass; }
        if ( this.hoverable ) {
            this.tip.onmouseout = dw_Tooltip.tipOutCheck;
            this.tip.onmouseover = function() { dw_Tooltip.clearTimer('hoverTimer'); }
        }
        if ( this.followMouse && !this.hoverable && !(e.type == 'focus') ) {
            dw_Event.add(document, 'mousemove', this.positionRelEvent);
        }
        
        if ( this.sticky || this.duration ) {
            dw_Event.add( document, "mouseup", dw_Tooltip.checkHide );
        }
    },
    
    coordinateOptions: function() {
        if ( this.sticky || this.hoverable || this.duration ) { this.followMouse = false; }
        if ( this.sticky ) { this.hoverable = false; this.duration = 0; }
        if ( this.hoverable ) { this.duration = 0; }
        if ( this.positionFn != this.positionRelEvent ) this.followMouse = false;
    },

    handleRestore: function() {
        if ( this.followMouse ) {
            dw_Event.remove(document, 'mousemove', this.positionRelEvent);
        }
        if ( this.sticky || this.duration ) {
            dw_Event.remove( document, "mouseup",   dw_Tooltip.checkHide, false );
        }
        this.tip.onmouseover = this.tip.onmouseout = function() {}
        
        if ( this.resetFlag ) this.setDefaults(); 
        this.tip.innerHTML = ''; 
        
        this.active = false; this.actuator = null;
        this.tip.style.width = ''; 
        this.restored = true;
    },
    
    // first class name is actuatorClass, second class would point to content 
    getTipClass: function(cls) {
        if (!cls) return ''; var c = '';
        var classes = cls.split(/\s+/);
        if ( classes[0] == this.actuatorClass && classes[1] ) {
            c = classes[1];
        }
        return c; // return second class name or ''
    },
    
    // This function inspects the object for properties corresponding to those allowed in default props 
    // Other properties might be set in the object which would be used by either the positioning or wrap functions 
    checkForProps: function(obj) {
        var list = ['adjustVert', 'sticky', 'duration', 'hoverable', 'followMouse', 'klass', 'positionFn', 'wrapFn'];
        for (var i=0; list[i]; i++) {
            if ( typeof obj[ list[i] ] != 'undefined' ) {
                this[ list[i] ] = obj[ list[i] ];
                //alert(this[ list[i] ])
                this.resetFlag = true;
            }
        }
    },

    getContentViaId: function(tgt) {
        var msg = '';
        var id = this.getTipClass(tgt.className);
        var el = document.getElementById(id);
        if (el) {
            msg = el.innerHTML;
        }
        return msg;
    },
    
    tipOutCheck: function(e) { // hover tip
        var _this = dw_Tooltip; e = dw_Event.DOMit(e);
        var tip = this; // assigned to onmouseover property of tip
        if ( dw_mouseleave(e, tip) ) {
            _this.timer = setTimeout( _this.hide, _this.hideDelay);
        }
    },

    // for sticky, duration, and onfocus activation
    checkKey: function(e) { // check for esc key
        e = e? e: window.event;  if ( e.keyCode == 27 ) dw_Tooltip.hide();
    },

    checkHide: function(e) { 
        if ( !dw_Tooltip.active ) return;
        var tgt = dw_Event.getTarget(e);
        // hide tooltip if you click anywhere in the document 
        // except on the tooltip, unless that click is on the tooltip's close box    
        var tip = document.getElementById(dw_Tooltip.tipID);
        if ( tgt == tip || dw_contained(tgt, tip) ) {
            if ( tgt.tagName && tgt.tagName.toLowerCase() == "img" ) tgt = tgt.parentNode; 
            if ( tgt.tagName.toLowerCase() != "a" || tgt.href.indexOf("dw_Tooltip.hide") != -1 ) return;
        }
        // slight delay to avoid crossing onfocus activation and doc click hide 
        dw_Tooltip.timer = setTimeout( dw_Tooltip.hide, 50);
    },
    
    // check need for and support of iframe shim (for ie win and select lists)
    checkOverlaySupport: function() {
        if ( navigator.userAgent.indexOf("Windows") != -1 && 
            typeof document.body != "undefined" && 
            typeof document.body.insertAdjacentHTML != "undefined" && 
            !window.opera && navigator.appVersion.indexOf("MSIE 5.0") == -1 
            ) return true;
        return false;
    }, 
    
    prepOverlay: function() {
        document.body.insertAdjacentHTML("beforeEnd", '<iframe id="tipShim" src="javascript: false" style="position:absolute; left:0; top:0; z-index:500; visibility:hidden" scrolling="no" frameborder="0"></iframe>');
        this.shim = document.getElementById('tipShim'); 
        if (this.shim && this.tip) {
            this.shim.style.width = this.tip.offsetWidth + "px";
            this.shim.style.height = this.tip.offsetHeight + "px";
        }
    },
    
    setOverlay: function() { // position and dimensions
        if ( this.shim ) {
            this.shim.style.left = this.tip.style.left;
            this.shim.style.top = this.tip.style.top;
            this.shim.style.width = this.tip.offsetWidth + "px";
            this.shim.style.height = this.tip.offsetHeight + "px";
        }
    },
    
    clearTimer: function(timer) {
        if ( dw_Tooltip[timer] ) { clearTimeout( dw_Tooltip[timer] ); dw_Tooltip[timer] = 0; }
    },
    
    getWidth: function() { return this.width = this.tip.offsetWidth; },
    getHeight: function() { return this.height = this.tip.offsetHeight; },
    getMaxX: function() { return dw_Viewport.width + dw_Viewport.scrollX - this.getWidth() - 1; },
    getMaxY: function() { return dw_Viewport.height + dw_Viewport.scrollY - this.getHeight() - 1; },
    getMouseEventX: function(e) { return e.pageX? e.pageX: e.clientX + dw_Viewport.scrollX; },
    getMouseEventY: function(e) { return e.pageY? e.pageY: e.clientY + dw_Viewport.scrollY; }
    
}

// Helper functions 
function dw_mouseleave(e, oNode) {
    e = dw_Event.DOMit(e);
    var toEl = e.relatedTarget? e.relatedTarget: e.toElement? e.toElement: null;
    if ( oNode != toEl && !dw_contained(toEl, oNode) ) {
        return true;
    }
    return false;
}

function dw_contained(oNode, oCont) {
    if (!oNode) return; // in case alt-tab away while hovering (prevent error)
    while ( oNode = oNode.parentNode ) if ( oNode == oCont ) return true;
    return false;
}

// Get position of element in page (treacherous cross-browser territory! Don't expect perfect results)
// can get weird results in ie
function dw_getPageOffsets(el) {
	var left = 0, top = 0;
    do {
        left += el.offsetLeft;
        top += el.offsetTop;
    } while (el = el.offsetParent);
    return { x:left, y:top };
}


