
/* Script: popup.js
 *  
 *  A class to create popups in a document, relative to another element with a user-
 *  controllable offset and configurable show and hide delays and effects.
 *
 *  Requires mootools-core (developed for 1.2.3), does not need -more
 *
 *  Copyright (C) 2009 Chris Page <chris@starforge.co.uk>
 *  This work is licensed under the Creative Commons Attribution-Share Alike 2.0
 *  License. To view a copy of this license, visit
 *  http://creativecommons.org/licenses/by/2.0/ or send a letter to Creative
 *  Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
 *
 *  This software is provided 'as is', without any warranty of merchantabilty
 *  or fitness for a particular purpose. You may use this code in your project
 *  provided that this copyright notice is retained, and attribution is given in the
 *  resulting program credits.
 *
 */
var Popup = new Class({

    Implements: [Events, Options],

    options: {
        onShow: function(popup){
            // uses the long version of fading in, as fade('in')
            // may not work properly on initial load. NFC why this is.
            popup.get('tween').start('opacity', 0, 1);
        },
        onHide: function(popup){
            popup.get('tween').start('opacity', 1, 0);
        },
        showDelay: 100,
        hideDelay: 100,
        relativeTo: null,
        hoverElem: null,
        offset: {x: 16, y: 16},
    },

    initialize: function(element, options) {
        this.setOptions(options);
        
        // Work out what the element is relative to
        if(this.options.relativeTo == null) this.options.relativeTo = element.getParent();

        // Make sure that there's an element to add the events to
        if(this.options.hoverElem == null) this.options.hoverElem = element.getParent();

        // Create the popup and ensure the timer is clear
        this.popup = this.createPopup(element);
        this.timer = $clear(this.timer);

        // This acts as a means of ensuring that the fade out
        // can not happen until the fade in has at least started.
        this.needfade = false;
    },

    createPopup: function(element) {
        // First we want a copy of all the contents of the div
        var contents = element.get('html');

        // Now nuke the contents
        element.set('html', '');

        // Add in the contents inside a popup...
        element.adopt(
            new Element('div', {'class': 'popup-header'}).adopt(new Element('div', {'class': 'popup-corner'}),
                                                                new Element('div', {'class': 'popup-bar'})),
            new Element('div', {'class': 'popup-body'  }).adopt(new Element('div', {'class': 'popup-core', 'html': contents}),
                                                                new Element('div', {'class': 'popup-bar'})),
            new Element('div', {'class': 'popup-footer'}).adopt(new Element('div', {'class': 'popup-corner'}),
                                                                new Element('div', {'class': 'popup-bar'}))
        )

        // Attach events for mouse enter and leave.
        var events = ['enter', 'leave'];
        events.each(function(value) {
            this.options.hoverElem.addEvent('mouse' + value, this['element' + value.capitalize()].bindWithEvent(this, element));
        }, this);
       
        var relCoords = this.getRelativePos(this.options.relativeTo, 'left', 'bottom');
        var popupPos  = { left: relCoords['x'] + this.options.offset['x'],
                           top: relCoords['y'] + this.options.offset['y'] };

        element.setStyles(popupPos);

        return element;
    },


    elementEnter: function(event, element){
        var relCoords = this.getRelativePos(this.options.relativeTo, 'left', 'bottom');
        var popupPos  = { left: relCoords['x'] + this.options.offset['x'],
                           top: relCoords['y'] + this.options.offset['y'] };

        element.setStyles(popupPos);

        this.timer = $clear(this.timer);
        this.timer = this.show.delay(this.options.showDelay, this, element);
    },

    elementLeave: function(event, element){
        $clear(this.timer);
        this.timer = this.hide.delay(this.options.hideDelay, this, element);
    },

    show: function(element) {
        if(!this.needfade) {
            this.needfade = true;
            this.fireEvent('show', [this.popup, element]);
        }
    },

    hide: function(element) {
        if(this.needfade) {
            this.fireEvent('hide', [this.popup, element]);
            this.needfade = false;
        }
    },

    getRelativePos: function(element, xhandle, yhandle) {
        var obj = element;

        var curleft = 0;
        var curtop = 0;
        // Work out the top left corner of the specified element.
        // Note that we need to stop the search at the first absolute 
        // position element encountered, as coords for left and top will
        // be relative to that element!
        while(obj.offsetParent && obj.getStyle('position') != 'absolute') {
            curleft += obj.offsetLeft;
            curtop  += obj.offsetTop;
            obj = obj.offsetParent;
        }

        // If the x handle is set to something other than 'left', deal with it
        if(xhandle == 'right') {
            curleft += element.offsetWidth;
        } else if(xhandle == 'center') {
            curleft += parseInt(element.offsetWidth / 2);
        }

        // Now do the same for the y
        if(yhandle == 'bottom') {
            curtop += element.offsetHeight;
        } else if(yhandle == 'center') {
            curtop += parseInt(element.offsetHeight / 2);
        }

        return { x: curleft, y: curtop }
    }

});


function buildpopups()
{
    // Go through each popup div in the document replacing it with a popup.
    $$('div.popup').each(function(element,index) {
        new Popup(element, 
                  {hideDelay: 500,
                   showDelay: 2000,
                   offset: {'x': 16, 'y': 0},
                  });
    });
    
}