/* Merged Plone Javascript file
 * This file is dynamically assembled from separate parts.
 * Some of these parts have 3rd party licenses or copyright information attached
 * Such information is valid for that section,
 * not for the entire composite file
 * originating files are separated by - filename.js -
 */

/* - dropdown.js - */
/*
 * This is the code for the dropdown menus. It uses the following markup:
 *
 * <dl class="actionMenu" id="uniqueIdForThisMenu">
 *   <dt class="actionMenuHeader">
 *     <!-- The following a-tag needs to be clicked to dropdown the menu -->
 *     <a href="some_destination">A Title</a>
 *   </dt>
 *   <dd class="actionMenuContent">
 *     <!-- Here can be any content you want -->
 *   </dd>
 * </dl>
 *
 * When the menu is toggled, then the dl with the class actionMenu will get an
 * additional class which switches between 'activated' and 'deactivated'.
 * You can use this to style it accordingly, for example:
 *
 * .actionMenu.activated {
 *   display: block;
 * }
 *
 * .actionMenu.deactivated {
 *   display: none;
 * }
 *
 * When you click somewhere else than the menu, then all open menus will be
 * deactivated. When you move your mouse over the a-tag of another menu, then
 * that one will be activated and all others deactivated. When you click on a
 * link inside the actionMenuContent element, then the menu will be closed and
 * the link followed.
 *
 */

function hideAllMenus() {
    jq('dl.actionMenu').removeClass('activated').addClass('deactivated');
};

function toggleMenuHandler(event) {
    // swap between activated and deactivated
    jq(this).parents('.actionMenu:first')
        .toggleClass('deactivated')
        .toggleClass('activated');
    return false;
};

function actionMenuDocumentMouseDown(event) {
    if (jq(event.target).parents('.actionMenu:first').length)
        // target is part of the menu, so just return and do the default
        return true;

    hideAllMenus();
};

function actionMenuMouseOver(event) {
    var menu_id = jq(this).parents('.actionMenu:first').attr('id');
    if (!menu_id) return true;

    var switch_menu = jq('dl.actionMenu.activated').length > 0;
    jq('dl.actionMenu').removeClass('activated').addClass('deactivated');
    if (switch_menu)
        jq('#' + menu_id).removeClass('deactivated').addClass('activated');
};

function initializeMenus() {
    jq(document).mousedown(actionMenuDocumentMouseDown);

    hideAllMenus();

    // add toggle function to header links
    jq('dl.actionMenu dt.actionMenuHeader a')
        .click(toggleMenuHandler)
        .mouseover(actionMenuMouseOver);
        
    // add hide function to all links in the dropdown, so the dropdown closes
    // when any link is clicked
    jq('dl.actionMenu > dd.actionMenuContent').click(hideAllMenus);
};

jq(initializeMenus);


/* - table_sorter.js - */

/********* Table sorter script *************/

function sortable(a) {
    // convert a to something sortable
    // A number, but not a date?
    if (a.charAt(4) != '-' && a.charAt(7) != '-' && !isNaN(parseFloat(a)))
        return parseFloat(a);    
    return a.toLowerCase();
}

function sort() {
    var name = jq(this).text();
    var table = jq(this).parents('table:first');
    var tbody = table.find('tbody:first');
    var reverse = table.attr('sorted') == name;

    jq(this).parent().find('th:not(.nosort) img.sortdirection')
        .attr('src', portal_url + '/arrowBlank.gif');
    jq(this).children('img.sortdirection').attr('src', portal_url + 
        (reverse ? '/arrowDown.gif' : '/arrowUp.gif'));
    
    var index = jq(this).parent().children('th').index(this);
    var data = [];
    tbody.find('tr').each(function() {
        var cells = jq(this).children('td');
        data.push([
            sortable(cells.slice(index,index+1).text()),
            // crude way to sort by surname and name after first choice
            sortable(cells.slice(1,2).text()), sortable(cells.slice(0,1).text()),
            this]);
    });

    if (data.length) {
        data.sort();
        if (reverse) data.reverse();
        table.attr('sorted', reverse ? '' : name);

        // appending the tr nodes in sorted order will remove them from their old ordering
        tbody.append(jq.map(data, function(a) { return a[3]; }));
        // jquery :odd and :even are 0 based
        tbody.find('tr').removeClass('odd').removeClass('even')
            .filter(':odd').addClass('even').end()
            .filter(':even').addClass('odd');
    }    
}

jq(function() {
    // set up blank spaceholder gif
    var blankarrow = jq('<img>')
        .attr('src', portal_url + '/arrowBlank.gif')
        .attr('width', 6).attr('height', 9).addClass('sortdirection');
    // all listing tables not explicitly nosort, all sortable th cells
    // give them a pointer cursor and  blank cell and click event handler
    // the first one of the cells gets a up arrow instead.
    jq('table.listing:not(.nosort) thead th:not(.nosort)')
        .append(blankarrow.clone())
        .css('cursor', 'pointer')
        .click(sort)
        .slice(0, 1)
        .find('img.sortdirection').attr('src', portal_url + '/arrowUp.gif');
});


