function $find(id) { return document.getElementById(id); };
function $first(el, tag) { var t = el.getElementsByTagName(tag); return t.length ? t[0] : null; };
function $children(el, tag) {
    tag = tag.toUpperCase();
    var a = [];
    for (var i = 0; i < el.childNodes.length; i++)
        if (el.childNodes[i].nodeName.toUpperCase() == tag) a.push(el.childNodes[i]);
    return a;
};
function $show(el) { if (el) el.style.display = 'block'; };
function $hide(el) { if (el) el.style.display = 'none'; };
function $toggle(el) { if (el) el.style.display = (el.style.display == 'block') ? 'none' : 'block'; };
function $bind() { // usage: $bind(obj, function, param1, param2, ...)
  var args = [];
  for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
  var object = args.shift(), __method = args.shift();
  return function() {
    return __method.apply(object, args);
  }
};

function popup(href, name, width, height, random) {
    width = typeof(width) != 'undefined' ? width : 626;
    height = typeof(height) != 'undefined' ? height : 436;
    random = typeof(random) != 'undefined' ? random : true;

    var params  = 'width='+width+',height='+height;
    params += ',top=50,left=50';
    params += ',resizable=yes,toolbar=0,status=0';
    if (random) {
        // always produce a new popup window; otherwise multiple calls to this
        // function for name will reload the current poup
        name += '_' + Math.floor(Math.random()*10000);
    }
    return window.open(href, name, params);
};

function urlDecode(s) {
    var c, r = /(%[^%]{2})/;
    while ((match = r.exec(s)) != null && match.length > 1 && match[1] != '') {
        c = String.fromCharCode(parseInt(match[1].substr(1),16));
        s = s.replace(match[1], c);
    }
    return s;
};

function addLevel3Handlers(t) {
    if (!t) t = $find('l3menu');
    if (!t) return;
    var tags = $children(t, 'li');
    for (var i = 0; i < tags.length; i++) {
        var ul = $first(tags[i], 'ul');
        if (!ul) continue;
        //tags[i].insertBefore(document.createElement('br'), ul);
        Event.observe(tags[i], 'mouseover', $bind(this, $show, ul));
        Event.observe(tags[i], 'mouseout',  $bind(this, $hide, ul));
    }
}

function addBarHandlers() {
    addLevel3Handlers($find('barmenu'));
}

function expandoHandler(ev) {
    ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    var div = $find(target.id.substr(5));
    $toggle(div);
    target.className = (target.className == 'expando') ? 'expanded' : 'expando';
}

function addExpandoHandlers() {
    Event.onDOMReady(function() {
        var h4s = document.getElementsByTagName('h4');
        for (var i = 0; i < h4s.length; i++) {
            var e = h4s[i];
            if (e.className.indexOf('expando') >= 0) {
                Event.observe(e, 'click', expandoHandler);
                $hide($find(e.id.substr(5)));
            }
        }
    });
}

function closeMessagePopup() {
    var target = $('message-popup');
    if (arguments.length) {
        target = $(arguments[0]);
        if (!target.hasClassName('message-popup')) target = target.up('.message-popup');
    }
    try {
        new Effect.DropOut(target);
    } catch (err) {
        target.hide();
    }
    return false;
};

var TikUtil = {
    getViewportSize: function() {
        var res = [0, 0];
        if ('undefined' != typeof window.innerWidth) {
            // standards compliant browsers
            res[0] = window.innerWidth;
            res[1] = window.innerHeight;
        } else {
            // IE6 uses either documentElement or document's body
            var d = document.documentElement;
            if ('undefined' == typeof document.documentElement) {
                d = document.getElementsByTagName('body')[0];
            }
            res[0] = d.clientWidth;
            res[1] = d.clientHeight;
        }

        return res;
    }
};

/**
 * Error display capability. Everything from queueMessage is handled automatically.
 * To use programmatically, call the methods of global TikMessageDisplay:
 *  - addMessage(message, type),
 *    @param type TikMessageDisplay.[SUCCESS|ERROR|WARN|INFO].
 *    Returns the message DOM element
 *  - close(el) closes an element returned by addMessage.
 *  - makePopup(header, body, relativeTo, orientation)
 *    creates a new popup, optionally placed relative to an item on the page.
 *    see documentation of Popup.create
 *    @param header string or DOM element to put in header
 *    @param body string or DOM elements to put in body
 *    @param relativeTo [optional] item relative to which to position (default: primary page container)
 *    @param orientation [optional] if above specified, specifies the side on which to show popop: [t, r, b, l]
 *    @return Popup object
 * - wrapPopup
 *    Wraps an existing DOM fragment as a popup. The fragment must be formed as follows:
 *      - div
 *         - div.head
 *         - div.body
 *    and should not have any other children.
 *    @param el root element (will be moved in DOM tree!)
 *    @param relativeTo [optional] item relative to which to position (default: primary page container)
 *    @param orientation [optional] if above specified, specifies the side on which to show popop: [t, r, b, l]
 *    @return Popup object
 * Popup object functions:
 *   - show: show popup
 *   - hide: hide popup
 */
