/*
 * ------------ common/js/facelift/flir.js ---------- 
 *
 */

/*
Facelift Image Replacement v1.2
Facelift was written and is maintained by Cory Mawhorter.  
It is available from http://facelift.mawhorter.net/

===

This file is part of Facelife Image Replacement ("FLIR").

FLIR is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

FLIR is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Facelift Image Replacement.  If not, see <http://www.gnu.org/licenses/>.
*/

var FLIR = {
     version: '1.2'
    
    ,options: {
         path: ''
        ,classnameIgnore: false
        ,findEmbededFonts: false
        ,ignoredElements: 'BR,HR,IMG,INPUT,SELECT'
    }
    
    ,onreplacing: null
    ,onreplaced: null
    ,onreplacingchild: null
    ,onreplacedchild: null
    
    ,flirElements: {}
    ,flirPlugins: []
    
    ,isCraptastic: true
    ,isIE: true

    ,defaultStyle: null
    ,classStyles: {}
        
    ,embededFonts: {}

    ,dpi: 96
    
    // either (options Object, fstyle FLIRStyle Object) or (fstyle FLIRStyle Object)
    ,init: function(options, fstyle) { // or options for flir style
        if(this.isFStyle(options)) { // (fstyle FLIRStyle Object)
            this.defaultStyle = options;
        }else { // [options Object, fstyle FLIRStyle Object]
            if(typeof options != 'undefined')
                this.loadOptions(options);
        
            if(typeof fstyle == 'undefined') {
                this.defaultStyle = new FLIRStyle();
            }else {
                if(this.isFStyle(fstyle))
                    this.defaultStyle = fstyle;
                else
                    this.defaultStyle = new FLIRStyle(fstyle);
            }
        }

        this.calcDPI();
                        
        if(this.options.findEmbededFonts)
            this.discoverEmbededFonts();

        this.isIE = (navigator.userAgent.toLowerCase().indexOf('msie')>-1 && navigator.userAgent.toLowerCase().indexOf('opera')<0);
        this.isCraptastic = (typeof document.body.style.maxHeight=='undefined');

        if(this.isIE) {
            this.flirIERepObj = [];
            this.flirIEHovEls = [];
            this.flirIEHovStyles = [];    
        }

        FLIR._call_plugin('init', arguments);
    }
    
    ,loadOptions: function(options) {
        for(var i in options)
            this.options[i] = options[i];
    }    
    
    ,installPlugin: function(plugin) {
        this.flirPlugins.push(plugin);
    }
    
    ,_call_plugin: function(func, call) {
        var ret = call;
        for(var i=0; i<this.flirPlugins.length; i++) {
            if(typeof this.flirPlugins[i][func] == 'function') {
                var pluginret = this.flirPlugins[i][func](ret);

                if(typeof pluginret == 'undefined') {
                    continue;
                }
                if(typeof pluginret == 'boolean' && pluginret == false) {
                    return false;
                }
                if(typeof pluginret != 'boolean') // passes changes on
                    ret = call;

            }
        }
        
        var ret = typeof ret != 'object' ? [ret] : ret;
        if(ret.length && ret[0] && ret[0].callee)
            return ret[0];
        else
            return ret;
    }
    
    ,auto: function(els) {
        if(!(args = FLIR._call_plugin('auto', arguments))) return;
        els = args[0];
        
        var tags = typeof els=='undefined'?['h1','h2','h3','h4','h5']:(els.indexOf && els.indexOf(',')>-1?els.split(','):els);
        var elements;
        for(var i=0; i<tags.length; i++) {
            elements = this.getElements(tags[i]);            

            if(elements.length>0)
                this.replace(elements);
        }
    }
    
    
    ,hover: function(e) {
        var o=FLIR.evsrc(e);
        var targ=o;
        var targDescHover = o.flirHasHover;
        var hoverTree = o;
        
        var on = (e.type == 'mouseover');
        
        while(o != document.body && !o.flirMainObj) {
            o = FLIR.getParentNode(o);
            
            if(!targDescHover) {
                    targDescHover = o.flirHasHover;
                    hoverTree = o;
            }
        }
        
        if(o==document.body) return;
        
        var FStyle = FLIR.getFStyle(o);
        if(on && FStyle != FStyle.hoverStyle)
            FStyle = FStyle.hoverStyle;
            
        if(!(args = FLIR._call_plugin('hover', [ on, targ, o, hoverTree ]))) return;
        on                = args[0];
        targ             = args[1];
        o                 = args[2];
        hoverTree     = args[3];
        
        var objs = FLIR.getChildren(hoverTree);
        if(objs.length == 0 || (objs.length == 1 && (objs[0].flirImage || objs[0].flirHasHover))) {
            objs = [hoverTree];
        }else if(objs.length == 1 && !FLIR.isIgnoredElement(objs[0])) {
            var subobjs = FLIR.getChildren(objs[0]);
            if(subobjs.length > 0)
                if((subobjs.length==1 && !subobjs[0].flirImage) || subobjs.length > 1)
                    objs = subobjs;
        }

        var rep_obj;
        for(var i=0; i < objs.length; i++) {
            rep_obj = objs[i];
            if(rep_obj.nodeName == 'IMG') continue;
            if(!rep_obj.innerHTML) continue; // IE 

            if(FLIR.isIE) {
                var idx = FLIR.flirIEHovEls.length;
                FLIR.flirIERepObj[idx] = rep_obj;
                FLIR.flirIEHovStyles[idx] = FStyle;
                
                if(!FLIR.isCraptastic) {
                    if(FStyle.useBackgroundMethod && FLIR.getStyle(rep_obj, 'display') == 'block') {
                        FLIR.flirIEHovEls[idx] = rep_obj;
                        setTimeout('FLIR.flirIERepObj['+idx+'].style.background = "url("+('+on+' ? FLIR.flirIEHovStyles['+idx+'].generateURL(FLIR.flirIERepObj['+idx+']) : FLIR.flirIERepObj['+idx+'].flirOrig)+") no-repeat";', 0);
                    }else {
                        FLIR.flirIEHovEls[idx] = rep_obj.flirImage ? rep_obj : FLIR.getChildren(rep_obj)[0];
                        if(!FLIR.flirIEHovEls[idx].flirOrigWidth) {
                            FLIR.flirIEHovEls[idx].flirOrigWidth = FLIR.flirIEHovEls[idx].width;
                            FLIR.flirIEHovEls[idx].flirOrigHeight = FLIR.flirIEHovEls[idx].height;
                        }
                        var ie_js = 'FLIR.flirIEHovEls['+idx+'].src = '+on+' ? FLIR.flirIEHovStyles['+idx+'].generateURL(FLIR.flirIERepObj['+idx+'], FLIR.flirIEHovEls['+idx+'].alt) : FLIR.flirIERepObj['+idx+'].flirOrig;'
                        ie_js += 'FLIR.flirIEHovEls['+idx+'].onload = function() { ';
                        if(on && !FLIR.flirIEHovEls[idx].flirHoverWidth) {
                            ie_js += '        FLIR.flirIEHovEls['+idx+'].flirHoverWidth = this.width; ';
                            ie_js += '        FLIR.flirIEHovEls['+idx+'].flirHoverHeight = this.height; ';
                        }
                        ie_js += '    this.style.width = FLIR.flirIEHovEls['+idx+'].'+(on?'flirHoverWidth':'flirOrigWidth')+'+"px"; ';
                        ie_js += '    this.style.height = FLIR.flirIEHovEls['+idx+'].'+(on?'flirHoverHeight':'flirOrigHeight')+'+"px"; ';
                        ie_js += '}; ';
                        setTimeout(ie_js, 0);
                    }
                }else {
                    FLIR.flirIEHovEls[idx] = rep_obj.flirImage ? rep_obj : FLIR.getChildren(rep_obj)[0];
                    setTimeout('  FLIR.flirIEHovEls['+idx+'].style.filter = \'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="\'+FLIR.flirIEHovStyles['+idx+'].generateURL(FLIR.flirIERepObj['+idx+'], FLIR.flirIEHovEls['+idx+'].alt)+\'", sizingMethod="image")\';  ', 0);
                }
            }else {
                if(FStyle.useBackgroundMethod && FLIR.getStyle(rep_obj, 'display') == 'block') {
                    var hovURL = rep_obj.flirHoverURL ? rep_obj.flirHoverURL : FStyle.generateURL(rep_obj);
                    rep_obj.style.background='url('+(on?hovURL:rep_obj.flirOrig)+') no-repeat';
                }else {
                    var img = rep_obj.flirImage ? rep_obj : FLIR.getChildren(rep_obj)[0];
                    var hovURL = rep_obj.flirHoverURL ? rep_obj.flirHoverURL : FStyle.generateURL(rep_obj, img.alt);
                    img.src = on?hovURL:rep_obj.flirOrig;
                }
            }
        }
    }

    ,addHover: function(obj) {
        if(!(args = FLIR._call_plugin('addHover', arguments))) return;
        obj    = args[0];
        
        obj.flirHasHover = true;
        
        if(obj.addEventListener) {
            obj.addEventListener( 'mouseover', FLIR.hover, false );
            obj.addEventListener( 'mouseout', FLIR.hover, false );
        }else if (obj.attachEvent) {
            obj.attachEvent( 'onmouseover', function() { FLIR.hover( window.event ); } );
            obj.attachEvent( 'onmouseout', function() { FLIR.hover( window.event ); } );
        }
    }
    
    ,prepare: function(n) {
        if(!(args = FLIR._call_plugin('prepare', arguments))) return;
        n = args[0];
        
        if(n && n.hasChildNodes() && n.childNodes.length > 1) {
            for(var i in n.childNodes) {
                var node = n.childNodes[i];
                if(node && node.nodeType == 3) {
                    var span = document.createElement('SPAN');
                    span.style.margin = span.style.padding = span.style.border = '0px';
                    span.className = 'flir-span';
						  span.flirSpan = true;
                    var txt = node.nodeValue.replace(/[\t\n\r]/g, ' ').replace(/\s\s+/g, ' ');
                    span.innerHTML = !FLIR.isIE ? txt : node.nodeValue.replace(/^\s+|\s+$/g,'&nbsp;');
                    n.replaceChild(span, node);
                }
            }
        }
    }
    
    ,replace: function(o, FStyle) {
        if(!(args = FLIR._call_plugin('replace', arguments))) return;
        o         = args[0];
        FStyle     = args[1];

        if (!o || o.flirReplaced)
            return;
        
        if(!this.isFStyle(FStyle))
            FStyle = this.getFStyle(o);

        if(typeof o == 'string')
            o = this.getElements(o);
        
        if(typeof o.length != 'undefined') {
            if(o.length == 0) return;

            for(var i=0; i< o.length; i++)
                this.replace(o[i], FStyle);
            
            return;
        }

        if(typeof FLIR.onreplacing == 'function') o = FLIR.onreplacing(o, FStyle);
        
        o.flirMainObj = true;
        this.setFStyle(o, FStyle);
        this.saveObject(o);
        
        if(this.options.findEmbededFonts && typeof this.embededFonts[FStyle.getFont(o)] != 'undefined')
            return;
        
        FLIR.prepare(o);        
        this._replace_tree(o, FStyle);

        if(typeof FLIR.onreplaced == 'function') FLIR.onreplaced(o, FStyle);
    }
    
    ,_replace_tree: function(o, FStyle) {
        if(typeof __flir_replacetree_recurse == 'undefined') __flir_replacetree_recurse = 1;
        else __flir_replacetree_recurse++;
        
        if(__flir_replacetree_recurse>1000) {
            console.error('Facelift: Too much recursion.');
            return;
        }
        
        var objs = !o.hasChildNodes() || (o.hasChildNodes() && o.childNodes.length==1 && o.childNodes[0].nodeType==3) ? [o] : o.childNodes;

        var rep_obj;
        for(var i=0; i < objs.length; i++) {
            rep_obj = objs[i];
            if(typeof FLIR.onreplacingchild == 'function') rep_obj = FLIR.onreplacingchild(rep_obj, FStyle);

            if(!rep_obj.innerHTML || rep_obj.nodeType != 1) continue;
            if(FLIR.isIgnoredElement(rep_obj)) continue;
            if(rep_obj.flirReplaced) continue;

            if(rep_obj.nodeName == 'A' && !rep_obj.flirHasHover)
                FLIR.addHover(rep_obj);

            if(rep_obj.hasChildNodes() && (rep_obj.childNodes.length > 1 || rep_obj.childNodes[0].nodeType != 3)) {
                FLIR.prepare(rep_obj);
                FLIR._replace_tree(rep_obj, FStyle);
                continue;
            }

            if(rep_obj.innerHTML == '') continue; // skip empty tags, if they exist
            
            if(!FLIR.isCraptastic)
                if(FStyle.useBackgroundMethod)
                    FLIR.replaceMethodBackground(rep_obj, FStyle);
                else
                    FLIR.replaceMethodOverlay(rep_obj, FStyle);
            else
                FLIR.replaceMethodCraptastic(rep_obj, FStyle);

            rep_obj.className += ' flir-replaced';
            rep_obj.flirReplaced = true;
            
            if(typeof FLIR.onreplacedchild == 'function') FLIR.onreplacedchild(o, FStyle);
        }
    }
    
    ,replaceMethodBackground: function(o, FStyle) {
        if(!(args = FLIR._call_plugin('replaceMethodBackground', arguments))) return;
        o         = args[0];
        FStyle     = args[1];

        var oid = this.saveObject(o);
        var url = FStyle.generateURL(o);
        
        if(FLIR.getStyle(o, 'display') != 'block')
            o.style.display='block';
        
        var tmp = new Image();
        tmp.onload = function() {
            FLIR.flirElements[oid].style.width=this.width+'px';
            FLIR.flirElements[oid].style.height=this.height+'px';
            
            if(FStyle != FStyle.hoverStyle) {
                var h_img = new Image();
                o.flirHoverURL = h_img.src = FStyle.hoverStyle.generateURL(o);
            }
        };
        tmp.src = url;
        
        o.style.background = 'url("'+url.replace(/ /g, '%20')+'") no-repeat';
        o.flirOrig = url;
        
        o.oldTextIndent = o.style.textIndent;
        o.style.textIndent='-9999px';
    }

    ,replaceMethodOverlay: function(o, FStyle) {
        if(!(args = FLIR._call_plugin('replaceMethodOverlay', arguments))) return;
        o         = args[0];
        FStyle     = args[1];

        var oid = this.saveObject(o);
        var img = document.createElement('IMG');
        img.alt = this.sanitizeHTML(o.innerHTML);

        if(FStyle != FStyle.hoverStyle) {
            img.onload = function() {
                    var h_img = new Image();
                    o.flirHoverURL = h_img.src = FStyle.hoverStyle.generateURL(o, img.alt);
            };
        }
        
        if(img.onerror) {
            img.onerror = function() {
                var span = document.createElement('SPAN');
                span.innerHTML = img.alt;
                try {
                    o.replaceChild(span,img)
                }catch(err) { }
            };
        }

        img.flirImage = true;
        img.className = 'flir-image';
        img.src = FStyle.generateURL(o);
        img.style.border='none';
        o.flirOrig = img.src;
        o.innerHTML='';
        o.appendChild(img);
    }

    ,replaceMethodCraptastic: function(o, FStyle) {
        if(!(args = FLIR._call_plugin('replaceMethodCraptastic', arguments))) return;
        o         = args[0];
        FStyle     = args[1];

        var oid = this.saveObject(o);
        var url = FStyle.generateURL(o);
        
        var img = document.createElement('IMG');
        img.alt = this.sanitizeHTML(o.innerHTML);        
        if(FStyle != FStyle.hoverStyle) {
            img.onload = function() {
                    var h_img = new Image();
                    o.flirHoverURL = h_img.src = FStyle.hoverStyle.generateURL(o, img.alt);
            };
        }

        img.flirImage = true;
        img.className = 'flir-image';
        img.src = this.options.path+'spacer.png';
        img.style.width=o.offsetWidth+'px';
        img.style.height=o.offsetHeight+'px';
        img.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+url+'", sizingMethod="image")';

        o.flirOrig = url;
        o.innerHTML='';
        o.appendChild(img);
    }

    ,saveObject: function(o) {
        if(typeof o.flirId == 'undefined') {
            o.flirId = this.getUID();
            this.flirElements[o.flirId] = o;
        }
        
        return o.flirId;
    }
    
    ,getUID: function() {
        var prefix='flir-';
        var id=prefix+Math.random().toString().split('.')[1];
        var i=0;
        while(typeof this.flirElements[id] != 'undefined') {
            if(i>100000) {
                console.error('Facelift: Unable to generate unique id.');    
            }
            id=prefix+Math.random().toString().split('.')[1];
            i++;
        }
        
        return id;
    }
    
    ,getElements: function(tag) {
        if(!(args = FLIR._call_plugin('getElements', arguments))) return;
        switch(args.length) {
            case 1:
                tag = args[0];
                break;
            case 2: // plugin returned list of elements
                return args[0];
                break;
        }
        
        var found = [];

        if(document.querySelectorAll) {
            var qsa = false;
            try{
                found = document.querySelectorAll(tag);
                qsa = true;
            }catch(err){ qsa=false; }

            if(qsa)
                return found;
        }

        var objs,subels,cn,childs,tag,el,matches,subel,rep_el;
    
        el = tag;
        
        subel=false;
		if(el.indexOf(' ')>-1) {
			var parts = el.split(' ');
			el = parts[0];
			subel = parts[1];
		}else if(el[0] == '#') {
			return document.getElementById(el.substr(1));
		}
        
        var grain_id=false;
        if(el.indexOf('#') > -1) {
            grain_id = el.split('#')[1];
            tag = el.split('#')[0];
        }

        var grain_cn=false;
        if(el.indexOf('.') > -1) {
            grain_cn = el.split('.')[1];
            tag = el.split('.')[0];
        }

        objs = document.getElementsByTagName(tag);
        for(var p=0; p<objs.length; p++) {
            if(objs[p].nodeType != 1) continue;
            matches = false;
            cn = objs[p].className?objs[p].className:'';
            
            if(grain_id && objs[p].id && objs[p].id == grain_id)
                matches=true;
            if(grain_cn && FLIR.hasClass(objs[p], grain_cn))
                matches=true;
            if(!grain_id && !grain_cn)
                matches=true;
            
            if(!matches) continue;
            if(this.options.classnameIgnore && cn.indexOf(this.options.classnameIgnore)>-1) continue;
            
            subels = false != subel ? objs[p].getElementsByTagName(subel) : [objs[p]];
            for(var pp=0; pp<subels.length; pp++) {
                rep_el = subels[pp];
                if(this.options.classnameIgnore && rep_el.className && rep_el.className.indexOf(this.options.classnameIgnore)>-1) continue;

                found.push(rep_el);
            }
        }
        
        return found;
    }
    
    ,discoverEmbededFonts: function() {
        this.embededFonts = {};
        for(var i in document.styleSheets) {
            if(!document.styleSheets[i].cssRules) continue;
            for(var ii in document.styleSheets[i].cssRules) {
                if(!document.styleSheets[0].cssRules[ii]) continue;
                var node = document.styleSheets[0].cssRules[ii];
                
                if(node.type && node.type == node.FONT_FACE_RULE) {
                    var nodesrc = node.style.getPropertyValue('src').match(/url\("?([^"\)]+\.[ot]tf)"?\)/i)[1];
                    var font = node.style.getPropertyValue('font-family');
                    if(font.indexOf(',')) {
                        font = font.split(',')[0];
                    }
                
                    font = font.replace(/['"]/g, '').toLowerCase();
                    
                    if(font!='' && nodesrc != '')
                        this.embededFonts[font] = nodesrc;
                }
            }
        }    
    }

    ,getStyle: function(el,prop) {
        if(el.currentStyle) {
            if(prop.indexOf('-') > -1)
                prop = prop.split('-')[0]+prop.split('-')[1].substr(0, 1).toUpperCase()+prop.split('-')[1].substr(1);
            var y = el.currentStyle[prop];
        }else if(window.getComputedStyle) {
            var y = document.defaultView.getComputedStyle(el,'').getPropertyValue(prop);
        }
        return y;
    }
        
    ,getChildren: function(n) {
        var children=[];
        if(n && n.hasChildNodes())
            for(var i in n.childNodes)
                if(n.childNodes[i] && n.childNodes[i].nodeType == 1)
                    children[children.length]=n.childNodes[i];
    
        return children;
    }
    
    ,getParentNode: function(n) {
        var o=n.parentNode;
        while(o != document && o.nodeType != 1)
            o=o.parentNode;
    
        return o;
    }
    
    ,hasClass: function(o, cn) {
        return (o && o.className && o.className.indexOf(cn)>-1);
    }
    
    ,evsrc: function(e) {
        var o;
        if (e.target) o = e.target;
        else if (e.srcElement) o = e.srcElement;
        if (o.nodeType == 3) // defeat Safari bug
            o = o.parentNode;    
            
        return o;
    }
    
    ,calcDPI: function() {
        if(screen.logicalXDPI) {
            var dpi = screen.logicalXDPI;
        }else {
            var id = 'flir-dpi-div-test';
            if(document.getElementById(id)) {
                var test = document.getElementById(id);
            }else {
                var test = document.createElement('DIV');
                test.id = id;
                test.style.position='absolute';
                test.style.visibility='hidden';
                test.style.border=test.style.padding=test.style.margin='0';
                test.style.left=test.style.top='-1000px';
                test.style.height=test.style.width='1in';
                document.body.appendChild(test);
            }
            
            var dpi = test.offsetHeight;
        }
        
        this.dpi = parseInt(dpi);
    }
    
    ,isIgnoredElement: function(el, breakIgnored) { return ((','+this.options.ignoredElements).indexOf(','+el.nodeName)>-1); }
    ,sanitizeHTML: function(html) { return html.replace(/<[^>]+>/g, ''); }
    
    ,getFStyle: function(o, fstyle) { 
        var cStyle = this.getClassStyle(o);
        if(this.isFStyle(cStyle))
            fstyle = cStyle;

        if(this.isFStyle(fstyle)) {
            return fstyle;
        }else if(this.isFStyle(o.flirStyle)) {
            return o.flirStyle;
        }else {
            return this.defaultStyle;
        }
    }
    ,setFStyle: function(o, FStyle) { o.flirStyle = FStyle; }
    ,isFStyle: function(o) { if(!o) return false; return (o.toString() == '[FLIRStyle Object]'); }

    ,addClassStyle: function(classname, FStyle) {
        if(this.isFStyle(FStyle))
            this.classStyles[classname] = FStyle;
    }
    ,getClassStyle: function(o) {
        if(!(args = FLIR._call_plugin('getClassStyle', arguments))) return;
        switch(args.length) {
            case 1:
                o = args[0];
                break;
            case 2: // plugin returned a style
                return args[0];
                break;
        }

        var cn = o.className;
        if(this.classStyles.length == 0 || typeof cn == 'undefined' || cn=='') return false;
        
        var classes = cn.split(' ');
        for(var i in this.classStyles) {
            for(var ii=0; ii<classes.length; ii++) {
                if(classes[ii]==i) {
                    return this.classStyles[i];
                }
            }
        }
        
        return false;
    }
};












function FLIRStyle(options) {
    this.useBackgroundMethod     = false;
    this.inheritStyle             = true;
    this.useExtendedStyles        = false;
    this.hoverStyle             = (arguments[1] && FLIR.isFStyle(arguments[1])) ? arguments[1] : this;
    
    // options are sent along with the query string
    this.options = {
         mode: '' // none (''), wrap,progressive or name of a plugin
        ,output:'auto' // auto, png, gif, jpg
        
        ,cSize: null
        ,cColor: null
        ,cFont: null // font-family
        
        ,realFontHeight: false
        ,dpi: 96
    };
    
    // supported css properties to internal name
    this.cssStyles = {
         'background-color'    : 'Background'
        ,'color'             : 'Color'
        ,'font-family'        : 'Font'
        ,'font-size'        : 'Size'
        ,'letter-spacing'    : 'Spacing'
        ,'line-height'        : 'Line'
        ,'text-align'        : 'Align'
        ,'text-transform'    : 'Transform'
    };
    
    this.extendedStyles = {
         'font-stretch'        : 'Stretch'
        ,'font-style'        : 'FontStyle'
        ,'font-variant'        : 'Variant'
        ,'font-weight'        : 'Weight'
        ,'opacity'            : 'Opacity'
        ,'text-decoration'    : 'Decoration'
    }
    
    // legacy option support
    for(var i in options) {
        if(i.indexOf('css')==0)
            i = 'c'+i.substr(3);

        if(typeof this[i] != 'undefined') {
            this[i] = options[i];
        }else {
            this.options[i] = options[i];
        }
    }
    this.options.dpi = FLIR.dpi;
    
    
    if(this.useExtendedStyles)
        for(var i in this.extendedStyles)
            this.cssStyles[i] = this.extendedStyles[i];
    
    for(var i=0; i<FLIR.flirPlugins.length; i++)
        if(FLIR.flirPlugins[i].FLIRStyleExtend && typeof FLIR.flirPlugins[i].FLIRStyleExtend.init)
            FLIR.flirPlugins[i].FLIRStyleExtend.init.call(this);
}

// generate a url based on an object
FLIRStyle.prototype.generateURL = function(o) { // [, text]
    var enc_text = (arguments[1]?arguments[1]:o.innerHTML);
    var transform = this.options.cTransform;
    if(transform==null)
        transform = FLIR.getStyle(o, 'text-transform');

    switch(transform) {
        case 'capitalize':
            enc_text = enc_text.replace(/\w+/g, function(w){
                              return w.charAt(0).toUpperCase() + w.substr(1).toLowerCase();
                         });
            break;
        case 'lowercase':
            enc_text = enc_text.toLowerCase();
            break;
        case 'uppercase':
            enc_text = enc_text.toUpperCase().replace(/&[a-z0-9]+;/gi, function(m) { return m.toLowerCase(); }); // keep entities lowercase, numeric don't matter
            break;
    }

    enc_text = encodeURIComponent(enc_text.replace(/&/g, '{amp}').replace(/\+/g, '{plus}'));

    return FLIR.options.path+'generate.php?text='+enc_text+'&h='+o.offsetHeight+'&w='+o.offsetWidth+'&fstyle='+this.serialize(o);
};

// create a custom image on the fly
FLIRStyle.prototype.buildURL = function(text, o, maxwidth, maxheight) {
    var enc_text = encodeURIComponent(text.replace(/&/g, '{amp}').replace(/\+/g, '{plus}'));
    return FLIR.options.path+'generate.php?text='+enc_text+'&h='+(maxheight?maxheight:'200')+'&w='+(maxwidth?maxwidth:'800')+'&fstyle='+(o?this.serialize(o):this.serialize());
};

FLIRStyle.prototype.serialize = function(o) {
    var sdata='';
    var options = this.copyObject(this.options);    
    
    if(o && this.inheritStyle) {
        for(var i in this.cssStyles) {
            var name = this.cssStyles[i];

            if(this.options['c'+name] == null || name=='Size')
                this.options['c'+name] = this.get(o, i, name);    
        }
    }
    
    for(var i in this.options) {
        if(this.options[i] == null || typeof this.options[i] == 'undefined' || this.options[i] == 'NaN')
            continue;
        sdata += ',"'+i+'":"'+this.options[i].toString().replace(/"/g, "'")+'"';
    }

    sdata = '{'+sdata.substr(1)+'}';
    this.options = options;

    return escape(sdata);
};

FLIRStyle.prototype.get = function(o, css_property, flirstyle_name) {
    var func = 'get'+flirstyle_name;
    
	 while(o.flirSpan && o != document.body)
	 	o = FLIR.getParentNode(o);
    
    return typeof this[func] == 'function' ? this[func](o) : FLIR.getStyle(o, css_property);
};

FLIRStyle.prototype.getFontStyle = function(o) { 
    return o.nodeName=='EM' || FLIR.getParentNode(o).nodeName=='EM' ? 'italic' : FLIR.getStyle(o, 'font-style');
};

FLIRStyle.prototype.getWeight = function(o) { 
    var fontweight = o.nodeName=='STRONG' || FLIR.getParentNode(o).nodeName=='STRONG' ? 'bold' : FLIR.getStyle(o, 'font-weight');
    
    switch(fontweight.toString()) {
        case '100': case '200': case '300': case 'lighter':
            return 'lighter';
        case '400': case 'normal':
            return '';
        case '500': case '600': case '700': case 'bold':
            return 'bold';
        case '800': case '900': case 'bolder':
            return 'bolder';
    }
};

FLIRStyle.prototype.getFont = function(o) { 
    var font = FLIR.getStyle(o, 'font-family');
    if(font.indexOf(',')) {
        font = font.split(',')[0];
    }

    return font.replace(/['"]/g, '').toLowerCase();
};

FLIRStyle.prototype.getColor = function(o) { 
    var color = FLIR.getStyle(o, 'color');
    if(color.substr(0, 1)=='#')
        color = color.substr(1);
    
    return color.replace(/['"]/g, '').toLowerCase();
};

FLIRStyle.prototype.getSize = function(o) {
    if(this.options.cSize!=null && '*/+-'.indexOf(this.options.cSize[0])<0)
        return this.options.cSize;
    
    var raw = FLIR.getStyle(o, 'font-size');

    var pix;
    if(raw.indexOf('px') > -1) {
        pix = Math.round(parseFloat(raw));
    }else {
        if(raw.indexOf('pt') > -1) {
            var pts = parseFloat(raw);
            pix = pts/(72/this.options.dpi);
        }else if(raw.indexOf('em') > -1 || raw.indexOf('%') > -1) {
            pix = this.calcFontSize(o);
        }
    }

    if(this.options.cSize && '*/+-'.indexOf(this.options.cSize[0])>-1) {
        try {
            pix = this.roundFloat(parseFloat(eval(pix.toString().concat(this.options.cSize))));
        }catch(err) { }
    }
    
    o.flirFontSize = pix;
    
    return pix;
};

FLIRStyle.prototype.getSpacing = function(o) {
    var spacing = FLIR.getStyle(o, 'letter-spacing');
    var ret;
    if(spacing != 'normal') {
        if(spacing.indexOf('em') > -1) {
            var fontsize = o.flirFontSize ? o.flirFontSize : this.getSize(o);
            ret = (parseFloat(spacing)*fontsize);
        }else if(spacing.indexOf('px') > -1) {
            ret = parseFloat(spacing);
        }else if(spacing.indexOf('pt') > -1) {
            var pts = parseFloat(spacing);
            ret = pts/(72/this.options.dpi);            
        }
        
        return this.roundFloat(ret);
    }

    return '';    
};

FLIRStyle.prototype.getLine = function(o) {
    var spacing = FLIR.getStyle(o, 'line-height');
    var val = parseFloat(spacing);
    var fontsize = o.flirFontSize ? o.flirFontSize : this.getSize(o);
    if(spacing.indexOf('em') > -1) {
        ret = (val*fontsize)/fontsize;
    }else if(spacing.indexOf('px') > -1) {
        ret = val/fontsize;
    }else if(spacing.indexOf('pt') > -1) {
        var pts = val;
        ret = (pts/(72/this.options.dpi))/fontsize;
    }else if(spacing.indexOf('%') > -1) {
        return 1.0;    
    }else {
        ret = val;    
    }
    
    return this.roundFloat(ret);
};

FLIRStyle.prototype.roundFloat = function(val) {
    return Math.round(val*10000)/10000;
};

FLIRStyle.prototype.calcFontSize = function(o) {
    var test = document.createElement('DIV');
    test.style.border = '0';
    test.style.padding = '0';
    test.style.position='absolute';
    test.style.visibility='hidden';
    test.style.left=test.style.top='-1000px';
    test.style.left=test.style.top='10px';
    test.style.lineHeight = '100%';
    test.innerHTML = 'Flir_Test';        
    o.appendChild(test);
    
    var size = test.offsetHeight;
    o.removeChild(test);

    return size;
};

FLIRStyle.prototype.copyObject = function(obj) { 
    var copy = {};
    for(var i in obj) {
        copy[i] = obj[i];    
    }
    
    return copy;
};

FLIRStyle.prototype.toString = function() { return '[FLIRStyle Object]'; };




/*
 * ------------ common/js/jdMenu/jquery.positionBy.js ---------- 
 *
 */

/*
 * positionBy 1.0.7 (2008-01-29)
 *
 * Copyright (c) 2006,2007 Jonathan Sharp (http://jdsharp.us)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://jdsharp.us/
 *
 * Built upon jQuery 1.2.2 (http://jquery.com)
 * This also requires the jQuery dimensions plugin
 */
(function($){
	/**
	 * This function centers an absolutely positioned element
	 */
	/*
	$.fn.positionCenter = function(offsetLeft, offsetTop) {
		var offsetLeft 	= offsetLeft || 1;
		var offsetTop 	= offsetTop || 1;

		var ww = $(window).width();
		var wh = $(window).height();
		var sl = $(window).scrollLeft();
		var st = $(window).scrollTop();

		return this.each(function() {
			var $t = $(this);
			
			// If we are not visible we have to display our element (with a negative position offscreen)

			var left = Math.round( ( ww - $t.outerWidth() ) / 2 );
			if ( left < 0 ) {
				left = 0;
			} else {
				left *= offsetLeft;
			}
			left += sl;
			var top  = Math.round( ( wh - $t.outerHeight() ) / 2 );
			if ( top < 0 ) {
				top = 0;
			} else {
				top *= offsetTop;
			}
			top += st;

			$(this).parents().each(function() {
				var $this = $(this);
				if ( $this.css('position') != 'static' ) {
					var o = $this.offset();
					left += -o.left;
					top	 += -o.top;
					return false;
				}
			});

			$t.css({left: left, top: top});
		});
	};
	*/
	
	// Our range object is used in calculating positions
	var Range = function(x1, y1, x2, y2) {
		this.x1	= x1;	this.x2 = x2;
		this.y1 = y1;	this.y2 = y2;
	};
	Range.prototype.contains = function(range) {
		return 	(this.x1 <= range.x1 && range.x2 <= this.x2) 
				&& 
				(this.y1 <= range.y1 && range.y2 <= this.y2);
	};
	Range.prototype.transform = function(x, y) {
		return new Range(this.x1 + x, this.y1 + y, this.x2 + x, this.y2 + y);
	};

	$.fn.positionBy = function(args) {
		var date1 = new Date();
		if ( this.length == 0 ) {
			return this;
		}
		
		var args = $.extend({	// The target element to position us relative to
								target:		null,
								// The target's corner, possible values 0-3
								targetPos:	null,
								// The element's corner, possible values 0-3
								elementPos:	null,
								
								// A raw x,y coordinate
								x:			null,
								y:			null,

								// Pass in an array of positions that are valid 0-15
								positions:	null,

								// Add the final position class to the element (eg. positionBy0 through positionBy3, positionBy15)
								addClass: 	false,
								
								// Force our element to be at the location we specified (don't try to auto position it)
								force: 		false,
								
								// The element that we will make sure our element doesn't go outside of
								container: 	window,

								// Should the element be hidden after positioning?
								hideAfterPosition: false
							}, args);

		if ( args.x != null ) {
			var tLeft	= args.x;
			var tTop	= args.y;
			var tWidth	= 0;
			var tHeight	= 0;
			
		// Position in relation to an element
		} else {
			var $target	= $( $( args.target )[0] );
			var tWidth	= $target.outerWidth();
			var tHeight	= $target.outerHeight();
			var tOffset	= $target.offset();
			var tLeft	= tOffset.left;
			var tTop	= tOffset.top;
		}

		// Our target right, bottom coord
		var tRight	= tLeft + tWidth;
		var tBottom	= tTop + tHeight;

		return this.each(function() {
			var $element = $( this );

			// Position our element in the top left so we can grab its width without triggering scrollbars
			if ( !$element.is(':visible') ) {
				$element.css({	left:  		-3000, 
								top: 		-3000
								})
								.show();
			}

			var eWidth	= $element.outerWidth();
			var eHeight	= $element.outerHeight();
	
			// Holds x1,y1,x2,y2 coordinates for a position in relation to our target element
			var position = [];
			// Holds a list of alternate positions to try if this one is not in the browser viewport
			var next	 = [];
	
			// Our Positions via ASCII ART
			/*
   	      	 8   9       10   11
			   +------------+
			 7 | 15      12 | 0
			   |            |
			 6 | 14      13 | 1
			   +------------+ 
			 5   4        3   2
	
			 */

			position[0]	= new Range(tRight, 			tTop, 				tRight + eWidth, 	tTop + eHeight);
			next[0]		= [1,7,4];
		
			position[1]	= new Range(tRight, 			tBottom - eHeight, 	tRight + eWidth, 	tBottom);
			next[1]		= [0,6,4];
		
			position[2] = new Range(tRight, 			tBottom,			tRight + eWidth, 	tBottom + eHeight);
			next[2]		= [1,3,10];
		
			position[3] = new Range(tRight - eWidth, 	tBottom,			tRight, 			tBottom + eHeight);
			next[3]		= [1,6,10];
			
			position[4] = new Range(tLeft, 				tBottom,			tLeft + eWidth, 	tBottom + eHeight);
			next[4]		= [1,6,9];
		
			position[5] = new Range(tLeft - eWidth, 	tBottom, 			tLeft, 				tBottom + eHeight);
			next[5]		= [6,4,9];
		
			position[6] = new Range(tLeft - eWidth, 	tBottom - eHeight,	tLeft, 				tBottom);
			next[6]		= [7,1,4];
			
			position[7] = new Range(tLeft - eWidth, 	tTop,				tLeft, 				tTop + eHeight);
			next[7]		= [6,0,4];
			
			position[8] = new Range(tLeft - eWidth, 	tTop - eHeight,		tLeft, 				tTop);
			next[8]		= [7,9,4];
			
			position[9] = new Range(tLeft, 				tTop - eHeight,		tLeft + eWidth, 	tTop);
			next[9]		= [0,7,4];
			
			position[10]= new Range(tRight - eWidth, 	tTop - eHeight,		tRight, 			tTop);
			next[10]	= [0,7,3];
		
			position[11]= new Range(tRight, 			tTop - eHeight, 	tRight + eWidth, 	tTop);
			next[11]	= [0,10,3];
			
			position[12]= new Range(tRight - eWidth, 	tTop,				tRight, 			tTop + eHeight);
			next[12]	= [13,7,10];
			
			position[13]= new Range(tRight - eWidth, 	tBottom - eHeight,	tRight, 			tBottom);
			next[13]	= [12,6,3];
			
			position[14]= new Range(tLeft, 				tBottom - eHeight,	tLeft + eWidth, 	tBottom);
			next[14]	= [15,1,4];
			
			position[15]= new Range(tLeft, 				tTop,				tLeft + eWidth, 	tTop + eHeight);
			next[15]	= [14,0,9];
	
			if ( args.positions !== null ) {
				var pos = args.positions[0];
			} else if ( args.targetPos != null && args.elementPos != null ) {
				var pos = [];
				pos[0] = [];
				pos[0][0] = 15;
				pos[0][1] = 7;
				pos[0][2] = 8;
				pos[0][3] = 9;
				pos[1] = [];
				pos[1][0] = 0;
				pos[1][1] = 12;
				pos[1][2] = 10;
				pos[1][3] = 11;
				pos[2] = [];
				pos[2][0] = 2;
				pos[2][1] = 3;
				pos[2][2] = 13;
				pos[2][3] = 1;
				pos[3] = [];
				pos[3][0] = 4;
				pos[3][1] = 5;
				pos[3][2] = 6;
				pos[3][3] = 14;

				var pos = pos[args.targetPos][args.elementPos];
			}
			var ePos = position[pos];
			var fPos = pos;

			if ( !args.force ) {
				// TODO: Do the args.container
				// window width & scroll offset
				$window = $( window );
				var sx = $window.scrollLeft();
				var sy = $window.scrollTop();
				
				// TODO: Look at innerWidth & innerHeight
				var container = new Range( sx, sy, sx + $window.width(), sy + $window.height() );
	
				// If we are outside of our viewport, see if we are outside vertically or horizontally and push onto the stack
				var stack;
				if ( args.positions ) {
					stack = args.positions;
				} else {
					stack = [pos];
				}
				var test = [];		// Keeps track of our positions we already tried
				
				while ( stack.length > 0 ) {
					var p = stack.shift();
					if ( test[p] ) {
						continue;
					}
					test[p] = true;
	
					// If our current position is not within the viewport (eg. window) 
					// add the next suggested position
					if ( !container.contains(position[p]) ) {
						if ( args.positions === null ) {
							stack = jQuery.merge( stack, next[p] );
						}
					} else {
						ePos = position[p];
						break;
					}
				}
			}

			// + TODO: Determine if we are going to use absolute left, top, bottom, right 
			// positions relative to our target
		
			// Take into account any absolute or fixed positioning
			// to 'normalize' our coordinates
			$element.parents().each(function() {
				var $this = $(this);
				if ( $this.css('position') != 'static' ) {
					var abs = $this.offset();
					ePos = ePos.transform( -abs.left, -abs.top );
					return false;
				}
			});
		
			// Finally position our element
			var css = { left: ePos.x1, top: ePos.y1 };
			if ( args.hideAfterPosition ) {
				css['display'] = 'none';
			}
			$element.css( css );

			if ( args.addClass ) {
				$element.removeClass( 'positionBy0 positionBy1 positionBy2 positionBy3 positionBy4 positionBy5 '
									+ 'positionBy6 positionBy7 positionBy8 positionBy9 positionBy10 positionBy11 '
									+ 'positionBy12 positionBy13 positionBy14 positionBy15')
						.addClass('positionBy' + p);
			}
		});
	};
})(jQuery);




/*
 * ------------ common/js/jdMenu/jquery.bgiframe.js ---------- 
 *
 */

/* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * $LastChangedDate$
 * $Rev$
 *
 * Version 2.1.1
 */

(function($){

/**
 * The bgiframe is chainable and applies the iframe hack to get 
 * around zIndex issues in IE6. It will only apply itself in IE6 
 * and adds a class to the iframe called 'bgiframe'. The iframe
 * is appeneded as the first child of the matched element(s) 
 * with a tabIndex and zIndex of -1.
 * 
 * By default the plugin will take borders, sized with pixel units,
 * into account. If a different unit is used for the border's width,
 * then you will need to use the top and left settings as explained below.
 *
 * NOTICE: This plugin has been reported to cause perfromance problems
 * when used on elements that change properties (like width, height and
 * opacity) a lot in IE6. Most of these problems have been caused by 
 * the expressions used to calculate the elements width, height and 
 * borders. Some have reported it is due to the opacity filter. All 
 * these settings can be changed if needed as explained below.
 *
 * @example $('div').bgiframe();
 * @before <div><p>Paragraph</p></div>
 * @result <div><iframe class="bgiframe".../><p>Paragraph</p></div>
 *
 * @param Map settings Optional settings to configure the iframe.
 * @option String|Number top The iframe must be offset to the top
 * 		by the width of the top border. This should be a negative 
 *      number representing the border-top-width. If a number is 
 * 		is used here, pixels will be assumed. Otherwise, be sure
 *		to specify a unit. An expression could also be used. 
 * 		By default the value is "auto" which will use an expression 
 * 		to get the border-top-width if it is in pixels.
 * @option String|Number left The iframe must be offset to the left
 * 		by the width of the left border. This should be a negative 
 *      number representing the border-left-width. If a number is 
 * 		is used here, pixels will be assumed. Otherwise, be sure
 *		to specify a unit. An expression could also be used. 
 * 		By default the value is "auto" which will use an expression 
 * 		to get the border-left-width if it is in pixels.
 * @option String|Number width This is the width of the iframe. If
 *		a number is used here, pixels will be assume. Otherwise, be sure
 * 		to specify a unit. An experssion could also be used.
 *		By default the value is "auto" which will use an experssion
 * 		to get the offsetWidth.
 * @option String|Number height This is the height of the iframe. If
 *		a number is used here, pixels will be assume. Otherwise, be sure
 * 		to specify a unit. An experssion could also be used.
 *		By default the value is "auto" which will use an experssion
 * 		to get the offsetHeight.
 * @option Boolean opacity This is a boolean representing whether or not
 * 		to use opacity. If set to true, the opacity of 0 is applied. If
 *		set to false, the opacity filter is not applied. Default: true.
 * @option String src This setting is provided so that one could change 
 *		the src of the iframe to whatever they need.
 *		Default: "javascript:false;"
 *
 * @name bgiframe
 * @type jQuery
 * @cat Plugins/bgiframe
 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
 */
$.fn.bgIframe = $.fn.bgiframe = function(s) {
	// This is only for IE6
	if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
		s = $.extend({
			top     : 'auto', // auto == .currentStyle.borderTopWidth
			left    : 'auto', // auto == .currentStyle.borderLeftWidth
			width   : 'auto', // auto == offsetWidth
			height  : 'auto', // auto == offsetHeight
			opacity : true,
			src     : 'javascript:false;'
		}, s || {});
		var prop = function(n){return n&&n.constructor==Number?n+'px':n;},
		    html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
		               'style="display:block;position:absolute;z-index:-1;'+
			               (s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
					       'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
					       'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
					       'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
					       'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
					'"/>';
		return this.each(function() {
			if ( $('> iframe.bgiframe', this).length == 0 )
				this.insertBefore( document.createElement(html), this.firstChild );
		});
	}
	return this;
};

})(jQuery);



/*
 * ------------ common/js/jdMenu/jquery.jdMenu.js ---------- 
 *
 */

/*
 * jdMenu 1.4.1 (2008-03-31)
 *
 * Copyright (c) 2006,2007 Jonathan Sharp (http://jdsharp.us)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://jdsharp.us/
 *
 * Built upon jQuery 1.2.1 (http://jquery.com)
 * This also requires the jQuery dimensions >= 1.2 plugin
 */

// This initializes the menu
$(function() {
	$('ul.jd_menu').jdMenu();
});

(function($){
	function addEvents(ul) {
		var settings = $.data( $(ul).parents().andSelf().filter('ul.jd_menu')[0], 'jdMenuSettings' );
		$('> li', ul)
			.bind('mouseenter.jdmenu mouseleave.jdmenu', function(evt) {
				$(this).toggleClass('jdm_hover');
				var ul = $('> ul', this);
				if ( ul.length == 1 ) {
					clearTimeout( this.$jdTimer );
					var enter = ( evt.type == 'mouseenter' );
					var fn = ( enter ? showMenu : hideMenu );
					this.$jdTimer = setTimeout(function() {
						fn( ul[0], settings.onAnimate, settings.isVertical );
					}, enter ? settings.showDelay : settings.hideDelay );
				}
			})
			.bind('click.jdmenu', function(evt) {
				var ul = $('> ul', this);
				if ( ul.length == 1 && 
					( settings.disableLinks == true || $(this).hasClass('accessible') ) ) {
					showMenu( ul, settings.onAnimate, settings.isVertical );
					return false;
				}
				
				// The user clicked the li and we need to trigger a click for the a
				if ( evt.target == this ) {
					var link = $('> a', evt.target).not('.accessible');
					if ( link.length > 0 ) {
						var a = link[0];
						if ( !a.onclick ) {
							window.open( a.href, a.target || '_self' );
						} else {
							$(a).trigger('click');
						}
					}
				}
				if ( settings.disableLinks || 
					( !settings.disableLinks && !$(this).parent().hasClass('jd_menu') ) ) {
					$(this).parent().jdMenuHide();
					evt.stopPropagation();
				}
			})
			.find('> a')
				.bind('focus.jdmenu blur.jdmenu', function(evt) {
					var p = $(this).parents('li:eq(0)');
					if ( evt.type == 'focus' ) {
						p.addClass('jdm_hover');
					} else { 
						p.removeClass('jdm_hover');
					}
				})
				.filter('.accessible')
					.bind('click.jdmenu', function(evt) {
						evt.preventDefault();
					});
	}

	function showMenu(ul, animate, vertical) {
		var ul = $(ul);
		if ( ul.is(':visible') ) {
			return;
		}
		ul.bgiframe();
		var li = ul.parent();
		ul	.trigger('jdMenuShow')
			.positionBy({ 	target: 	li[0], 
							targetPos: 	( vertical === true || !li.parent().hasClass('jd_menu') ? 1 : 3 ), 
							elementPos: 0,
							hideAfterPosition: true
							});
		if ( !ul.hasClass('jdm_events') ) {
			ul.addClass('jdm_events');
			addEvents(ul);
		}
		li	.addClass('jdm_active')
			// Hide any adjacent menus
			.siblings('li').find('> ul:eq(0):visible')
				.each(function(){
					hideMenu( this ); 
				});
		if ( animate === undefined ) {
			ul.show();
		} else {
			animate.apply( ul[0], [true] );
		}
	}
	
	function hideMenu(ul, animate) {
		var ul = $(ul);
		$('.bgiframe', ul).remove();
		ul	.filter(':not(.jd_menu)')
			.find('> li > ul:eq(0):visible')
				.each(function() {
					hideMenu( this );
				})
			.end();
		if ( animate === undefined ) {
			ul.hide()
		} else {
			animate.apply( ul[0], [false] );
		}

		ul	.trigger('jdMenuHide')
			.parents('li:eq(0)')
				.removeClass('jdm_active jdm_hover')
			.end()
				.find('> li')
				.removeClass('jdm_active jdm_hover');
	}
	
	// Public methods
	$.fn.jdMenu = function(settings) {
		// Future settings: activateDelay
		var settings = $.extend({	// Time in ms before menu shows
									showDelay: 		200,
									// Time in ms before menu hides
									hideDelay: 		500,
									// Should items that contain submenus not 
									// respond to clicks
									disableLinks:	false
									// This callback allows for you to animate menus
									//onAnimate:	null
									}, settings);
		if ( !$.isFunction( settings.onAnimate ) ) {
			settings.onAnimate = undefined;
		}
		return this.filter('ul.jd_menu').each(function() {
			$.data(	this, 
					'jdMenuSettings', 
					$.extend({ isVertical: $(this).hasClass('jd_menu_vertical') }, settings) 
					);
			addEvents(this);
		});
	};
	
	$.fn.jdMenuUnbind = function() {
		$('ul.jdm_events', this)
			.unbind('.jdmenu')
			.find('> a').unbind('.jdmenu');
	};
	$.fn.jdMenuHide = function() {
		return this.filter('ul').each(function(){ 
			hideMenu( this );
		});
	};

	// Private methods and logic
	$(window)
		// Bind a click event to hide all visible menus when the document is clicked
		.bind('click.jdmenu', function(){
			$('ul.jd_menu ul:visible').jdMenuHide();
		});
})(jQuery);