/* - calendar_formfield.js - */
/* jscalendar glue
 *
 * Expects one hidden calendar field, and up to 6 extra calendar fields with
 * the same id as the hidden field, with _year, _month, _day, _hour, _minute
 * and _ampm added. These fields are expected to be single selects, although
 * _year can be a hidden field as well. The _hour and _minute fields are
 * optional, is is the _ampm field. See calendar_macros.pt for a complete
 * example
 *
 * Included are backwards-compatibility function names to support the pre-3.1.4
 * calling conventions, so 3rd-party usage will still work. A future version of
 * Plone may remove this support.
 */
if (typeof(plone)=='undefined')
    var plone = {};

plone.jscalendar = {
    _calendar: null,
    _current_input: null,
    _field_names: ['year', 'month', 'day', 'hour', 'minute', 'ampm'],
    
    // All calendar fields
    _fields: function(selector) {
        if (selector === undefined) selector = plone.jscalendar._current_input;
        var fields = {field: jq(selector)};
        jq.each(plone.jscalendar._field_names, function() {
            fields[this] = jq(selector + '_' + this);
        });
        return fields;
    },
    
    // Attach event handlers on load
    init: function() {
        jq('.plone_jscalendar > input:hidden').each(function() {
            var selector = '#' + this.id;
            jq.each(plone.jscalendar._fields(selector), function() {
                this.filter('select').bind(
                    'change.plone.jscalendar', {selector: selector}, 
                    plone.jscalendar.update_hidden);
            });
        });
    },
    
    // show calendar popup
    show: function(input_id, yearStart, yearEnd) {
        var cal = plone.jscalendar._cal;
        if (!cal) {
            cal = plone.jscalendar._cal = new Calendar(
                    // firstdDay, datestr, onSelect, onClose
                    1, null, plone.jscalendar.handle_select, 
                    plone.jscalendar.handle_close
            );
            cal.create();
        } else
            cal.hide();
        
        if (arguments.length > 3) {
            // Backwards compatibility, fields passed in manually
            cal.params = {
                range: [arguments[7], arguments[8]], // yearStart, yearEnd
                inputField    : jq('#' + arguments[1]).get(0), // input_id
                input_id_year : jq('#' + arguments[2]).get(0), // "_year
                input_id_month: jq('#' + arguments[3]).get(0), // "_month,
                input_id_day  : jq('#' + arguments[4]).get(0)  // "_day
            };
            var anchor = jq('#' + arguments[0]);               // "_anchor
            cal.setRange(cal.params.range[0], cal.params.range[1]);
            window.calendar = cal;
            var fields = {
                year: jq(cal.params.input_id_year),
                month: jq(cal.params.input_id_month),
                day: jq(cal.params.input_id_day)
            };
        } else {
            plone.jscalendar._current_input = input_id;
            var fields = plone.jscalendar._fields();
            var anchor = fields.month;
            cal.setRange(yearStart, yearEnd);
        }
        
        // Set calendar popup date to currently selected values
        if (fields.year.val() > 0)  cal.date.setFullYear(fields.year.val());
        if (fields.month.val() > 0) cal.date.setMonth(fields.month.val() - 1);
        if (fields.day.val() > 0)   cal.date.setDate(fields.day.val());
        cal.refresh();
        cal.showAtElement(anchor.get(0), null);
        return false;
    },
    
    // handle calendar popup date select
    handle_select: function(cal, date) {
        if (cal.params !== undefined) {
            // backwards compat; field references stored in cal.params
            var fields = {
                year : jq(cal.params.input_id_year),
                month: jq(cal.params.input_id_month),
                day  : jq(cal.params.input_id_day)
            };
        } else
            var fields = plone.jscalendar._fields();
        
        var yearValue = date.substring(0,4);
        
        if (jq.nodeName(fields.year.get(0), 'select') && 
            !fields.year.children('option[value=' + yearValue + ']').length) {
            // insert missing year into the options list
            var options = fields.year.get(0).options;
            for (var i=options.length; i--; i > 0) {
                if (options[i].value > yearValue)
                    options[i + 1] = new Option(options[i].value, 
                                                options[i].text);
                else {
                    options[i + 1] = new Option(yearValue, yearValue);
                    break;
                }
            }
        }
        
        fields.year.val(yearValue);
        fields.month.val(date.substring(5, 7));
        fields.day.val(date.substring(8, 10));
        
        if (cal.params !== undefined) {
            // backwards compat, direct ref to field stored in cal.params
            var inputField = jq(cal.params.inputField);
            inputField.val(date + inputField.val().substr(10)); // keep time
        } else
            plone.jscalendar.update_hidden();
    },
    
    // handle calendar popup close
    handle_close: function(cal) {
        // clean up backwards compat structure
        if (cal.params !== undefined) cal.params = window.calendar = undefined;
        cal.hide();
    },
    
    // updates a hidden date field with the current values of the widgets
    update_hidden: function(e) {
        var val = '';
        
        if (arguments.length > 1)
            // backwards compat, direct ids for fields passed in
            var f = {
                field : jq('#' + arguments[0]),
                year  : jq('#' + arguments[1]),
                month : jq('#' + arguments[2]),
                day   : jq('#' + arguments[3]),
                hour  : jq('#' + arguments[4]),
                minute: jq('#' + arguments[5]),
                ampm  : jq('#' + arguments[6])
            };
        else
            var f = plone.jscalendar._fields(e && e.data.selector);
        
        // backwards-compatibility check; only the year widget can reset then
        if ((arguments.length > 1 && f.year.val() == 0) || 
            (e && e.target.selectedIndex === 0)) {
            // Reset widgets; only the time widgets if this is a time select box.
            var type = arguments.length == 1 && e.target.id.substr(e.data.selector.length);
            var filter = jq.inArray(type, ['hour', 'minute', 'ampm']) > -1 ?
                'select[id$=hour],select[id$=minute],select[id$=ampm]': 'select';
            jq.each(f, function() { this.filter(filter).attr('selectedIndex', 0); });
        } else if (f.year.val() > 0 && f.month.val() > 0 && f.day.val() > 0) {
            // ISO date string
            val = [f.year.val(), f.month.val(), f.day.val()].join('-');
            
            var date = new Date(val.replace(/-/g, '/'));
            if (date.print('%Y-%m-%d') != val) {
                // Date turnes illegal dates into legal ones, update widgets
                val = date.print('%Y-%m-%d');
                f.year.val(val.substring(0, 4));
                f.month.val(val.substring(5, 7));
                f.day.val(val.substring(8, 10));
            }
            
            // optional time
            if (f.hour.length && f.minute.length) {
                val += " " + [f.hour.val(), f.minute.val()].join(':');
                if (f.ampm.length) val += " " + f.ampm.val();
            }
        }
        
        f.field.val(val);
    }
};

