blob: 4aa7952453654599b21464bb7ab3416362b14a67 [file] [log] [blame]
/*
Copyright (c) 2004-2006, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/community/licensing.shtml
*/
dojo.provide("dojo.lfx.rounded");
dojo.require("dojo.lang.common");
dojo.require("dojo.html.common");
dojo.require("dojo.html.style");
dojo.require("dojo.html.display");
dojo.require("dojo.html.layout");
/* Port of curvyCorners, by Cameron Cooke and Tim Hutchison.
* Original port done by Brian Lucas.
* Refactor and function by trt.
*/
dojo.lfx.rounded = function(/* object */settings /* ... */){
// summary
// Creates a set of rounded corners based on settings.
var options={
validTags:settings.validTags || ["div"], // tags we can apply this to
autoPad:settings.autoPad!=null ? settings.autoPad : true, // automatically pad
antiAlias:settings.antiAlias!=null ? settings.antiAlias : true, // anti-alias corners
radii:{ // corner radii
tl:(settings.tl && settings.tl.radius!=null) ? settings.tl.radius:5,
tr:(settings.tr && settings.tr.radius!=null) ? settings.tr.radius:5,
bl:(settings.bl && settings.bl.radius!=null) ? settings.bl.radius:5,
br:(settings.br && settings.br.radius!=null) ? settings.br.radius:5
}
};
// get the node list to operate on.
var nodes;
if(typeof(arguments[1]) == "string"){
// a CSS classname was passed, grab a node list.
nodes=dojo.html.getElementsByClass(arguments[1]);
} else if(dojo.lang.isArrayLike(arguments[1])){
// we assume that the second argument is an array of nodes to apply this to.
nodes=arguments[1];
for(var i=0; i<nodes.length; i++){ nodes[i]=dojo.byId(nodes[i]); }
}
if(nodes.length == 0) return; // don't bother.
////////////////////////////////////////////////////////////////////
for(var i=0; i<nodes.length; i++){
dojo.lfx.rounded.applyCorners(options, nodes[i]);
}
};
// can call this directly if one wants.
dojo.lfx.rounded.applyCorners = function(/* object */options, /* HTMLElement */node){
// summary
// Rounds corners based on options to passed node.
var top = null;
var bottom = null;
var contentNode = null;
var fns=dojo.lfx.rounded._fns;
// node details
var width = node.offsetWidth;
var height = node.offsetHeight;
var borderWidth = parseInt(dojo.html.getComputedStyle(node, "border-top-width"));
var borderColor = dojo.html.getComputedStyle(node, "border-top-color");
var color = dojo.html.getComputedStyle(node, "background-color");
var bgImage = dojo.html.getComputedStyle(node, "background-image");
var position = dojo.html.getComputedStyle(node, "position");
var padding = parseInt(dojo.html.getComputedStyle(node, "padding-top"));
// formatting details
// TODO: use Dojo equivilents for these if exists.
var format={
height : height,
width : width,
borderWidth : borderWidth,
color : fns.getRGB(color),
padding : padding,
borderColor : fns.getRGB(borderColor),
borderString : borderWidth + "px" + " solid " + fns.getRGB(borderColor),
bgImage : ((bgImage != "none")? bgImage : ""),
content : node.innerHTML
};
if(!dojo.html.isPositionAbsolute(node)){ node.style.position="relative"; }
node.style.padding="0px";
if(dojo.render.html.ie && width=="auto" && height=="auto"){ node.style.width="100%"; }
if(options.autoPad && format.padding>0){
node.innerHTML="";
}
var topHeight=Math.max(options.radii.tl, options.radii.tr);
var bottomHeight=Math.max(options.radii.bl, options.radii.br);
// build the containers.
if(options.radii.tl || options.radii.tr){
top = document.createElement("div");
top.style.width="100%";
top.style.fontSize="1px";
top.style.overflow="hidden";
top.style.position="absolute";
top.style.paddingLeft=format.borderWidth+"px";
top.style.paddingRight=format.borderWidth+"px";
top.style.height=topHeight+"px";
top.style.top=(0-topHeight)+"px";
top.style.left=(0-format.borderWidth)+"px";
node.appendChild(top);
}
if(options.radii.bl || options.radii.br){ // bottom
bottom = document.createElement("div");
bottom.style.width="100%";
bottom.style.fontSize="1px";
bottom.style.overflow="hidden";
bottom.style.position="absolute";
bottom.style.paddingLeft=format.borderWidth+"px";
bottom.style.paddingRight=format.borderWidth+"px";
bottom.style.height=bottomHeight+"px";
bottom.style.bottom=(0-bottomHeight)+"px";
bottom.style.left=(0-format.borderWidth)+"px";
node.appendChild(bottom);
}
// turn off the borders
if(top){ node.style.borderTopWidth = "0px"; }
if(bottom){ node.style.borderBottomWidth = "0px"; }
// do the corners
var corners = ["tr", "tl", "br", "bl"];
for(var i=0; i<corners.length; i++){
var cc=corners[i];
if(options.radii[cc]==0){
// fill up the space with a div.
if((cc.charAt(0)=="t"&&top) || (cc.charAt(0)=="b"&&bottom)){
var corner=document.createElement("div");
corner.style.position="relative";
corner.style.fontSize="1px;";
corner.style.overflow="hidden";
if(format.bgImage==""){
corner.style.backgroundColor=format.color;
} else {
corner.style.backgroundImage=format.bgImage;
}
switch(cc){
case "tl":{
corner.style.height=topHeight-format.borderWidth+"px";
corner.style.marginRight=options.radii[cc]-(format.borderWidth*2)+"px";
corner.style.borderLeft=format.borderString;
corner.style.borderTop=format.borderString;
corner.style.left=-format.borderWidth+"px";
break;
}
case "tr":{
corner.style.height=topHeight-format.borderWidth+"px";
corner.style.marginLeft=options.radii[cc]-(format.borderWidth*2)+"px";
corner.style.borderRight=format.borderString;
corner.style.borderTop=format.borderString;
corner.style.backgroundPosition="-"+(topHeight-format.borderWidth)+"px 0px";
corner.style.left=format.borderWidth+"px";
break;
}
case "bl":{
corner.style.height=bottomHeight-format.borderWidth+"px";
corner.style.marginRight=options.radii[cc]-(format.borderWidth*2)+"px";
corner.style.borderLeft=format.borderString;
corner.style.borderBottom=format.borderString;
corner.style.left=format.borderWidth+"px";
corner.style.backgroundPosition="-"+format.borderWidth+"px -"+(format.height+(bottomHeight+format.borderWidth))+"px";
break;
}
case "br":{
corner.style.height=bottomHeight-format.borderWidth+"px";
corner.style.marginLeft=options.radii[cc]-(format.borderWidth*2)+"px";
corner.style.borderRight=format.borderString;
corner.style.borderBottom=format.borderString;
corner.style.left=format.borderWidth+"px";
corner.style.backgroundPosition="-"+(bottomHeight+format.borderWidth)+"px -"+(format.height+(bottomHeight+format.borderWidth))+"px";
break;
}
}
}
} else {
// NB: this version will not do the caching they built into the
// current version of curvyCorners.
var corner=document.createElement("div");
corner.style.height=options.radii[cc]+"px";
corner.style.width=options.radii[cc]+"px";
corner.style.position="absolute";
corner.style.fontSize="1px";
corner.style.overflow="hidden";
var borderRadius=Math.floor(options.radii[cc] - format.borderWidth);
for(var x=0, j=options.radii[cc]; x<j; x++){
// figure out y coords
var y1=Math.floor(Math.sqrt(Math.pow(borderRadius,2)-Math.pow((x+1),2)))-1;
if((x+1) >= borderRadius){ var y1=-1; }
var y2=Math.ceil(Math.sqrt(Math.pow(borderRadius,2)-Math.pow(x,2)));
if(x >= borderRadius){ y2=-1; }
var y3=Math.floor(Math.sqrt(Math.pow(j,2)-Math.pow((x+1),2)))-1;
if((x+1) >= j){ y3=-1; }
var y4=Math.ceil(Math.sqrt(Math.pow(j, 2)-Math.pow(x, 2)));
if(x >= j){ y4=-1; }
// start drawing
if(y1 > -1){
fns.draw(x, 0, format.color, 100, (y1+1), corner, -1, j, topHeight, format);
}
// cycle the y-axis
for(var y=(y1+1); y<y2; y++){
if(options.antiAlias){
if(format.bgImage != ""){
var fract=fns.fraction(x, y, borderRadius)*100;
if(fract < 30){
fns.draw(x, y, format.borderColor, 100, 1, corner, 0, options.radii[cc], topHeight, format);
} else {
fns.draw(x, y, format.borderColor, 100, 1, corner, -1, options.radii[cc], topHeight, format);
}
} else {
var clr=fns.blend(format.color, format.borderColor, fns.fraction(x, y, borderRadius));
fns.draw(x, y, clr, 100, 1, corner, 0, options.radii[cc], topHeight, format);
}
}
}
// bar for the border
if(options.antiAlias){
if(y3 >= y2){
if(y2 == -1){ y2 = 0; }
fns.draw(x, y2, format.borderColor, 100, (y3-y2+1), corner, 0, 0, topHeight, format)
} else {
if(y3 >= y1){
fns.draw(x, (y1+1), format.borderColor, 100, (y3-y1), corner, 0, 0, topHeight, format);
}
}
for(var y=(y3+1); y<y4; y++){
fns.draw(x, y, format.borderColor, (fns.fraction(x, y, j)*100), 1, corner, (format.borderWidth>0 ? 0:-1), options.radii[cc], topHeight, format);
}
} else {
y3=y1;
}
}
// reposition pixels if not the bottom right.
if(cc != "br"){
for(var t=0, k=corner.childNodes.length; t<k; t++){
var bar=corner.childNodes[t];
var barTop = parseInt(dojo.html.getComputedStyle(bar, "top"));
var barLeft = parseInt(dojo.html.getComputedStyle(bar, "left"));
var barHeight = parseInt(dojo.html.getComputedStyle(bar, "height"));
// reposition.
if(cc.charAt(1)=="l"){
bar.style.left = (options.radii[cc]-barLeft-1)+"px";
}
if(cc=="tr"){
bar.style.top = (options.radii[cc]-barHeight-barTop)+"px";
bar.style.backgroundPosition="-"+Math.abs((format.width-options.radii[cc]+format.borderWidth)+barLeft)
+"px -"+Math.abs(options.radii[cc]-barHeight-barTop-format.borderWidth)+"px";
} else if (cc=="tl"){
bar.style.top = (options.radii[cc]-barHeight-barTop)+"px";
bar.style.backgroundPosition="-"+Math.abs((options.radii[cc]-barLeft-1)-format.borderWidth)
+"px -"+Math.abs(options.radii[cc]-barHeight-barTop-format.borderWidth)+"px";
} else {
bar.style.backgroundPosition="-"+Math.abs((options.radii[cc]+barLeft)+format.borderWidth)
+"px -"+Math.abs((format.height+options.radii[cc]+barTop)-format.borderWidth)+"px";
}
}
}
}
if(corner){
// position the container.
var psn=[];
if(cc.charAt(0)=="t"){ psn.push("top"); }
else { psn.push("bottom"); }
if(cc.charAt(1)=="l"){ psn.push("left"); }
else { psn.push("right"); }
if(corner.style.position=="absolute"){
for(var z=0; z<psn.length; z++){ corner.style[psn[z]]="0px"; }
}
if(psn[0]=="top"){
if(top){ top.appendChild(corner); }
} else {
if(bottom){ bottom.appendChild(corner); }
}
}
}
// draw fillers.
var diff={
t: Math.abs(options.radii.tl - options.radii.tr),
b: Math.abs(options.radii.bl - options.radii.br)
};
for(var z in diff){
var smaller=(options.radii[z+"l"]<options.radii[z+"r"] ? z+"l":z+"r");
var filler=document.createElement("div");
filler.style.height=diff[z]+"px";
filler.style.width=options.radii[smaller]+"px";
filler.style.position="absolute";
filler.style.fontSize="1px";
filler.style.overflow="hidden";
filler.style.backgroundColor=format.color;
switch(smaller){
case "tl":{
filler.style.bottom="0px";
filler.style.left="0px";
filler.style.borderLeft=format.borderString;
top.appendChild(filler);
break;
}
case "tr":{
filler.style.bottom="0px";
filler.style.right="0px";
filler.style.borderRight=format.borderString;
top.appendChild(filler);
break;
}
case "bl":{
filler.style.top="0px";
filler.style.left="0px";
filler.style.borderLeft=format.borderString;
bottom.appendChild(filler);
break;
}
case "br":{
filler.style.top="0px";
filler.style.right="0px";
filler.style.borderRight=format.borderString;
bottom.appendChild(filler);
break;
}
}
var fillBar=document.createElement("div");
fillBar.style.position="relative";
fillBar.style.fontSize="1px";
fillBar.style.overflow="hidden";
fillBar.style.backgroundColor=format.color;
fillBar.style.backgroundImage=format.bgImage;
if(z=="t"){
if(top){
if(options.radii.tl && options.radii.tr){
fillBar.style.height=(topHeight-format.borderWidth) + "px";
fillBar.style.marginLeft=(options.radii.tl-format.borderWidth)+"px";
fillBar.style.marginRight=(options.radii.tr-format.borderWidth)+"px";
fillBar.style.borderTop=format.borderString;
if(format.bgImage!=""){
fillBar.style.backgroundPosition="-"+(topHeight+format.borderWidth)+"px 0px";
}
}
top.appendChild(fillBar);
}
} else {
if(bottom){
if(options.radii.bl && options.radii.br){
fillBar.style.height=(bottomHeight-format.borderWidth) + "px";
fillBar.style.marginLeft=(options.radii.bl-format.borderWidth)+"px";
fillBar.style.marginRight=(options.radii.br-format.borderWidth)+"px";
fillBar.style.borderBottom=format.borderString;
if(format.bgImage!=""){
fillBar.style.backgroundPosition="-"+(bottomHeight+format.borderWidth)+"px -"
+ (format.height + (topHeight+format.borderWidth))+"px";
}
}
bottom.appendChild(fillBar);
}
}
}
// finally, set up the padding.
if(options.autoPad && format.padding>0){
var content=document.createElement("div");
content.style.position="relative";
content.innerHTML=format.content;
content.className="autoPadDiv";
if(topHeight < format.padding){
content.style.paddingTop = Math.abs(topHeight-format.padding)+"px";
}
if(bottomHeight < format.padding){
content.style.paddingBottom = Math.abs(bottomHeight-format.padding)+"px";
}
content.style.paddingLeft=format.padding+"px";
content.style.paddingRight=format.padding+"px";
node.appendChild(content);
}
};
var count=0;
// helper methods.
dojo.lfx.rounded._fns={
blend:function(clr1, clr2, frac){
var c1={
r:parseInt(clr1.substr(1,2),16),
g:parseInt(clr1.substr(3,2),16),
b:parseInt(clr1.substr(5,2),16)
};
var c2={
r:parseInt(clr2.substr(1,2),16),
g:parseInt(clr2.substr(3,2),16),
b:parseInt(clr2.substr(5,2),16)
};
if(frac>1||frac<0){ frac=1; }
var ret=[
Math.min(Math.max(Math.round((c1.r*frac)+(c2.r*(1-frac))),0),255),
Math.min(Math.max(Math.round((c1.g*frac)+(c2.g*(1-frac))),0),255),
Math.min(Math.max(Math.round((c1.b*frac)+(c2.b*(1-frac))),0),255)
];
for(var i=0; i<ret.length; i++){
var n=ret[i].toString(16);
if(n.length<2){ n="0"+n; }
ret[i]=n;
}
return "#"+ret.join("");
},
fraction:function(x, y, r){
var frac=0;
var xval=[];
var yval=[];
var point=0;
var whatsides="";
var intersect=Math.sqrt((Math.pow(r,2)-Math.pow(x,2)));
if(intersect >=y && intersect < (y+1)){
whatsides="Left";
xval[point]=0;
yval[point++]=intersect-y;
}
intersect=Math.sqrt((Math.pow(r,2)-Math.pow(y+1,2)));
if(intersect >=x && intersect < (x+1)){
whatsides += "Top";
xval[point]=intersect-x;
yval[point++]=1;
}
intersect=Math.sqrt((Math.pow(r,2)-Math.pow(x+1,2)));
if(intersect >= y && intersect < (y+1)){
whatsides += "Right";
xval[point]=1;
yval[point++] = intersect-y;
}
intersect=Math.sqrt((Math.pow(r,2)-Math.pow(y,2)));
if(intersect >=x && intersect < (x+1)){
whatsides += "Bottom";
xval[point]=intersect-x;
yval[point]=1;
}
switch(whatsides){
case "LeftRight":
return Math.min(yval[0],yval[1]) + ((Math.max(yval[0],yval[1])-Math.min(yval[0],yval[1]))/2);
case "TopRight":
return 1-(((1-xval[0])*(1-yval[1]))/2);
case "TopBottom":
return Math.min(xval[0],xval[1]) + ((Math.max(xval[0],xval[1])-Math.min(xval[0],xval[1]))/2);
case "LeftBottom":
return (yval[0]*xval[1])/2;
default: return 1;
}
},
draw:function(x, y, color, opac, height, corner, image, radius, top, format){
var px=document.createElement("div");
px.style.height=height+"px"
px.style.width="1px";
px.style.position="absolute";
px.style.fontSize="1px";
px.style.overflow="hidden";
if(image==-1 && format.bgImage!=""){
px.style.backgroundImage=format.bgImage;
px.style.backgroundPosition="-"+(format.width-(radius-x)+format.borderWidth)
+"px -"+((format.height+top+y)-format.borderWidth)+"px";
} else {
px.style.backgroundColor=color;
}
if(opac!=100){ dojo.html.setOpacity(px, (opac/100)); }
px.style.top=y+"px";
px.style.left=x+"px";
corner.appendChild(px);
},
getRGB:function(clr){
var ret="#ffffff";
if(clr!="" && clr!="transparent"){
if(clr.substr(0,3)=="rgb"){
var t=clr.substring(4, clr.indexOf(")"));
t=t.split(",");
for(var i=0; i<t.length; i++){
var n=parseInt(t[i]).toString(16);
if(n.length<2){ n = "0"+n; }
t[i]=n;
}
ret = "#"+t.join("");
}
else if(clr.length==4){
ret = "#"+clr.substring(1,2)+clr.substring(1,2)
+ clr.substring(2,3)+clr.substring(2,3)
+ clr.substring(3,4)+clr.substring(3,4);
}
else {
ret = clr;
}
}
return ret;
}
};