TikMessageDisplay = function() {
    function getDiv(classes) {
        var e = $(document.createElement('div'));
        e.className = classes || '';
        return e;
    };

    function initialize() {
        var parent = $('popup-messages');
        if (!parent) return;
        var messages = $$('#popup-messages .message');
        if (messages.length) {
            messages.each(function(e) {
                var c = getDiv('close');
                e.appendChild(c);
            });
            Event.observe($('popup-messages'), 'click', closeMessage);

            setTimeout(showMessages, 1000);
            Event.observe(window, 'load', showMessages);

        } else {
            parent.hide();
        }
    };

    function showMessages() {
        if ($('popup-messages').visible()) return;
        try { window.scrollTo(0,0); } catch (e) { }
        new Effect.BlindDown($('popup-messages'), { 'duration': 1 });
    };

    function getOpenCount() {
        var messages = $$('#popup-messages .message');
        var c = 0;
        for (var i = 0; i < messages.length; i++) {
            if (messages[i].style.display != 'none') c++;
        }
        return c;
    };

    function finishClose(ef) {
        try { $(ef.element).remove(); } catch (ex) { }
        if (!$('popup-messages').down('.message')) {
            var e = $('popup-messages').down('.first');
            e.appendChild(getDiv(''));
            new Effect.SlideUp(e, { 'duration': 0.35, 'afterFinish': function() { $('popup-messages').hide(); try { e.remove(); } catch(ex) {} } });
        }
    };

    function closeMessage(evt) {
        var e = Event.element(evt);
        if (!e.hasClassName('close')) return;
        e = e.up('.message');
        new Effect.SlideUp(e, { 'duration': 0.7, 'afterFinish': finishClose });
    };

    function closeMessageElement(e) {
        new Effect.SlideUp(e, { 'duration': 0.7, 'afterFinish': finishClose });
    };

    function addMessage(text, type) {
        var parent = $('popup-messages');
        var msg = getDiv('message ' + type);
        msg.update('<div class="content">' + text + '</div><div class="close"></div>');
        Event.observe(msg, 'click', closeMessage);
        var c = getOpenCount();
        if (!parent.down('.first')) {
            // doesn't have the spacer, so it's actually empty
            parent.hide();
            parent.appendChild(getDiv('first'));
            parent.appendChild(msg);
            new Effect.BlindDown(parent, { 'duration': 1 });
        } else {
            // otherwise, other messages are showing
            parent.show();
            msg.hide();
            parent.appendChild(msg);
            new Effect.BlindDown(msg, { 'duration': 0.5 });
        }

        return msg;
    };

    Popup = Class.create();
    Popup.prototype = {
        initialize: function() {},

        /**
        * creates a new popup window, optionally relative to an item on the page
        * @param header string or DOM element to put in header
        * @param body string or DOM elements to put in body
        * @param relativeTo [optional] item relative to which to position (default: primary page container)
        * @param orientation [optional] if above specified, specifies the side on which to show popop: [t, r, b, l]
         */
        create: function (header, body, relativeTo, orientation) {
            this.element = getDiv('ui-pop');
            this.element.hide();
            ($('page') || document.body).appendChild(this.element);
            this.element.appendChild(getDiv('t'));
            this.head = getDiv('head');
            this.head.update(header);
            this.element.appendChild(this.head);
            this.element.appendChild(getDiv('m'));
            this.body = getDiv('body');
            this.body.update(body);
            this.element.appendChild(this.body);
            this.element.appendChild(getDiv('b'));
            this.position(relativeTo, orientation);
        },

        /**
         * Wraps an existing DOM fragment as a popup. The fragment must be formed as follows:
         *   - div
         *      - div.head
         *      - div.body
         * and should not have any other children.
         * @param el root element (will be moved in DOM tree!)
         * @param relativeTo [optional] item relative to which to position (default: primary page container)
         * @param orientation [optional] if above specified, specifies the side on which to show popop: [t, r, b, l]
         */
        wrap: function(el, relativeTo, orientation) {
            this.element = $(el);
            if (this.element.hasClassName('ui-pop')) return;
            this.element.addClassName('ui-pop');
            this.element.hide();
            ($('page') || document.body).appendChild(this.element);
            this.head = this.element.down('.head');
            this.body = this.element.down('.body');
            this.element.insertBefore(getDiv('t'), this.head);
            this.element.insertBefore(getDiv('m'), this.body);
            this.element.appendChild(getDiv('b'));
            this.position(relativeTo, orientation);
            this.position(relativeTo, orientation);
        },

        /**
         * position this popup. orientation is optional if relativeTo has class t, r, b, or l assigned to it.
         * @param relativeTo [optional] item relative to which to position (default: primary page container)
         * @param orientation [optional] if above specified, specifies the side on which to show popop: [t, r, b, l]
         */
        position: function(relativeTo, orientation) {
            var ox = 0, oy = 0;
            if (relativeTo) {
                relativeTo = $(relativeTo);
                if (orientation) {
                    orientation = orientation.toLowerCase();
                } else {
                    if (relativeTo.hasClassName('t')) orientation = 't';
                    if (relativeTo.hasClassName('r')) orientation = 'r';
                    if (relativeTo.hasClassName('b')) orientation = 'b';
                    if (relativeTo.hasClassName('l')) orientation = 'l';
                }
                this.element.getElementsBySelector('.arrow').invoke('remove');
                this.arrow = getDiv('arrow a' + orientation);
                this.element.appendChild(this.arrow);
                switch (orientation) {
                    default:
                    case 'r':
                        ox = relativeTo.getWidth();
                        oy = -50;
                        break;
                    case 'l':
                        ox = -this.element.getWidth();
                        oy = (relativeTo.getHeight() - this.element.getHeight())/2;
                        break;
                    case 'b':
                        ox = (relativeTo.getWidth() - this.element.getWidth())/2;
                        oy = relativeTo.getHeight() + 15;
                        break;
                    case 't':
                        ox = (relativeTo.getWidth() - this.element.getWidth())/2;
                        oy = -this.element.getHeight() - 15;
                        break;
                }
            } else {
                relativeTo = ($('page') || document.body);
                ox = (relativeTo.getWidth() - 300)/2;
                oy = 100;
            }
            Position.clone(relativeTo, this.element, {
                setHeight: false, setWidth: false,
                offsetTop: oy, offsetLeft: ox
            });

            return this;
        },

        show: function() {
            new Effect.Appear(this.element, { duration: 0.5 });
            return this;
        },

        hide: function() {
            this.element.hide();
        }
    };

    function makePopup(header, body, relativeTo, orientation) {
        var p = new Popup();
        p.create(header, body, relativeTo, orientation);
        return p;
    };

    function wrapPopup(element, relativeTo, orientation) {
        if (! element) alert('Nothing to wrap.');
        var p = new Popup();
        p.wrap(element, relativeTo, orientation);
        return p;
    };

    Event.onDOMReady(initialize);

    return {
        'addMessage': addMessage,
        'close'     : closeMessageElement,
        'makePopup' : makePopup,
        'wrapPopup' : wrapPopup,
        'SUCCESS'   : 'success',
        'ERROR'     : 'error',
        'INFO'      : 'info',
        'WARN'      : 'warn'
    }
}();

