1 // Copyright (c) 2006 spinelz.org (http://script.spinelz.org/)
\r
3 // This code is substantially based on code from script.aculo.us which has the
\r
4 // following copyright and permission notice
\r
6 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
\r
8 // Permission is hereby granted, free of charge, to any person obtaining
\r
9 // a copy of this software and associated documentation files (the
\r
10 // "Software"), to deal in the Software without restriction, including
\r
11 // without limitation the rights to use, copy, modify, merge, publish,
\r
12 // distribute, sublicense, and/or sell copies of the Software, and to
\r
13 // permit persons to whom the Software is furnished to do so, subject to
\r
14 // the following conditions:
\r
16 // The above copyright notice and this permission notice shall be
\r
17 // included in all copies or substantial portions of the Software.
\r
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
\r
27 var Balloon = Class.create()
\r
28 Balloon.classNames = {
\r
29 tooltip: 'balloon_tooltip',
\r
31 topLeft: 'balloon_top_left',
\r
32 topMiddle: 'balloon_top_middle',
\r
33 topRight: 'balloon_top_right',
\r
34 middle: 'balloon_middle',
\r
35 middleLeft: 'balloon_middle_left',
\r
36 middleRight: 'balloon_middle_right',
\r
37 middleLeftRowT: 'balloon_middle_left_row',
\r
38 middleLeftRowB: 'balloon_middle_left_row',
\r
39 middleRightRowT: 'balloon_middle_right_row',
\r
40 middleRightRowB: 'balloon_middle_right_row',
\r
41 leftArrow: 'balloon_left_arrow',
\r
42 rightArrow: 'balloon_right_arrow',
\r
43 leftUpArrow: 'balloon_left_up_arrow',
\r
44 leftDownArrow: 'balloon_left_down_arrow',
\r
45 rightUpArrow: 'balloon_right_up_arrow',
\r
46 rightDownArrow: 'balloon_right_down_arrow',
\r
47 body: 'balloon_body',
\r
48 bottom: 'balloon_bottom',
\r
49 bottomLeft: 'balloon_bottom_left',
\r
50 bottomMiddle: 'balloon_bottom_middle',
\r
51 bottomRight: 'balloon_bottom_right'
\r
53 Balloon.allBalloons = [];
\r
54 Balloon.closeAll = function(){
\r
55 Balloon.allBalloons.each(function(b){
\r
59 Balloon.eventSetting = false;
\r
60 Balloon.prototype = {
\r
61 initialize : function (target, message){
\r
62 this.target = $(target);
\r
64 this.options = Object.extend({
\r
65 cssPrefix: 'custom_',
\r
66 trigger: this.target,
\r
67 tipId: this.target.id + '_balloon',
\r
71 }, arguments[2] || {});
\r
73 var customCss = CssUtil.appendPrefix(this.options.cssPrefix, Balloon.classNames);
\r
74 this.classNames = new CssUtil([Balloon.classNames, customCss]);
\r
76 this.tipNode = this._buildTipNode(message);
\r
77 Element.hide(this.tipNode);
\r
78 this._setMessage(message);
\r
79 document.body.appendChild(this.tipNode);
\r
81 Balloon.allBalloons.push(this)
\r
85 _setEvent: function() {
\r
87 this.options.events.each(function(e) {
\r
88 Event.observe(self.options.trigger, e, self.open.bindAsEventListener(self));
\r
91 Event.observe(this.tipNode, 'click', this.close.bind(this), true)
\r
93 if (!Balloon.eventSetting) {
\r
94 Event.observe(document, 'click', Balloon.closeAll, true);
\r
95 Balloon.eventSetting = true;
\r
99 _buildTipNode : function() {
\r
100 var tipNode = Builder.node('div', {id: this.options.tipId});
\r
101 this.classNames.addClassNames(tipNode, 'tooltip');
\r
102 tipNode.appendChild(this._buildTop());
\r
103 tipNode.appendChild(this._buildMiddle());
\r
104 tipNode.appendChild(this._buildBottom());
\r
108 _setMessage: function(message) {
\r
109 var type = message.constructor;
\r
110 if (type == String) {
\r
111 this.body.innerHTML = message;
\r
112 } else if (type == Object) {
\r
113 this.body.appendChild(message);
\r
117 _buildTop: function() {
\r
118 return this._buildMulti('top', ['topLeft', 'topMiddle', 'topRight'], true);
\r
121 _buildBottom: function() {
\r
122 return this._buildMulti('bottom', ['bottomLeft', 'bottomMiddle', 'bottomRight'], true);
\r
125 _buildMiddle: function() {
\r
126 this.middle = Builder.node('div');
\r
127 this.classNames.addClassNames(this.middle, 'middle');
\r
128 this.middle.appendChild(
\r
129 this._buildMulti('middleLeft', ['middleLeftRowT', 'leftArrow', 'middleLeftRowB'], true));
\r
130 this.middle.appendChild(this._buildMulti('body', [], true));
\r
131 this.middle.appendChild(
\r
132 this._buildMulti('middleRight', ['middleRightRowT', 'rightArrow', 'middleRightRowB'], true));
\r
133 return this.middle;
\r
136 _buildMulti: function(main, subs, hold) {
\r
137 var topNode = Builder.node('div');
\r
138 this.classNames.addClassNames(topNode, main);
\r
139 if (hold) this[main] = topNode;
\r
142 subs.each(function(s) {
\r
143 node = Builder.node('div');
\r
144 self.classNames.addClassNames(node, s);
\r
145 topNode.appendChild(node);
\r
146 if (hold) self[s] = node;
\r
151 _setPosition: function() {
\r
152 var scrollPosition = Position.realOffset(this.tipNode);
\r
153 var screenWidth = document.documentElement.clientWidth;
\r
154 var screenHeight = document.documentElement.clientHeight;
\r
156 var positionList = Position.cumulativeOffset(this.target);
\r
157 var dimension = Element.getDimensions(this.target);
\r
158 var tipNodeLeft = Math.round(positionList[0] + dimension.width);
\r
159 var tipDimension = Element.getDimensions(this.tipNode);
\r
160 var tipNodeTop = Math.round(positionList[1] - tipDimension.height / 2);
\r
162 var addLR = 'left';
\r
163 var remLR = 'right';
\r
165 if((tmpY = tipNodeTop - scrollPosition[1]) < 0) {
\r
166 tipNodeTop -= tmpY;
\r
168 if( (tipNodeLeft+tipDimension.width) > (screenWidth+scrollPosition[0]) ) {
\r
169 tipNodeLeft = Math.round(positionList[0] - tipDimension.width);
\r
174 var y = positionList[1] - tipNodeTop;
\r
175 this._setArrow(addLR, y);
\r
176 this._unsetArrow(remLR);
\r
178 Element.setStyle(this.tipNode, {
\r
179 top: tipNodeTop + 'px',
\r
180 left: tipNodeLeft + 'px',
\r
181 zIndex: ZindexManager.getIndex()
\r
185 _setArrow: function(lr, y) {
\r
186 var headerH = (this.options.height - this.middleH) / 2;
\r
187 var topH, bottomH, h, ud;
\r
188 var minH = 10; // for ie
\r
189 if (lr == 'left') {
\r
190 h = this.middleH - this.leftArrowH;
\r
192 h = this.middleH - this.rightArrowH;
\r
196 bottomH = h - topH;
\r
198 } else if ((this.middleH + headerH) < y) {
\r
200 topH = h - bottomH;
\r
203 topH = y - headerH;
\r
204 topH = (topH < minH) ? minH : topH;
\r
205 bottomH = h - topH;
\r
208 if (lr == 'left') {
\r
210 this.classNames.refreshClassNames(this.leftArrow, 'leftUpArrow');
\r
211 Element.setStyle(this.leftArrow, {height: this.leftArrowH + 'px'});
\r
212 Element.setStyle(this.middleLeftRowT, {height: topH + 'px'});
\r
213 Element.setStyle(this.middleLeftRowB, {height: bottomH + 'px'});
\r
215 this.classNames.refreshClassNames(this.leftArrow, 'leftDownArrow');
\r
216 Element.setStyle(this.leftArrow, {height: this.leftArrowH + 'px'});
\r
217 Element.setStyle(this.middleLeftRowT, {height: topH + 'px'});
\r
218 Element.setStyle(this.middleLeftRowB, {height: bottomH + 'px'});
\r
222 this.classNames.refreshClassNames(this.rightArrow, 'rightUpArrow');
\r
223 Element.setStyle(this.rightArrow, {height: this.rightArrowH + 'px'});
\r
224 Element.setStyle(this.middleRightRowT, {height: topH + 'px'});
\r
225 Element.setStyle(this.middleRightRowB, {height: bottomH + 'px'});
\r
227 this.classNames.refreshClassNames(this.rightArrow, 'rightDownArrow');
\r
228 Element.setStyle(this.rightArrow, {height: this.rightArrowH + 'px'});
\r
229 Element.setStyle(this.middleRightRowT, {height: topH + 'px'});
\r
230 Element.setStyle(this.middleRightRowB, {height: bottomH + 'px'});
\r
235 _unsetArrow: function(direction) {
\r
236 if (direction == 'left') {
\r
237 var h = (this.middleH - this.leftArrowH) / 2;
\r
238 this.classNames.refreshClassNames(this.leftArrow, 'middleLeftRowB');
\r
239 Element.setStyle(this.leftArrow, {height: this.leftArrowH + 'px'});
\r
240 Element.setStyle(this.middleLeftRowT, {height: h + 'px'});
\r
241 Element.setStyle(this.middleLeftRowB, {height: h + 'px'});
\r
243 var h = (this.middleH - this.rightArrowH) / 2;
\r
244 this.classNames.refreshClassNames(this.rightArrow, 'middleRightRowB');
\r
245 Element.setStyle(this.rightArrow, {height: this.rightArrowH + 'px'});
\r
246 Element.setStyle(this.middleRightRowT, {height: h + 'px'});
\r
247 Element.setStyle(this.middleRightRowB, {height: h + 'px'});
\r
251 _setSize: function() {
\r
252 var width = this.options.width;
\r
253 var height = this.options.height;
\r
254 Element.setStyle(this.tipNode, {
\r
255 width: width + 'px',
\r
256 height: height + 'px'
\r
259 var topH = parseInt(Element.getStyle(this.top, 'height'));
\r
260 var bottomH = parseInt(Element.getStyle(this.bottom, 'height'));
\r
261 var middleH = this.options.height - topH - bottomH;
\r
263 var style = {height: middleH + 'px'};
\r
264 Element.setStyle(this.middle, style);
\r
265 Element.setStyle(this.middleLeft, style);
\r
266 Element.setStyle(this.middleRight, style);
\r
267 Element.setStyle(this.body, style);
\r
269 this.leftArrowH = parseInt(Element.getStyle(this.leftArrow, 'height'));
\r
270 this.rightArrowH = parseInt(Element.getStyle(this.rightArrow, 'height'));
\r
271 this.middleH = middleH;
\r
274 open : function() {
\r
275 if (!Element.visible(this.tipNode)) {
\r
276 this._setPosition();
\r
277 Effect.Appear(this.tipNode);
\r
281 close : function(){
\r
282 if (Element.visible(this.tipNode)) {
\r
283 this._setPosition();
\r
284 Effect.Fade(this.tipNode);
\r