Kang-min Liu - Widget.Balloon-0.03

Documentation | Source

NAME

Widget.Balloon - Generate pop-up balloons above objects as a hint.

SYNOPSIS

  <img id="img_one" src="foo.jpg" />
  <script type="text/javascript">
     new Widget.Balloon({
       elementId: "img_one",
       innerHTML: "<h1>Balloon A</h1><p>Lorem Ipsum</p>"
     })
  </script>

DESCRIPTION

This library provides a ballon widget that could possibly be used to provide user hint, or some fancy in-place editing area. The balloon is shown when user's mouse is over the reference object, which could be an image or a div element. The content of the balloon is given in HTML. When user moves the mouse, the balloon will follow the mouse, and when user clicks the reference object, the balloon will freeze on current position. Clicking on the reference object again will release it. Clicking on the balloon will highlight it, clicking it again will cancel the highlight.

METHODS

Here is the API of Widget.Balloon object.

new({elementId: id, element: e, innerHTML: html })

The constructor takes one argument, which is a hash with three possible keys: element, elementId, and innerHTML. The value to 'element' is the reference object, which you usually obtain by calling getElementById(). Alternatively, you could give the constructor the element's ID. If the value of innerHTML is given, the constructor calls setInnerHTML() automatically. If not, you could set the content of balloon by calling setInnerHTML() method manually.

Balloons' boundary are checked so that it always resides inside browser window.

setInnerHTML(html)

This method sets the innerHTML property of balloon.

AUTHOR

Kang-min Liu, <gugod@gugod.org>

COPYRIGHT

Copyright (c) 2006 Kang-min Liu. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as the Perl programming language (your choice of GPL or the Perl Artistic license).

/**

*/

JSAN.use("DOM.Events");

if ( typeof Widget == "undefined" )
    Widget = {};

Widget.Balloon = function(params) {
    var refNodeId = params['elementId'];
    var refNode = params['element'];
    var html = params['innerHTML'];

    var self = this._init(params);
    if ( !refNode && refNodeId ) {
        refNode = document.getElementById(refNodeId);
    }
    this.eventIds = { refNode: {}, ballonNode: {} };
    if ( refNode ) {
        this.refNode = refNode;
        this._addListeners();
    }

    if ( html )
        self.setInnerHTML(html);

    return self;
}

Widget.Balloon.VERSION = '0.03';
Widget.Balloon.EXPORT = [];
Widget.Balloon.EXPORT_OK = [];
Widget.Balloon.EXPORT_TAGS = {};

Widget.Balloon.prototype.balloon_divs = [];

Widget.Balloon.prototype.balloon_master;

Widget.Balloon.prototype._init = function( params ) {
    var id = this.balloon_divs.length + 1;
    var div = document.createElement('div');
    div.id = 'balloon_' + id;
    div.setAttribute('class', 'balloon-div');
    with ( div.style ) {
        zIndex = 65535; // Large enough to be above everything.
        borderWidth = "1px";
        borderStyle = "solid";
        borderColor = "#000";
        position = "absolute";
        width = "200px";
        height = "200px";
        background = "#fff";
        display = "block";
    }
    this.balloon_divs.push(div);
    this.div = div;
    this.is_hidden = true;
    this.is_freezed = false;
    this.is_highlighted = false;
    this.params = params;
    return this;
}

Widget.Balloon.prototype.setInnerHTML = function(html) {
    this.div.innerHTML = html;
}

Widget.Balloon.prototype.setPosition = function(e) {
    var threshold = 10;
    var top =  e.pageY + threshold;
    var left = e.pageX + threshold;

    if ( e.clientX + this.div.offsetWidth > window.innerWidth ) {
        left = e.pageX - this.div.offsetWidth - threshold;
    }

    if ( e.clientY + this.div.offsetHeight > window.innerHeight ) {
        top = e.pageY - this.div.offsetHeight - threshold
    }

    this.div.style.top = top + "px";
    this.div.style.left = left + "px";

    this.debug(
               "style.left:" + left + "\n" +
               "style.top: " + top  + "\n" +
               "pageX:" + e.pageX + "\n" +
               "pageY: " + e.pageY  + "\n" +
               "div.left:" + this.div.style.left + "\n" +
               "div.top: " + this.div.style.top  + "\n" +
               "innerWidth: " + window.innerWidth  + "\n" +
               "innerHeight:" + window.innerHeight
               );
    
}

