/**
* @license
* jQuery Tools @VERSION Tooltip - UI essentials
*
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
*
* http://flowplayer.org/tools/tooltip/
*
* Since: November 2008
* Date: @DATE
*/
(function($) {
// static constructs
$.tools = $.tools || {version: '@VERSION'};

$.tools.tooltip = {

conf: {

// default effect variables
effect: 'toggle',
fadeOutSpeed: "fast",
predelay: 0,
delay: 30,
opacity: 1,
tip: 0,

// 'top', 'bottom', 'right', 'left', 'center'
position: ['top', 'center'],
offset: [0, 0],
relative: false,
cancelDefault: true,

// type to event mapping
events: {
def: "mouseenter,mouseleave",
input: "focus,blur",
widget: "focus mouseenter,blur mouseleave",
tooltip: "mouseenter,mouseleave"
},

// 1.2
layout: '<div/>',
tipClass: 'tooltip'
},

addEffect: function(name, loadFn, hideFn) {
effects[name] = [loadFn, hideFn];
}
};


var effects = {
toggle: [
function(done) {
var conf = this.getConf(), tip = this.getTip(), o = conf.opacity;
if (o < 1) { tip.css({opacity: o}); }
tip.show();
done.call();
},

function(done) {
this.getTip().hide();
done.call();
}
],

fade: [
function(done) {
var conf = this.getConf();
this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done);
},
function(done) {
this.getTip().fadeOut(this.getConf().fadeOutSpeed, done);
}
]
};


/* calculate tip position relative to the trigger */
function getPosition(trigger, tip, conf) {


// get origin top/left position
var top = conf.relative ? trigger.position().top : trigger.offset().top,
left = conf.relative ? trigger.position().left : trigger.offset().left,
pos = conf.position[0];

top -= tip.outerHeight() - conf.offset[0];
left += trigger.outerWidth() + conf.offset[1];

// iPad position fix
if (/iPad/i.test(navigator.userAgent)) {
top -= $(window).scrollTop();
}

// adjust Y
var height = tip.outerHeight() + trigger.outerHeight();
if (pos == 'center') { top += height / 2; }
if (pos == 'bottom') { top += height; }


// adjust X
pos = conf.position[1];
var width = tip.outerWidth() + trigger.outerWidth();
if (pos == 'center') { left -= width / 2; }
if (pos == 'left') { left -= width; }

return {top: top, left: left};
}



function Tooltip(trigger, conf) {

var self = this,
fire = trigger.add(self),
tip,
timer = 0,
pretimer = 0,
title = trigger.attr("title"),
tipAttr = trigger.attr("data-tooltip"),
effect = effects[conf.effect],
shown,

// get show/hide configuration
isInput = trigger.is(":input"),
isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"),
type = trigger.attr("type"),
evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def'];


// check that configuration is sane
if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; }

evt = evt.split(/,\s*/);
if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; }


// trigger --> show
trigger.bind(evt[0], function(e) {

clearTimeout(timer);
if (conf.predelay) {
pretimer = setTimeout(function() { self.show(e); }, conf.predelay);

} else {
self.show(e);
}

// trigger --> hide
}).bind(evt[1], function(e) {
clearTimeout(pretimer);
if (conf.delay) {
timer = setTimeout(function() { self.hide(e); }, conf.delay);

} else {
self.hide(e);
}

});


// remove default title
if (title && conf.cancelDefault) {
trigger.removeAttr("title");
trigger.data("title", title);
}

