/**
 * An edit panel
 *
 * This panel requires Prototype javascript framework and scriptaculous effects
 * library.
 *
 * To use the panel, first include this script in the head of the document:
 * <script src="panel.js" type="text/javascript"></script>
 *
 * To add an open link to open the panel add an anchor to the DOM with the
 * attribute rel set to panel:
 * <a href="javascript:{};" rel="panel">Open Panel</a>
 *
 * You can also call the panel by defining your own onclick functionality
 * <a href="javascript:{};" onclick="javascript:myPanel.open();" />Open</a>
 *
 *
 * By default this script creates an instance of the panel called myPanel( you
 * can see this at the bottom of this script).  So to work with or use the panel
 * you can simply use myPanel, which is a global instance of the panel.
 *
 * The panel has configuration that at this time can be set and passed at the
 * bottom of this file.  The following options are available
 *
 * height - integer - default null
 *    height of the panel, can be controlled through css as well by defining a
 *    height for #panel
 * width - integer - default null
 *    width of the panel, can be controlled through css as well by defining a
 *    width for #panel
 *
 * overlay - options for the overlay backdrop behind the panel
 * overlayClickExit - boolean - default true
 *    defines whether the backdrop exits the panel when clicked
 * overlayDuration - integer - default 1
 *    defines the duration in seconds of the overlay open or close effect
 * overlayDurationFrom - float - default 0.0
 *    defines the start opacity for the overlay effect
 * overlayDurationTo - float - default 0.8
 *    defines the end opacity for the overlay effect
 *
 * open - options for the open of the panel
 * openEffect - string - default 'Appear'
 *    defines the scriptaculous effect to use to open the panel
 * openOptions - JS object
 *    defines the options to pass to the scriptaculous effect
 *
 * close - options for the close of the panel
 * closeEffect - string - default 'Fade'
 *    defines the scriptaculous effect to use to close the panel
 * closeOptions - JS object
 *    defines the options to pass to the scriptaculous effect
 *
 *
 * @category
 * @package
 * @author      Joshua Ross <joshualross@gmail.com>
 * @copyright   Copyright (C) ???? All rights reserved.
 * @version     $Id: $
 * @revision    $LastChangedRevision: $
 * @date        $LastChangedDate: $
 * @file        $HeadURL: $
 * @link
 * @since       File available since release 0.1
 */

//this is temporary, we need to check both that scriptaculous is available
//as well as prototype
if (typeof Prototype=='undefined')
  throw("panel.js requires the Prototype JavaScript framework >= 1.6.0");
if (typeof Effect=='undefined')
  throw("panel.js requires script.aculo.us' effects.js library");
if (typeof Resizable=='undefined')
  throw("panel.js requires resizable.js effect library");

/**
 * An edit Panel
 *
 * @author      Joshua Ross <joshualross@gmail.com>
 * @package
 * @subpackage
 * @copyright   Copyright (C) ???? All rights reserved.
 */
