Tuesday, June 2, 2009

Add Tooltip on disabled button


<div style="position:relative;text-align:right;">
<span id="Configure_overlay" style="z-index:2; position:absolute; background-color:white; opacity:0.60; filter:alpha(opacity=60); width:125px; height:20px; display:none;"></span>
<input type="button" name="Configure" id="configure" value="Configure" onclick="clickMe()"style="font-size: 7pt;width:125px">
</div>

The overlay must be absolute positioned to be placed right on top of the button. Because the default position is static which means overlay will occupy space in document flow. For postion:absolute, the element is removed from the document. The surrounding relative div is required to deal with scroll case. Without it, the overlay is relative to the body and is fixed whereas the button may move in a scrolling area.

function disableCustomConfig(rId, msg) {
var customConfigOverlay = document.getElementById("Configure_overlay");
var customConfigTooltip = new YAHOO.widget.Tooltip("customConfigTooltip", {
context:"Configure_overlay", text:msg, showDelay:0, hideDelay:0, disabled: false
});

customConfigOverlay.style.display = "";
customConfigTooltip.cfg.setProperty("text",msg);
}

Wednesday, May 6, 2009

Javascript Closures

A re-introduction to JavaScript and Simplify Scope with Javascript Closures have a good introduction on the concept of javascript closures. A closure is the combination of a function and the scope object in which it was created. Closures let you save state. The memory of a closure can be freed only when the returned callMe is no longer accessible.

function createClosure(scope_) {
    var origScope = scope_;

    var callMe = function() {   // inner private function
        origScope.somefunc();
    }
    return callMe;    // inner function returned
}

// preserve 'this'
someobject.someproperty = createClosure(this);

function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}

x = makeAdder(5);  // each call to the outer function create a closure
y = makeAdder(20);

x(6)    // return 11, with scope object a=5, 
        // a closure preserves the arguments and variables in all scopes it contains. 
y(7)    // return 27, with scope object a=20

Any nested function forms closure. It doesn't have to be returned, it can be an anonymous inner function as below. We should try to avoid closure whenever possible, it may potentially cause memory leak if there exists circular reference between JS object and DOM object.
function assignOnclick(element) {
   element.onclick = function() {
      this.style.backgroundColor = 'blue';
   };
}

See: Functions and function scope

Tuesday, May 5, 2009

Rounded Box



<div class="boxcontainer">
<div class="t"><div class="b"><div class="l"><div class="r">
<div class="bl"><div class="br"><div class="tl"><div class="tr">
<div class="boxlabel">{LABEL}</div>
<div class="boxcontent">{CONTENT}</div>
</div></div></div></div>
</div></div></div></div>
</div>


.t {background: url(/images/dot.jpg) 0 0 repeat-x;}
.b {background: url(/images/dot.jpg) 0 100% repeat-x;}
.l {background: url(/images/dot.jpg) 0 0 repeat-y;}
.r {background: url(/images/dot.jpg) 100% 0 repeat-y;}
.bl {background: url(/images/bl.jpg) 0 100% no-repeat;}
.br {background: url(/images/br.jpg) 100% 100% no-repeat;}
.tl {background: url(/images/tl.jpg) 0 0 no-repeat;}
.tr {background: url(/images/tr.jpg) 100% 0 no-repeat; padding: 5px;}

.boxlabel {
float:left;
display:inline;
position:relative;
top:-5px;
left:5px;
padding: 0px 2px 0px 2px;
background-color:#FFFFFF;
color:#1E90FF;
font-size:10;
font-family: Arial;
}

.boxcontent {
position:relative;
top:-5px;
text-align: left;
}

.boxcontainer {
margin: 5px;
}

The boxlabel float left with relative/top/left position to adjust its position. And its background-color is set to make the background opaque, otherwise, it is transparent and the below border line is shown.

Also, note that we have a padding on the last img div, to avoid text inside the box overlap with image.

See: CSS and Round Corners: Build Boxes with Curves

Tuesday, April 28, 2009

object references an unsaved transient instance

    Match m = new Match();
    m.setPlayDate(playDate);

    for (int playerId : playerIds) {
        Player p = playerDao.read(playerId);
     
        // update player's stat
        PlayerStats ps = playerStatsDao.findByPlayerAndYear(playerId, year);
        ps.increment();
        playerStatsDao.saveOrUpdate(ps);
   
        m.addParticipant(p);
    }
  
    matchDao.persist(m);

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: poker.db.model.Match.

On 2nd iteration of the loop, in line 8, while querying playerstats, it threw the above exception. It was doing a *flush* before the query since playerstats was dirty atm.

I really made an effort to dig into it. It turns out that it was caused by the *inverse* setting of a many-to-many relationship. I used to think it doesn't matter as long as you set either side of it.

In Class Match:
public void addParticipant(Player p) {
    getParticipants().add(p);
    p.getMatches().add(this);
}

PlayStats * <---> 1 Player * <---> * Match
All relationships are bi-directional. PlayStats is owner side by default for the many-to-one. And I set (inverse=true) on Player side by rolling a dice. I thought when I called match.addPaticipant(), both collections are updated and the relation is built. It shouldn't make any difference by setting inverse.

I imagine that the reference link is uni-directional for underlying presistence with the inverse setting. When Player is the owner of this many-to-many, the link is PlayerStats ->Player->Match, since Match object is a transient object (not saved yet) when we tried to save/update PlayerStats, it threw the above exception. However, if we change the owner side to Match, the reference link is broken, PlayerStats->Player, Match->Player, save/update PlayerStats has nothing to do with the state of Match object anymore. Thus it runs perfectly!

* Inverse=true means relationship owner to avoid unnecessary update. Basically on many side, insert a new record already with FK value, you don't need another update statement to set it.
http://www.mkyong.com/hibernate/inverse-true-example-and-explanation/

Alternatively, the second solution is to set FlushMode of query in playerStatsDao.findByPlayerAndYear().

public PlayerStats findByPlayerAndYear(int playerId, int year) {
    String queryString = "from PlayerStats ps where ps.player.id = :playerId "
                          + "and ps.year = :year";
    Query query = getSession().createQuery(queryString)
                .setInteger("playerId", playerId)
                .setInteger("year", year)
                .setFlushMode(FlushMode.COMMIT);
    return (PlayerStats) query.uniqueResult();
}

By default, FlushMode is AUTO, flush synchronize the in-memory persistent object with underlying database store by running the SQLs, the changes not visible to others until tx.commit() though.

Note that flush occurs in three cases: transcation commit, session.flush() invoked or before the query. By changing flush to tx commit time, we avoid the "object references an unsaved transient instance" issue. It is also good for performance.