$.extend(self, {

show: function(e) {

// tip not initialized yet
if (!tip) {

// data-tooltip
if (tipAttr) {
tip = $(tipAttr);

// single tip element for all
} else if (conf.tip) {
tip = $(conf.tip).eq(0);

// autogenerated tooltip
} else if (title) {
tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body)
.hide().append(title);

// manual tooltip
} else {
tip = trigger.next();
if (!tip.length) { tip = trigger.parent().next(); }
}

if (!tip.length) { throw "Cannot find tooltip for " + trigger; }
}

if (self.isShown()) { return self; }

// stop previous animation
tip.stop(true, true);

// get position
var pos = getPosition(trigger, tip, conf);

// restore title for single tooltip element
if (conf.tip) {
tip.html(trigger.data("title"));
}

// onBeforeShow
e = e || $.Event();
e.type = "onBeforeShow";
fire.trigger(e, [pos]);
if (e.isDefaultPrevented()) { return self; }


// onBeforeShow may have altered the configuration
pos = getPosition(trigger, tip, conf);

// set position
tip.css({position:'absolute', top: pos.top, left: pos.left});

shown = true;

// invoke effect
effect[0].call(self, function() {
e.type = "onShow";
shown = 'full';
fire.trigger(e);
});


// tooltip events
var event = conf.events.tooltip.split(/,\s*/);

if (!tip.data("__set")) {

tip.bind(event[0], function() {
clearTimeout(timer);
clearTimeout(pretimer);
});

if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) {
tip.bind(event[1], function(e) {

// being moved to the trigger element
if (e.relatedTarget != trigger[0]) {
trigger.trigger(evt[1].split(" ")[0]);
}
});
}

tip.data("__set", true);
}

return self;
},

hide: function(e) {

if (!tip || !self.isShown()) { return self; }

// onBeforeHide
e = e || $.Event();
e.type = "onBeforeHide";
fire.trigger(e);
if (e.isDefaultPrevented()) { return; }

shown = false;

effects[conf.effect][1].call(self, function() {
e.type = "onHide";
fire.trigger(e);
});

return self;
},

isShown: function(fully) {
return fully ? shown == 'full' : shown;
},

getConf: function() {
return conf;
},

getTip: function() {
return tip;
},

getTrigger: function() {
return trigger;
}

});

// callbacks
$.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function(i, name) {

// configuration
if ($.isFunction(conf[name])) {
$(self).bind(name, conf[name]);
}

// API
self[name] = function(fn) {
if (fn) { $(self).bind(name, fn); }
return self;
};
});

}


// jQuery plugin implementation
$.fn.tooltip = function(conf) {

// return existing instance
var api = this.data("tooltip");
if (api) { return api; }

conf = $.extend(true, {}, $.tools.tooltip.conf, conf);

// position can also be given as string
if (typeof conf.position == 'string') {
conf.position = conf.position.split(/,?\s/);
}

// install tooltip for each entry in jQuery object
this.each(function() {
api = new Tooltip($(this), conf);
$(this).data("tooltip", api);
});

return conf.api ? api: this;
};

}) (jQuery);


