﻿/****************************************************************************************
* Overlay Javascript file
* 
* Creates an overlay element for use in presenting modal content
*/
/****************************************************************************************
 * Brighthouse initialization code
 */

bhn['overlay'] = {}
bhn['overlay'].options = {
    cssclass: 'rel',
    filter: '.modal-container',
    unloadOnHide: true,
    modal: true,
    title: ' ',
    closeText: ' ',
    afterShow: function() {
        $('.overlay-wrapper select').sSelect({
            ddMaxHeight: 260,
            ddMinWidth: 139,
            ddMaxWidth: 310,
            ulWidthOffset: 8
        });
        $('.overlay-content .tab-control').tabcontrol({ tabpadding: 3 });
        // instantiates solo-input validation 
        $('.input-solo').restrict('input[name=zipcode]','zipcode')
        // instantiates select validation 
        $('form[name=area_form]').restrict('select','select')
        $('.overlay-wrapper a').each(function(){
            var $this = $(this);
            var appURL = $this.attr('href');
            // Accomodate bug in IE8 on windows 7
            if(appURL.indexOf('applications.') >= 0){
                var pos1 = appURL.indexOf('applications.');
                appURL = appURL.substring(pos1);
                var pos2 = appURL.indexOf('/');
                var target = appURL.substring(0,pos2);
                appURL = appURL.replace(target ,cmsBaseURL);
                $this.attr('href', appURL);
            };
        })
    },
    beforeUnload: function() {
        overlayState = null;
    }
}
$(document).ready(function() {
    // Dynamically load css
    $("head").addStyleSheet(cmsBaseURL + "/static/stylesheets/jquery-overlay.css");
   
    // instantiate all common modal links
    $('a[rel*=modal]').overlay(bhn['overlay'].options)
    
})
$.errorModal = function(title, message) {
    new Overlay(message, { type: "error", title: title })
}

/****************************************************************************************
* Brighthouse Development notes:
*
* 10/12/2010 - JMP
* Created intelligence for modal content accessed via ajax from a different domain or subdomain.
* See ajax error callback in "load: function".
*
* 1/11/2010 - JMP
* Created support for media lightbox functionality.
*
* 12/23/2009 - JMP
* Created a default implementation for simple error messaging.
*
* 12/22/2009 - JMP
* Extended "cssclass" functionality to inherit rel attribute value when 'rel' is assigned.
*
* 8/31/2009 - JMP
* Corrected issue with filtering of ajax return html. See "load: function"
*
* 8/28/2009 - JMP 
* Added the ability to assign a unique css class to the dialog via the option "cssclass".
* See "function Overlay()"
*/
// Largely based on the following plugin:
/****************************************************************************************
* Boxy 0.1.4 - Facebook-style dialog, with frills
* URL: http://onehackoranother.com/projects/jquery/overlay/
* (c) 2008 Jason Frame
* Licensed under the MIT License (LICENSE)
*
*
* Options:
*   message: confirmation message for form submit hook (default: "Please confirm:")
* 
* Any other options - e.g. 'clone' - will be passed onto the overlay constructor (or
* Overlay.load for AJAX operations)
*/