VideoLoader = (function() {
    var _flashTemplate = new Template('<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" style="#{style}"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="#{path}" /><embed src="#{path}" style="#{style}" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always"></embed></object>');
    var lastVideo = null;

    function getVideoURL(src, id, autoplay) {
        var p = null;
        switch (src) {
            case 'yt':
                p = 'http://www.youtube.com/v/' + id;
                if (autoplay) p += '&autoplay=1';
                p += '&rel=0&disablekb=1&showsearch=0&showinfo=0&iv_load_policy=3&fs=1';
                break;
            case 'vim':
                p = 'http://vimeo.com/moogaloop.swf?clip_id=' + id;
                if (autoplay) p += '&autoplay=1';
                p += '&title=false&byline=false&portrait=false';
                break;
        }
        return p;
    };

    function getVideoPlayer(src, id, autoplay, width, height) {
        if (width) width = 'width:' + width + ';'; else width = '';
        if (height) height = 'height:' + height + ';'; else height = '';
        var p = getVideoURL(src, id, autoplay);
        if (p) return _flashTemplate.evaluate({ 'path': p, 'style': width + height });
        return null;
    };

    function play(div, service, id) {
        closeVideo();
        var d = div.getDimensions();
        div.down('.vcont').hide();
        var vc = $(document.createElement('div')).addClassName('vid');
        div.appendChild(vc);
        vc.update(getVideoPlayer(service, id, true, d.width + 'px', d.height + 'px'));
        lastVideo = div;
        if (VideoLoader.onPlay) {
            try {
                VideoLoader.onPlay();
            } catch (ex) {}
        }
    };

    function closeVideo() {
        if (!lastVideo) return;
        lastVideo.down('.vcont').show();
        lastVideo.down('.vid').remove();
        lastVideo = null;
    };

    function embed(div, service, id) {
        div = $(div);
        if (!div) return;
        div.addClassName('video');
        var p = div.down('.play');
        if (!div.down('.vcont')) {
            var e, cont = $(document.createElement('div')).addClassName('vcont');
            while (div.firstChild) cont.appendChild(div.firstChild);
            div.appendChild(cont);
            if (!p) {
                p = $(document.createElement('div')).addClassName('play');
                cont.appendChild(p);
            }
        }
        Event.observe(p, 'click', play.bind(this, div, service, id));
    };

    return {
        'getVideoURL': getVideoURL,
        'getVideoPlayer': getVideoPlayer,
        'play': play,
        'embed': embed,
        'close': closeVideo
    };
})();

Event.onDOMReady(addLevel3Handlers);
Event.onDOMReady(addBarHandlers);