// initialize fields
jq(plone.jscalendar.init);

// Backwards compatibility function names
var showJsCalendar = plone.jscalendar.show;
var onJsCalendarDateUpdate = plone.jscalendar.handle_select;
var update_date_field = plone.jscalendar.update_hidden;


/* - calendarpopup.js - */

// The calendar popup show/hide:

    function showDay(date) {
        $('#day' + date).css({'visibility': 'visible'});
        return true;
    }    
    function hideDay(date) {
        $('#day' + date).css({'visibility': 'hidden'});
        return true;
    }


 




/* - formUnload.js - */
/* BeforeUnload form processing */
if (!window.beforeunload) (function() {
    var BeforeUnloadHandler = function() {
        var self = this;

        this.message = window.form_modified_message ||
            "Discard changes? If you click OK, any changes you have made will be lost.";

        this.forms = [];
        this.chkId = [];
        this.chkType = new this.CheckType();
        this.handlers = [this.isAnyFormChanged];
        this.submitting = false;

        this.execute = function(event) {
            // NOTE: this handler is not jQuery-wrapped!
            // First clean out dead references to make sure we only work on
            // forms that are actually in the dom. This is needed in
            // combination with KSS and/or other dynamic replacements.
            var domforms = jq('form'); 
            self.forms = jq.grep(self.forms, function(form) {
                return domforms.index(form) > -1;
            });             
            
            // Now do the protection work
            if (self.submitting) return;

            var message;
            jq.each(self.handlers, function(i, fn) {
                message = message || fn.apply(self);
            });
            if (message===true) message = self.message;
            if (message===false) message = undefined;
            if (event && message) event.returnValue = message;
            return message;
        }
        this.execute.tool = this;
    }
    var Class = BeforeUnloadHandler.prototype;

    // form checking code
    Class.isAnyFormChanged = function() {
        for (var i = 0; form = this.forms[i++];) {
            if (this.isElementChanged(form))
                return true;
        }
        return false;
    }
    Class.addHandler = function(fn) {
        this.handlers.push(fn);
    }
    Class.onsubmit = function() {
        var tool = window.onbeforeunload && window.onbeforeunload.tool;
        tool.submitting = true;
        // Also set this on the unlocking tool!
        // This way the tool knows we are in submitting,
        // and can prevent unlocking.
        plone.UnlockHandler.submitting = true;
    }
    Class.addForm = function(form) {
        if (jq.inArray(form, this.forms) > -1) return;
        this.forms.push(form);
        jq(form).submit(this.onsubmit);
        var elements = form.getElementsByTagName('input');
        // store hidden input's defaultValue to work around a moz bug
        jq(form).find('input:hidden').each(function() {
            var value = this.defaultValue;
            if (value!==undefined&&value!==null)
                jq(this).attr('originalValue', value.replace(/\r\n?/g,'\n'));
        });
    }
    Class.addForms = function() {
        var self = this;
        jq.each(arguments, function() {
            if (this.tagName.toLowerCase() == 'form')
                self.addForm(this);
            else
                self.addForms.apply(self, jq(this).find('form').get());
        });
    }
    Class.removeForms = function() {
        var self = this;
        jq.each(arguments, function() {
            if (this.tagName.toLowerCase() == 'form') {
                var el = this;
                self.forms = jq.grep(self.forms, function(form) {
                    return form != el;
                });
                jq(element).unbind('submit', self.onsubmit);
            } else
                self.removeForms.apply(self, jq(this).find('form').get());
        });
    }

    Class.CheckType = function() {};
    var c = Class.CheckType.prototype;
    c.checkbox = c.radio = function(ele) {
        return ele.checked != ele.defaultChecked;
    }
    c.file = c.password = c.textarea = c.text = function(ele) {
        return ele.value != ele.defaultValue;
    }
    // hidden: cannot tell on Mozilla without special treatment
    c.hidden = function(ele) {
        var orig = jq(ele).attr('originalValue');
        if (orig===undefined||orig===null) return false;
        return jq(ele).val().replace(/\r\n?/g, '\n') != orig;
    }

    c['select-one'] = function(ele) {
        for (var i = 0; opt=ele[i++];) {
            if (opt.selected != opt.defaultSelected) {
                if (i===1 && opt.selected) continue; /* maybe no default */
                return true;
            }
        }
        return false;
    }

    c['select-multiple'] = function(ele) {
        for (var i = 0; opt = ele[i++];) {
            if ( opt.selected != opt.defaultSelected)
                return true;
        }
        return false;
    }

    Class.chk_form = function(form) {
        // Find all form elements that are a) not marked as not-protected
        // or b) not a descendant of a non-protected element.
        var elems = jq(form).find('> :input:not(.noUnloadProtection),' +
            ':not(.noUnloadProtection) :input:not(.noUnloadProtection)');
        for (var i = 0; element = elems.get(i++);) {
            if (this.isElementChanged(element))
                return true;
        }
        return false;
    }

    Class.isElementChanged = function(ele) {
        var method = ele.id && this.chkId[ele.id];
        if (!method && ele.type && ele.name)
            method = this.chkType[ele.type];
        if (!method && ele.tagName)
            method = this['chk_'+ele.tagName.toLowerCase()];

        return method? method.call(this, ele) : false;
    };

    // Can't use jQuery handlers here as kupu and kss rely on direct access.
    window.onbeforeunload = new BeforeUnloadHandler().execute;
    
    jq(function() {
        var tool = window.onbeforeunload && window.onbeforeunload.tool;
        var content = getContentArea();
        if (tool && content)
            tool.addForms.apply(tool, jq('form.enableUnloadProtection').get());
    });
})();