Widget.Balloon.prototype.show = function(e) {
    if ( this.is_hidden ) {
        document.body.appendChild( this.div );
        this.is_hidden = false;
    }
    this.refresh(e);
}

Widget.Balloon.prototype.hide = function(e) {
    if ( this.is_hidden ) return;
        document.body.removeChild( this.div );
    this.is_hidden = true;
    this.unhighlight();
}

Widget.Balloon.prototype.refresh = function(e) {
    if ( ! this.is_freezed )
        this.setPosition(e);
}

Widget.Balloon.prototype.highlight = function(e) {
    this.is_highlighted = true;
    this.div.style.backgroundColor = "#fbffcc";
}

Widget.Balloon.prototype.unhighlight = function(e) {
    this.is_highlighted = false;
    this.div.style.backgroundColor = "#fff";
}

Widget.Balloon.prototype.debug = function(str) {
    var e = document.getElementById("debug");
    if ( e ) {
        e.innerHTML = '<pre>' + str + '</pre>';
    }
}

Widget.Balloon.prototype.freeze = function(e) {
    if ( this.is_freezed ) {
        this._addListeners();
        this.is_freezed = false;
        return;
    }
    
    this.is_freezed = true;
    this._removeListeners({ refNode: ['mouseover', 'mousemove', 'mouseout'] });
}

Widget.Balloon.prototype._listeners = function () {
    var self = this;
    return {
        refNode: {
            mouseover: function(e) { self.show(e);   },
            mouseout:  function(e) { self.hide(e);   },
            mousemove: function(e) { self.refresh(e) },
            click:     function(e) {
                if ( self.is_freezed ) {
                    self.hide(e);
                } else if ( self.is_hidden ) {
                    self.show(e);
                }
                self.freeze(e);
                self.refresh(e);
            }
        },
        ballonNode: {
            click:     function(e) {
                if ( self.is_highlighted )
                    self.unhighlight();
                else
                    self.highlight();
            }
        }
    };
}

Widget.Balloon.prototype._addListeners = function() {
    var self = this;
    var listeners = this._listeners();

    for (id in listeners["ballonNode"]) {
        if ( this.eventIds["ballonNode"][id] )
            continue;
        this.eventIds["ballonNode"][id]
            = DOM.Events.addListener(this.div, id,
                                     listeners["ballonNode"][id]);
    }
    
    var refNode = this.refNode;
    if ( ! refNode )
        return;
    
    for (id in listeners["refNode"]) {
        if ( this.eventIds["refNode"][id] )
            continue;
        this.eventIds["refNode"][id]
            = DOM.Events.addListener(refNode, id, listeners["refNode"][id]);
    }
}

Widget.Balloon.prototype._removeListeners = function(ids) {
    if ( !ids ) {
        ids = [];
        for(var i in this.eventIds) {
            ids[i] = this.eventIds[i];
        }
    }
    
    for(var node in ids) {
        var events = ids[node];
        for ( var i = 0 ; i < events.length ; i++ ) {
            var id = events[i];
            if ( ! ( this.eventIds[node] && this.eventIds[node][id] ) )
                continue;
            DOM.Events.removeListener(this.eventIds[node][id]);
            this.eventIds[node][id] = false;
        }
    }
}

/* Two currently unused functions. */

/*
Widget.Balloon.prototype._findPosX = function(obj) {
    var curleft = 0;
    if (obj.offsetParent) {
	while (obj.offsetParent) {
	    curleft += obj.offsetLeft
	    obj = obj.offsetParent;
	}
    }
    else if (obj.x)
	curleft += obj.x;
    return curleft;
}

Widget.Balloon.prototype._findPosY = function(obj) {
    var curtop = 0;
    if (obj.offsetParent) {
	while (obj.offsetParent) {
	    curtop += obj.offsetTop
	    obj = obj.offsetParent;
	}
    }
    else if (obj.y)
	curtop += obj.y;
    return curtop;
}
*/

/**


*/