/**
* @license
* jQuery Tools @VERSION Overlay - Overlay base. Extend it.
*
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
*
* http://flowplayer.org/tools/overlay/
*
* Since: March 2008
* Date: @DATE
*/
(function($) {

// static constructs
$.tools = $.tools || {version: '@VERSION'};

$.tools.overlay = {

addEffect: function(name, loadFn, closeFn) {
effects[name] = [loadFn, closeFn];
},

conf: {
close: null,
closeOnClick: true,
closeOnEsc: true,
closeSpeed: 'fast',
effect: 'default',

// since 1.2. fixed positioning not supported by IE6
fixed: !$.browser.msie || $.browser.version > 6,

left: 'center',
load: false, // 1.2
mask: null,
oneInstance: true,
speed: 'normal',
target: null, // target element to be overlayed. by default taken from [rel]
top: '10%'
}
};


var instances = [], effects = {};

// the default effect. nice and easy!
$.tools.overlay.addEffect('default',

/*
onLoad/onClose functions must be called otherwise none of the
user supplied callback methods won't be called
*/
function(pos, onLoad) {

var conf = this.getConf(),
w = $(window);

if (!conf.fixed) {
pos.top += w.scrollTop();
pos.left += w.scrollLeft();
}

pos.position = conf.fixed ? 'fixed' : 'absolute';
this.getOverlay().css(pos).fadeIn(conf.speed, onLoad);

}, function(onClose) {
this.getOverlay().fadeOut(this.getConf().closeSpeed, onClose);
}
);


function Overlay(trigger, conf) {

// private variables
var self = this,
fire = trigger.add(self),
w = $(window),
closers,
overlay,
opened,
maskConf = $.tools.expose && (conf.mask || conf.expose),
uid = Math.random().toString().slice(10);


// mask configuration
if (maskConf) {
if (typeof maskConf == 'string') { maskConf = {color: maskConf}; }
maskConf.closeOnClick = maskConf.closeOnEsc = false;
}

// get overlay and triggerr
var jq = conf.target || trigger.attr("rel");
overlay = jq ? $(jq) : null || trigger;

// overlay not found. cannot continue
if (!overlay.length) { throw "Could not find Overlay: " + jq; }

// trigger's click event
if (trigger && trigger.index(overlay) == -1) {
trigger.click(function(e) {
self.load(e);
return e.preventDefault();
});
}

// API methods
$.extend(self, {

load: function(e) {

// can be opened only once
if (self.isOpened()) { return self; }

// find the effect
var eff = effects[conf.effect];
if (!eff) { throw "Overlay: cannot find effect : \"" + conf.effect + "\""; }

// close other instances?
if (conf.oneInstance) {
$.each(instances, function() {
this.close(e);
});
}

// onBeforeLoad
e = e || $.Event();
e.type = "onBeforeLoad";
fire.trigger(e);
if (e.isDefaultPrevented()) { return self; }

// opened
opened = true;

// possible mask effect
if (maskConf) { $(overlay).expose(maskConf); }

// position & dimensions
var top = conf.top,
left = conf.left,
oWidth = overlay.outerWidth({margin:true}),
oHeight = overlay.outerHeight({margin:true});

if (typeof top == 'string') {
top = top == 'center' ? Math.max((w.height() - oHeight) / 2, 0) :
parseInt(top, 10) / 100 * w.height();
}

if (left == 'center') { left = Math.max((w.width() - oWidth) / 2, 0); }


// load effect
eff[0].call(self, {top: top, left: left}, function() {
if (opened) {
e.type = "onLoad";
fire.trigger(e);
}
});

// mask.click closes overlay
if (maskConf && conf.closeOnClick) {
$.mask.getMask().one("click", self.close);
}

// when window is clicked outside overlay, we close
if (conf.closeOnClick) {
$(document).bind("click." + uid, function(e) {
if (!$(e.target).parents(overlay).length) {
self.close(e);
}
});
}

// keyboard::escape
if (conf.closeOnEsc) {

// one callback is enough if multiple instances are loaded simultaneously
$(document).bind("keydown." + uid, function(e) {
if (e.keyCode == 27) {
self.close(e);
}
});
}


return self;
},

close: function(e) {

if (!self.isOpened()) { return self; }

e = e || $.Event();
e.type = "onBeforeClose";
fire.trigger(e);
if (e.isDefaultPrevented()) { return; }

opened = false;

// close effect
effects[conf.effect][1].call(self, function() {
e.type = "onClose";
fire.trigger(e);
});

// unbind the keyboard / clicking actions
$(document).unbind("click." + uid).unbind("keydown." + uid);

if (maskConf) {
$.mask.close();
}

return self;
},

getOverlay: function() {
return overlay;
},

getTrigger: function() {
return trigger;
},

getClosers: function() {
return closers;
},

isOpened: function() {
return opened;
},

// manipulate start, finish and speeds
getConf: function() {
return conf;
}

});

// callbacks
$.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","), function(i, name) {

// configuration
if ($.isFunction(conf[name])) {
$(self).bind(name, conf[name]);
}

// API
self[name] = function(fn) {
if (fn) { $(self).bind(name, fn); }
return self;
};
});

// close button
closers = overlay.find(conf.close || ".close");

if (!closers.length && !conf.close) {
closers = $('<a class="close"></a>');
overlay.prepend(closers);
}

closers.click(function(e) {
self.close(e);
});

// autoload
if (conf.load) { self.load(); }

}

// jQuery plugin initialization
$.fn.overlay = function(conf) {

// already constructed --> return API
var el = this.data("overlay");
if (el) { return el; }

if ($.isFunction(conf)) {
conf = {onBeforeLoad: conf};
}

conf = $.extend(true, {}, $.tools.overlay.conf, conf);

this.each(function() {
el = new Overlay($(this), conf);
instances.push(el);
$(this).data("overlay", el);
});

return conf.api ? el: this;
};

})(jQuery);