/* - formsubmithelpers.js - */
function inputSubmitOnClick(event) {
    if (jq(this).hasClass('submitting') && !jq(this).hasClass('allowMultiSubmit'))
        return confirm(window.form_resubmit_message);
    else
        jq(this).addClass('submitting');
}

jq(function() {
    jq(':submit').each(function() {
      if (!this.onclick)
        jq(this).click(inputSubmitOnClick);
    });
});


/* - unlockOnFormUnload.js - */
// http://www.dowlenroadvetcenter.com/portal_javascripts/unlockOnFormUnload.js?original=1
if(typeof(plone)=='undefined')
var plone={};plone.UnlockHandler={init: function(){if(jq('form.enableUnlockProtection').length){jq(window).unload(plone.UnlockHandler.execute);plone.UnlockHandler._refresher=setInterval(plone.UnlockHandler.refresh,300000)}},cleanup: function(){jq(window).unbind('unload',plone.UnlockHandler.execute);clearInterval(plone.UnlockHandler._refresher)},execute: function(){if(this.submitting) return;jq.get(plone.UnlockHandler._baseUrl()+'/@@plone_lock_operations/safe_unlock')},refresh: function(){if(this.submitting) return;jq.get(plone.UnlockHandler._baseUrl()+'/@@plone_lock_operations/refresh_lock')},_baseUrl: function(){var baseUrl=jq('base').attr('href');if(!baseUrl){var pieces=window.location.href.split('/');pieces.pop();baseUrl=pieces.join('/')}
return baseUrl}};jq(plone.UnlockHandler.init);

