/** * Hover balloon on elements without css and images. * * Copyright (c) 2011 Hayato Takenaka * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * @author: Hayato Takenaka (http://urin.github.com) * @version: 0.3.5 - 2013/06/26 **/ (function($) { //----------------------------------------------------------------------------- // Private //----------------------------------------------------------------------------- // Helper for meta programming var Meta = {}; Meta.pos = $.extend(["top", "bottom", "left", "right"], {camel: ["Top", "Bottom", "Left", "Right"]}); Meta.size = $.extend(["height", "width"], {camel: ["Height", "Width"]}); Meta.getRelativeNames = function(position) { var idx = { pos: { o: position, // origin f: (position % 2 == 0) ? position + 1 : position - 1, // faced p1: (position % 2 == 0) ? position : position - 1, p2: (position % 2 == 0) ? position + 1 : position, c1: (position < 2) ? 2 : 0, c2: (position < 2) ? 3 : 1 }, size: { p: (position < 2) ? 0 : 1, // parallel c: (position < 2) ? 1 : 0 // cross } }; var names = {}; for(var m1 in idx) { if(!names[m1]) names[m1] = {}; for(var m2 in idx[m1]) { names[m1][m2] = Meta[m1][idx[m1][m2]]; if(!names.camel) names.camel = {}; if(!names.camel[m1]) names.camel[m1] = {}; names.camel[m1][m2] = Meta[m1].camel[idx[m1][m2]]; } } names.isTopLeft = (names.pos.o == names.pos.p1); return names; }; // Helper class to handle position and size as numerical pixels. function NumericalBoxElement() { this.initialize.apply(this, arguments); } (function() { // Method factories var Methods = { setBorder: function(pos, isVertical) { return function(value) { this.$.css("border-" + pos.toLowerCase() + "-width", value + "px"); this["border" + pos] = value; return (this.isActive) ? digitalize(this, isVertical) : this; } }, setPosition: function(pos, isVertical) { return function(value) { this.$.css(pos.toLowerCase(), value + "px"); this[pos.toLowerCase()] = value; return (this.isActive) ? digitalize(this, isVertical) : this; } } }; NumericalBoxElement.prototype = { initialize: function($element) { this.$ = $element; $.extend(true, this, this.$.offset(), {center: {}, inner: {center: {}}}); for(var i = 0; i < Meta.pos.length; i++) { this["border" + Meta.pos.camel[i]] = parseInt(this.$.css("border-" + Meta.pos[i] + "-width")) || 0; } this.active(); }, active: function() { this.isActive = true; digitalize(this); return this; }, inactive: function() { this.isActive = false; return this; } }; for(var i = 0; i < Meta.pos.length; i++) { NumericalBoxElement.prototype["setBorder" + Meta.pos.camel[i]] = Methods.setBorder(Meta.pos.camel[i], (i < 2)); if(i % 2 == 0) NumericalBoxElement.prototype["set" + Meta.pos.camel[i]] = Methods.setPosition(Meta.pos.camel[i], (i < 2)); } function digitalize(box, isVertical) { if(isVertical == undefined) { digitalize(box, true); return digitalize(box, false); } var m = Meta.getRelativeNames((isVertical) ? 0 : 2); box[m.size.p] = box.$["outer" + m.camel.size.p](); box[m.pos.f] = box[m.pos.o] + box[m.size.p]; box.center[m.pos.o] = box[m.pos.o] + box[m.size.p] / 2; box.inner[m.pos.o] = box[m.pos.o] + box["border" + m.camel.pos.o]; box.inner[m.size.p] = box.$["inner" + m.camel.size.p](); box.inner[m.pos.f] = box.inner[m.pos.o] + box.inner[m.size.p]; box.inner.center[m.pos.o] = box.inner[m.pos.f] + box.inner[m.size.p] / 2; return box; } })(); // Adjust position of balloon body function makeupBalloon($target, $balloon, options) { $balloon.stop(true, true); var outerTip, innerTip, initTipStyle = {position: "absolute", height: "0", width: "0", border: "solid 0 transparent"}, target = new NumericalBoxElement($target), balloon = new NumericalBoxElement($balloon); balloon.setTop(-options.offsetY + ((options.position && options.position.indexOf("top") >= 0) ? target.top - balloon.height : ((options.position && options.position.indexOf("bottom") >= 0) ? target.bottom : target.center.top - balloon.height / 2))); balloon.setLeft(options.offsetX + ((options.position && options.position.indexOf("left") >= 0) ? target.left - balloon.width : ((options.position && options.position.indexOf("right") >= 0) ? target.right : target.center.left - balloon.width / 2))); if(options.tipSize > 0) { // Add hidden balloon tips into balloon body. if($balloon.data("outerTip")) { $balloon.data("outerTip").remove(); $balloon.removeData("outerTip"); } if($balloon.data("innerTip")) { $balloon.data("innerTip").remove(); $balloon.removeData("innerTip"); } outerTip = new NumericalBoxElement($("