jQuery.fn.overlay = function(options) {
    options = options || {};
    return this.each(function() {      
        var node = this.nodeName.toLowerCase(), self = this;
        if (node == 'a') {
            jQuery(this).click(function() {
                var active = Overlay.linkedTo(this),
                    href = this.getAttribute('href'),
                    localOptions = jQuery.extend({actuator: this, title: this.title}, options);
                if (options.cssclass) {
                    if (options.cssclass == 'rel') {
                        localOptions.cssclass = $(this).attr('rel');
                    }
                }
                if (active) {
                    active.show();
                } else if (href.indexOf('#') >= 0) {
                    var content = jQuery(href.substr(href.indexOf('#'))),
                        newContent = content.clone(true);
                    // content.remove();
                    //localOptions.unloadOnHide = false;
                    new Overlay(newContent, localOptions);
                } else { // fall back to AJAX; could do with a same-origin check
                    if (!localOptions.cache) localOptions.unloadOnHide = true;
                    Overlay.load(this.href, localOptions);
                }
                
                return false;
            });
        } else if (node == 'form') {
            jQuery(this).bind('submit.overlay', function() {
                Overlay.confirm(options.message || 'Please confirm:', function() {
                    jQuery(self).unbind('submit.overlay').submit();
                });
                return false;
            });
        }
    });
};
//
// Overlay Class
function Overlay(element, options) {
    
    this.overlay = jQuery(Overlay.WRAPPER);
    jQuery.data(this.overlay[0], 'overlay', this);
    
    this.visible = false;
    this.options = jQuery.extend({}, Overlay.DEFAULTS, options || {});
    // Used to define overlay types and their default values
    if (options.type) {
        switch (this.options.type) {
            case 'error':
                element = "<div class='error'><div class='message'>" + element + "</div><button class='submitbtn close'><span>Ok</span></button></div>";
                this.options.cssclass = 'modal-alt small';
                this.options.unloadOnHide = true;
                this.options.modal = true;
                this.options.closeText = ' ';
            break
            case 'lightbox':
                this.options.cssclass = 'modal-lightbox';
                this.options.unloadOnHide = true;
                this.options.modal = true;
                this.options.closeText = ' ';
            break
            case 'modal':
                this.options.cssclass = 'modal';
                this.options.unloadOnHide = true;
                this.options.modal = true;
                this.options.closeText = ' ';
            break
        }
    }
    
    
    if (this.options.modal) {
        this.options = jQuery.extend(this.options, {center: true, draggable: false});
    }
    
    // options.actuator == DOM element that opened this overlay
    // association will be automatically deleted when this overlay is remove()d
    if (this.options.actuator) {
        jQuery.data(this.options.actuator, 'active.overlay', this);
    }
    
    this.setContent(element || "<div></div>");
    this._setupTitleBar();
    
    this.overlay.css('display', 'none').appendTo(document.body);
    this.toTop();
    if (this.options.fixed) {
        if (jQuery.browser.msie && jQuery.browser.version < 7) {
            this.options.fixed = false; // IE6 doesn't support fixed positioning
        } else {
            this.overlay.addClass('fixed');
        }
    }
    
    if (this.options.cssclass) this.overlay.addClass(this.options.cssclass);
    
    if (this.options.center && Overlay._u(this.options.x, this.options.y)) {
        this.center();
    } else {
        this.moveTo(
            Overlay._u(this.options.x) ? this.options.x : Overlay.DEFAULT_X,
            Overlay._u(this.options.y) ? this.options.y : Overlay.DEFAULT_Y
        );
    }
    if (this.options.show) this.show();

};
Overlay.EF = function() {};
jQuery.extend(Overlay, {
    
    WRAPPER:    "<table cellspacing='0' cellpadding='0' border='0' class='overlay-wrapper'>" +
                "<tr><td class='top-left'></td><td class='top'></td><td class='top-right'></td></tr>" +
                "<tr><td class='left'></td><td class='overlay-inner'></td><td class='right'></td></tr>" +
                "<tr><td class='bottom-left'></td><td class='bottom'></td><td class='bottom-right'></td></tr>" +
                "</table>",
    
    DEFAULTS: {
        title:                  null,           // titlebar text. titlebar will not be visible if not set.
        closeable:              true,           // display close link in titlebar?
        draggable:              true,           // can this dialog be dragged?
        clone:                  false,          // clone content prior to insertion into dialog?
        actuator:               null,           // element which opened this dialog
        center:                 true,           // center dialog in viewport?
        show:                   true,           // show dialog immediately?
        modal:                  false,          // make dialog modal?
        fixed:                  true,           // use fixed positioning, if supported? absolute positioning used otherwise
        closeText:              '[close]',      // text to use for default close link
        unloadOnHide:           false,          // should this dialog be removed from the DOM after being hidden?
        clickToFront:           false,          // bring dialog to foreground on any click (not just titlebar)?
        behaviours:             Overlay.EF,        // function used to apply behaviours to all content embedded in dialog.
        afterDrop:              Overlay.EF,        // callback fired after dialog is dropped. executes in context of Overlay instance.
        afterShow:              Overlay.EF,        // callback fired after dialog becomes visible. executes in context of Overlay instance.
        afterHide:              Overlay.EF,        // callback fired after dialog is hidden. executed in context of Overlay instance.
        beforeUnload:           Overlay.EF         // callback fired after dialog is unloaded. executed in context of Overlay instance.
    },
    
    DEFAULT_X:          50,
    DEFAULT_Y:          50,
    zIndex:             1337,
    dragConfigured:     false, // only set up one drag handler for all overlays
    resizeConfigured:   false,
    dragging:           null,
    
    // load a URL and display in overlay
    // url - url to load
    // options keys (any not listed below are passed to overlay constructor)
    //   type: HTTP method, default: GET
    //   cache: cache retrieved content? default: false
    //   filter: jQuery selector used to filter remote content
    load: function(url, options) {
        options = options || {};
    
        var ajax = {
            url: url, type: 'GET', dataType: 'html', cache: false, 
            success: function(html) {
                html = jQuery(html);
                if (options.filter){
                    content = html.filter(options.filter);
                    if (!content.html()) content = html.find(options.filter);
                    if (content) html = content;
                }
                new Overlay(html, options);
            },
            error: function(XMLHttpRequest, textStatus, error){
                // if ajax call fails (usually due to cross-site security)
                var id = url.split('/').pop();
                var el = $('#'+id);
                if(el.html()){
                // if an element has an id that matches the page name of the modal, use it for modal content
                    new Overlay(el.html(), options);
                }else{
                // provide notice of error
                    alert(status + ' : ' + error + ' while accessing '+ url +' and no local element found.');
                }  
            }
        };
        
        jQuery.each(['type', 'cache'], function() {
            if (this in options) {
                ajax[this] = options[this];
                delete options[this];
            }
        });

        try{
            jQuery.ajax(ajax);
        }catch(error){
            // if ajax call fails but error event not triggered
            var id = url.split('/').pop();
            var el = $('#'+id).clone();
            if(el.html()){
            // if an element has an id that matches the page name of the modal, use it for modal content
                new Overlay(el, options);
            }else{
            // provide notice of error
                alert(status + ' : ' + error + ' while accessing '+ url +' and no local element found.');
            }  
        }
        
    },
    
    // allows you to get a handle to the containing overlay instance of any element
    // e.g. <a href='#' onclick='alert(Overlay.get(this));'>inspect!</a>.
    // this returns the actual instance of the overlay 'class', not just a DOM element.
    // Overlay.get(this).hide() would be valid, for instance.
    get: function(ele) {
        var p = jQuery(ele).parents('.overlay-wrapper');
        return p.length ? jQuery.data(p[0], 'overlay') : null;
    },
    
    // returns the overlay instance which has been linked to a given element via the
    // 'actuator' constructor option.
    linkedTo: function(ele) {
        return jQuery.data(ele, 'active.overlay');
    },
    
    // displays an alert box with a given message, calling optional callback
    // after dismissal.
    alert: function(message, callback, options) {
        return Overlay.ask(message, ['OK'], callback, options);
    },
    
    // displays an alert box with a given message, calling after callback iff
    // user selects OK.
    confirm: function(message, after, options) {
        return Overlay.ask(message, ['OK', 'Cancel'], function(response) {
            if (response == 'OK') after();
        }, options);
    },
    
    // asks a question with multiple responses presented as buttons
    // selected item is returned to a callback method.
    // answers may be either an array or a hash. if it's an array, the
    // the callback will received the selected value. if it's a hash,
    // you'll get the corresponding key.
    ask: function(question, answers, callback, options) {
        
        options = jQuery.extend({modal: true, closeable: false},
                                options || {},
                                {show: true, unloadOnHide: true});
        
        var body = jQuery('<div></div>').append(jQuery('<div class="question"></div>').html(question));
        
        // ick
        var map = {}, answerStrings = [];
        if (answers instanceof Array) {
            for (var i = 0; i < answers.length; i++) {
                map[answers[i]] = answers[i];
                answerStrings.push(answers[i]);
            }
        } else {
            for (var k in answers) {
                map[answers[k]] = k;
                answerStrings.push(answers[k]);
            }
        }
        
        var buttons = jQuery('<form class="answers"></form>');
        buttons.html(jQuery.map(answerStrings, function(v) {
            return "<input type='button' value='" + v + "' />";
        }).join(' '));
        
        jQuery('input[type=button]', buttons).click(function() {
            var clicked = this;
            Overlay.get(this).hide(function() {
                if (callback) callback(map[clicked.value]);
            });
        });
        
        body.append(buttons);
        
        new Overlay(body, options);
        
    },
    
    // returns true if a modal overlay is visible, false otherwise
    isModalVisible: function() {
        return jQuery('.overlay-modal-blackout').length > 0;
    },
    
    _u: function() {
        for (var i = 0; i < arguments.length; i++)
            if (typeof arguments[i] != 'undefined') return false;
        return true;
    },
    
    _handleResize: function(evt) {
        var d = jQuery(document);
        jQuery('.overlay-modal-blackout').css('display', 'none').css({
            width: d.width(), height: d.height()
        }).css('display', 'block');
    },
    
    _handleDrag: function(evt) {
        var d;
        if (d = Overlay.dragging) {
            d[0].overlay.css({left: evt.pageX - d[1], top: evt.pageY - d[2]});
        }
    },
    
    _nextZ: function() {
        return Overlay.zIndex++;
    },
    
    _viewport: function() {
        var d = document.documentElement, b = document.body, w = window;
        return jQuery.extend(
            jQuery.browser.msie ?
                { left: b.scrollLeft || d.scrollLeft, top: b.scrollTop || d.scrollTop } :
                { left: w.pageXOffset, top: w.pageYOffset },
            !Overlay._u(w.innerWidth) ?
                { width: w.innerWidth, height: w.innerHeight } :
                (!Overlay._u(d) && !Overlay._u(d.clientWidth) && d.clientWidth != 0 ?
                    { width: d.clientWidth, height: d.clientHeight } :
                    { width: b.clientWidth, height: b.clientHeight }) );
    }
});
Overlay.prototype = {
    
    // Returns the size of this overlay instance without displaying it.
    // Do not use this method if overlay is already visible, use getSize() instead.
    estimateSize: function() {
        this.overlay.css({visibility: 'hidden', display: 'block'});
        var dims = this.getSize();
        this.overlay.css('display', 'none').css('visibility', 'visible');
        return dims;
    },
                
    // Returns the dimensions of the entire overlay dialog as [width,height]
    getSize: function() {
        return [this.overlay.width(), this.overlay.height()];
    },
    
    // Returns the dimensions of the content region as [width,height]
    getContentSize: function() {
        var c = this.getContent();
        return [c.width(), c.height()];
    },
    
    // Returns the position of this dialog as [x,y]
    getPosition: function() {
        var b = this.overlay[0];
        return [b.offsetLeft, b.offsetTop];
    },
    
    // Returns the center point of this dialog as [x,y]
    getCenter: function() {
        var p = this.getPosition();
        var s = this.getSize();
        return [Math.floor(p[0] + s[0] / 2), Math.floor(p[1] + s[1] / 2)];
    },
                
    // Returns a jQuery object wrapping the inner overlay region.
    // Not much reason to use this, you're probably more interested in getContent()
    getInner: function() {
        return jQuery('.overlay-inner', this.overlay);
    },
    
    // Returns a jQuery object wrapping the overlay content region.
    // This is the user-editable content area (i.e. excludes titlebar)
    getContent: function() {
        return jQuery('.overlay-content', this.overlay);
    },
    
    // Replace dialog content
    setContent: function(newContent) {
        newContent = jQuery(newContent).css({display: 'block'}).addClass('overlay-content');
        if (this.options.clone) newContent = newContent.clone(true);
        this.getContent().remove();
        this.getInner().append(newContent);
        this._setupDefaultBehaviours(newContent);
        this.options.behaviours.call(this, newContent);
        return this;
    },
    
    // Move this dialog to some position, funnily enough
    moveTo: function(x, y) {
        this.moveToX(x).moveToY(y);
        return this;
    },
    
    // Move this dialog (x-coord only)
    moveToX: function(x) {
        if (typeof x == 'number') this.overlay.css({left: x});
        else this.centerX();
        return this;
    },
    
    // Move this dialog (y-coord only)
    moveToY: function(y) {
        if (typeof y == 'number') {
            y = Math.max(y, 10);
            this.overlay.css({ top: y });
        }
        else this.centerY();
        return this;
    },
    
    // Move this dialog so that it is centered at (x,y)
    centerAt: function(x, y) {
        var s = this[this.visible ? 'getSize' : 'estimateSize']();
        if (typeof x == 'number') this.moveToX(x - s[0] / 2);
        if (typeof y == 'number') this.moveToY(y - s[1] / 2);
        return this;
    },
    
    centerAtX: function(x) {
        return this.centerAt(x, null);
    },
    
    centerAtY: function(y) {
        return this.centerAt(null, y);
    },
    
    // Center this dialog in the viewport
    // axis is optional, can be 'x', 'y'.
    center: function(axis) {
        var v = Overlay._viewport();
        var o = this.options.fixed ? [0, 0] : [v.left, v.top];
        if (!axis || axis == 'x') this.centerAt(o[0] + v.width / 2, null);
        if (!axis || axis == 'y') {
            y = Math.max(o[1] + v.height / 2, 10);
            this.centerAt(null, y);
        }
        return this;
    },
    
    // Center this dialog in the viewport (x-coord only)
    centerX: function() {
        return this.center('x');
    },
    
    // Center this dialog in the viewport (y-coord only)
    centerY: function() {
        return this.center('y');
    },
    
    // Resize the content region to a specific size
    resize: function(width, height, after) {
        if (!this.visible) return;
        var bounds = this._getBoundsForResize(width, height);
        this.overlay.css({left: bounds[0], top: bounds[1]});
        this.getContent().css({width: bounds[2], height: bounds[3]});
        if (after) after(this);
        return this;
    },
    
    // Tween the content region to a specific size
    tween: function(width, height, after) {
        if (!this.visible) return;
        var bounds = this._getBoundsForResize(width, height);
        var self = this;
        this.overlay.stop().animate({left: bounds[0], top: bounds[1]});
        this.getContent().stop().animate({width: bounds[2], height: bounds[3]}, function() {
            if (after) after(self);
        });
        return this;
    },
    
    // Returns true if this dialog is visible, false otherwise
    isVisible: function() {
        return this.visible;    
    },
    
    // Make this overlay instance visible
    show: function() {
        if (this.visible) return;
        if (this.options.modal) {
            var self = this;
            if (!Overlay.resizeConfigured) {
                Overlay.resizeConfigured = true;
                jQuery(window).resize(function() { Overlay._handleResize(); });
            }
            this.modalBlackout = jQuery('<div class="overlay-modal-blackout"></div>')
                .css({zIndex: Overlay._nextZ(),
                      opacity: 0.5,
                      width: jQuery(document).width(),
                      height: jQuery(document).height()})
                .appendTo(document.body);
            this.toTop();
            if (this.options.closeable) {
                jQuery(document.body).bind('keypress.overlay', function(evt) {
                    var key = evt.which || evt.keyCode;
                    if (key == 27) {
                        self.hide();
                        jQuery(document.body).unbind('keypress.overlay');
                    }
                });
            }
        }

        this.overlay.stop().show();
        this.visible = true;
        this._fire('afterShow');

        document.documentElement.style.overflow = 'hidden';
        document.body.scroll = 'no';

        return this;
    },
    
    // Hide this overlay instance
    hide: function(after) {
        if (!this.visible) return;
        var self = this;
        if (this.options.modal) {
            jQuery(document.body).unbind('keypress.overlay');
            this.modalBlackout.animate({opacity: 0}, function() {
                jQuery(this).remove();
            });
        }
        this.overlay.stop().animate({opacity: 0}, 300, function() {
            self.overlay.css({display: 'none'});
            self.visible = false;
            self._fire('afterHide');
            if (after) after(self);
            if (self.options.unloadOnHide) self.unload();
        });
        return this;
    },
    
    toggle: function() {
        this[this.visible ? 'hide' : 'show']();
        return this;
    },
    
    hideAndUnload: function(after) {
        this.options.unloadOnHide = true;
        this.hide(after);
        return this;
    },
    
    unload: function() {
        this._fire('beforeUnload');
        this.overlay.remove();
        if (this.options.actuator) {
            jQuery.data(this.options.actuator, 'active.overlay', false);
        }

        document.documentElement.style.overflow = 'auto';
        document.body.scroll = 'yes';
    },
    
    // Move this dialog box above all other overlay instances
    toTop: function() {
        this.overlay.css({zIndex: Overlay._nextZ()});
        return this;
    },
    
    // Returns the title of this dialog
    getTitle: function() {
        return jQuery('> .title-bar h2', this.getInner()).html();
    },
    
    // Sets the title of this dialog
    setTitle: function(t) {
        jQuery('> .title-bar h2', this.getInner()).html(t);
        return this;
    },
    
    //
    // Don't touch these privates
    
    _getBoundsForResize: function(width, height) {
        var csize = this.getContentSize();
        var delta = [width - csize[0], height - csize[1]];
        var p = this.getPosition();
        return [Math.max(p[0] - delta[0] / 2, 0),
                Math.max(p[1] - delta[1] / 2, 0), width, height];
    },
    
    _setupTitleBar: function() {
        if (this.options.title) {
            var self = this;
            var h2 = (this.options.title != ' ')? "<h2>" + this.options.title + "</h2>":null;
            var tb = jQuery("<div class='title-bar'></div>").html(h2);
            if (this.options.closeable) {
                tb.append(jQuery("<a href='#' class='close' title='Close'></a>").html(this.options.closeText));
            }
            if (this.options.draggable) {
                tb[0].onselectstart = function() { return false; }
                tb[0].unselectable = 'on';
                tb[0].style.MozUserSelect = 'none';
                if (!Overlay.dragConfigured) {
                    jQuery(document).mousemove(Overlay._handleDrag);
                    Overlay.dragConfigured = true;
                }
                tb.mousedown(function(evt) {
                    self.toTop();
                    Overlay.dragging = [self, evt.pageX - self.overlay[0].offsetLeft, evt.pageY - self.overlay[0].offsetTop];
                    jQuery(this).addClass('dragging');
                }).mouseup(function() {
                    jQuery(this).removeClass('dragging');
                    Overlay.dragging = null;
                    self._fire('afterDrop');
                });
            }
            this.getInner().prepend(tb);
            this._setupDefaultBehaviours(tb);
        }
    },
    
    _setupDefaultBehaviours: function(root) {
        var self = this;
        if (this.options.clickToFront) {
            root.click(function() { self.toTop(); });
        }
        jQuery('.close', root).click(function() {
            this.blur();
            self.hide();
            return false;
        }).mousedown(function(evt) { evt.stopPropagation(); });
    },
    
    _fire: function(event) {
        this.options[event].call(this);
    }
    
};