var Panel = Class.create();
Panel.prototype = {

    DefaultOptions: {
        overlayClickExit:       true,
        overlayDuration:        1, //seconds
        overlayDurationFrom:    0.0, //opacity
        overlayDurationTo:      0.8,  //opacity
        openEffect:             'Appear',
        openOptions:            {duration: 1}, //seconds
        closeEffect:            'Fade',
        closeOptions:           {duration: 1}, //second
        height:                 null,
        width:                  null
    },

    /**
     * Scriptaculous Draggable object
     *
     * @var Draggable
     * @access private
     */
    _draggable: null,

    /**
     * The panel title
     * @var string
     * @access private
     */
    _title: 'Control Panel',

    /**
     * The panel subtitle
     * @var string
     * @access private
     */
    _subtitle: 'drag me around',

    /**
     * The panel body
     * @var string
     * @access private
     */
    _body: '',

    /**
     * The panel body header
     * @var string
     * @access private
     */
    _header: '',

    /**
     * The panel body footer
     * @var string
     * @access private
     */
    _footer: '',

    /**
     *
     * @var Object
     * @access private
     */
    _options: {},

    /**
     * This method is a private static initializer(constructor).
     *
     * The first part of this method loops through the DOM looking for
     * references to panel in an anchor tag.  When it finds one it adds the
     * onclick functionality to it.  The second part creates the panel
     * by appending the DOM elements to the document.body
     *
     * @todo accept configuration/parameters
     * @access private
     * @return void
     */
    initialize: function(options) {

        this._options = Object.extend(Object.extend({ },this.DefaultOptions), options || { });

        // loop through all anchor tags and add open event
        $$('a').each(function(a){
            if ($(a).hasAttribute('rel') && $(a).readAttribute('rel').match('panel')) {
                $(a).observe('click', function(){myPanel.open()});
            }
        });


        /*
            <div id="overlay"></div>
            <div id="panel" style="display:none">
                <div id="panelControl">
                    <a id="panelClose" href="javascript:{};" onclick="javascript:panel.close()">x</a>
                    <div id="panelTitle"></div>
                    <div id="panelSubtitle"></div>
                </div>
                <div id="panelHeader"></div>
                <div id="panelBody"></div>
                <div id="panelFooter"></div>
            </div>
        */

        $(document.body).appendChild(
            Builder.node('div',{
                id:'overlay',
                style:'display:none;position:absolute;top:0;left:0;width:100%;z-index:900'
            })
        );

        if (true==this._options.overlayClickExit) {
            $('overlay').observe('click','myPanel.close()');
        }

        $(document.body).appendChild(
            Builder.node('div', {id:'panel',style:'display:none;z-index:1000'},[
                Builder.node('div',{
                        id:'panelClose',
                        //href:'javascript:{};',
                        onclick:'myPanel.close();',
                        style:'position:absolute;top:0;right:4px;font-weight:bold;font-size:12px;font-family:Arial;cursor:pointer'
                    }, 'x'),
                Builder.node('div',{
                        id:'panelResizer',
                        style:'position:absolute;bottom:2px;right:2px'
                    }),
                Builder.node('div',{id:'panelControl'},[
                    Builder.node('div',{id:'panelTitle'}, [
                        this._title
                    ]),
                    Builder.node('div',{id:'panelSubtitle'},this._subtitle)
                ]),
                Builder.node('div',{id:'panelHeader'},this._header),
                Builder.node('div',{id:'panelBody'},this._body),
                Builder.node('div',{id:'panelFooter'},this._footer)
            ])
        );

        if (null!=this._options.height) {
            $('panel').setStyle({height:this._options.height+'px'});
        }

        if (null!=this._options.width) {
            $('panel').setStyle({width:this._options.width+'px'});
        }

        //resizable
        new Resizable('panel',{handle:'panelResizer',minWidth:150,minHeight:150});

    },

    /**
     * Hides the panel
     *
     * This method uses the optional parameter for close effect to hide the
     * panel
     *
     * @return void
     */
    close: function() {

        if (typeof Effect[this._options.closeEffect]=='undefined')
            throw('Invalid close effect');

        new Effect[this._options.closeEffect]('panel', this._options.closeOptions);

        this.draggable.destroy();
        this.unoverlay();
    },

    /**
     * Displays the panel
     *
     * This method uses prototype's show method to display the panel.  It also
     * adds the
     *
     * @todo accept a animation effect to use when we open
     * @access public
     * @return void
     */
    open: function() {

        if (typeof Effect[this._options.openEffect]=='undefined')
            throw('Invalid open effect');

        new Effect[this._options.openEffect]('panel', this._options.openOptions);
        this.draggable = new Draggable($('panel'),{revert:false, handle:'panelControl'});
        this.overlay();

    },


    /**
     *
     * This method uses the element name to get the innerHTML for editing
     *
     * @accept string id of the element to edit
     * @access public
     * @return void
     */
    edit: function(value) {
        if (typeof value == 'string'){
            this.setBodyRaw(value);
        } else if (typeof value == 'object'){
            this.setBodyElement(value);
        } else {
            return;
        }
        this.open();


    },

    /**
     * This method hides select boxes and flash elements and then fades the
     * overlay in.
     *
     * @access public
     * @return void
     */
    overlay: function() {
        $$('object', 'embeds', 'select').each(function(e){
            $(e).setStyle({visibility:'hidden'});
        });


        $('overlay').setStyle({height:document.viewport.getHeight()+'px'});
        new Effect.Appear('overlay', {
            duration:   this._options.overlayDuration,
            from:       this._options.overlayDurationFrom,
            to:         this._options.overlayDurationTo
        });
    },

    /**
     * This method fades out the overlay and then unhides select boxes and
     * flash elements
     *
     * @access public
     * @return void
     */
    unoverlay: function() {
        new Effect.Fade('overlay',{
            duration:   this._options.overlayDuration
        });
        $$('object', 'embeds', 'select').each(function(e){
            $(e).setStyle({visibility:'visible'});
        });
    },

    /**
     * Returns a property
     *
     * @todo disallow private variable access
     * @param string name
     * @return mixed
     */
    get: function(name) {
        return this.name;
    },

    /**
     * Sets the property name to value
     *
     * @todo disallow private variable access
     * @param string name
     * @param mixed value
     * @return void
     */
    set: function(name, value) {
        this.name = value;
    },

    /**
     * Returns the title
     *
     * @access public
     * @return string
     */
    getTitle: function() {
        return this._title;
    },

    /**
     * Sets the title to value
     *
     * @param string value
     * @return void
     */
    setTitle: function(value) {
        this._title = value;
    },

    /**
     * Returns the subtitle
     *
     * @access public
     * @return string
     */
    getSubtitle: function() {
        return this._subtitle;
    },

    /**
     * Sets the subtitle to value
     *
     * @param string value
     * @return void
     */
    setSubtitle: function(value) {
        this._subtitle = value;
    },

    /**
     * Returns the body
     *
     * @access public
     * @return string
     */
    getBody: function() {
        return this._body;
    },

    /**
     * Sets the body to value.  Value is expected to be an id of a DOM element
     *
     * @param string value
     * @return void
     */
    setBodyElement: function(value) {
        this._body = new Element('div',{}).update($(value).innerHTML);
        $('panelBody').update(this._body);
    },

    /**
     * Sets the body to the raw value
     *
     * @param string value
     * @return void
     */
    setBodyRaw: function(value) {
        this._body = new Element('div',{}).update(value);
        $('panelBody').update(this._body);
    },

    /**
     * Returns the body header
     *
     * @access public
     * @return string
     */
    getHeader: function() {
        return this._header;
    },

    /**
     * Sets the body header to value
     *
     * @param string value
     * @return void
     */
    setHeader: function(value) {
        this._header = value;
        $('panelHeader').insert(this._header);
    },

    /**
     * Returns the body footer
     *
     * @access public
     * @return string
     */
    getFooter: function() {
        return this._footer;
    },

    /**
     * Sets the body footer to value
     *
     * @param string value
     * @return void
     */
    setFooter: function(value) {
        this._footer = value;
        $('panelFooter').insert(this._footer);
    }



};

var myPanel;
function initPanel() { myPanel = new Panel({height:450,width:400,openEffect:'Appear',overlayClickExit:false}); }
Event.observe(window, 'load', initPanel, false);
