← servoy magazine

Hacking Servoy's Calendar Picker

Servoy's web client calendar picker is an easy target to make fun of—the last time it looked even somewhat passable was probably 15 years ago. A picture is worth a 1,000 words:

[image unavailable: Servoy_calendar_picker]

Additionally, Servoy doesn't have any hooks or abstractions to modify the look and feel of this component. And all the markup and css is generated at runtime so not so easy as just going in and adjusting a bit of CSS.

The rest of this post explains how to arrive at this:

[image unavailable: Calendar_picker_after]

The good news is that Servoy does use a css file (and graphics) to style the calendar widget. The bad news is that since it is generated at runtime you need to do some URL rewriting to divert Servoy's path for the style sheet and graphics to a location of your own choosing.

We use UrlRewriteFilter for this job as it simplifies the task of creating rewrite rules so you don't need a PhD. Here's the two rules to point to our new style sheet and graphics directory:

<rule>
<name>Servoy calendar css</name>
<note>Better calendar picker graphics</note>
<from>^/servoy-webclient/resources/datepicker/dhtmlgoodies_calendar.css$</from>
<to last="true">/servoy-webclient/templates/datasutra/override/calendar/calendar.css</to>
</rule>

<rule>
<name>Servoy calendar graphics</name>
<note>Better calendar picker graphics</note>
<from>^/servoy-webclient/resources/datepicker/images/(.*)$</from>
<to last="true">/servoy-webclient/templates/datasutra/override/calendar/images/$1</to>
</rule>

[image unavailable: File location]

Note that you can place these files anywhere in Servoy's ROOT directory and adjust the rules accordingly.

Now all you need to do is fiddle with the css and replace out some images. By "fiddling" I mean it really helps to know some advanced css tricks. In the following css note how we're using gradient stops, positional selectors, and even inserting content:

#calendarDiv {
position: absolute;
width: 250px;
border: 1px solid gray;
padding: 10px;
font-family: arial,helvetica,clean,sans-serif;
font-size: 10px;
padding-bottom: 20px;
visibility: hidden;
background: #F2F2F2;

background: -moz-linear-gradient(top, #ffffff 0%, #f9f9f9 41%, #e8eaea 63%, #d8d8da 79%, #d8d8da 97%, #8c8c8c 100%) 0 0 no-repeat, #F2F2F2;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(41%,#f9f9f9), color-stop(63%,#e8eaea), color-stop(79%,#d8d8da), color-stop(97%,#d8d8da), color-stop(100%,#8c8c8c)) 0 0 no-repeat, #F2F2F2;
background: -webkit-linear-gradient(top, #ffffff 0%,#f9f9f9 41%,#e8eaea 63%,#d8d8da 79%,#d8d8da 97%,#8c8c8c 100%) 0 0 no-repeat, #F2F2F2;
background: -o-linear-gradient(top, #ffffff 0%,#f9f9f9 41%,#e8eaea 63%,#d8d8da 79%,#d8d8da 97%,#8c8c8c 100%) 0 0 no-repeat, #F2F2F2;
background: -ms-linear-gradient(top, #ffffff 0%,#f9f9f9 41%,#e8eaea 63%,#d8d8da 79%,#d8d8da 97%,#8c8c8c 100%) 0 0 no-repeat, #F2F2F2;
background: linear-gradient(to bottom, #ffffff 0%,#f9f9f9 41%,#e8eaea 63%,#d8d8da 79%,#d8d8da 97%,#8c8c8c 100%) 0 0 no-repeat, #F2F2F2;
filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#8c8c8c',GradientType=0 );

-moz-background-size: 100% 24px;
-webkit-background-size: 100% 24px;
-o-background-size: 100% 24px;
-ms-background-size: 100% 24px;
background-size: 100% 24px;
}

#calendarDiv span, #calendarDiv img {
float: left;
padding-top: 1px;
}

#calendarDiv .calendar_week_column {
display: none;
}

#calendarDiv .selectBox img,
#calendarDiv .selectBoxOver img {
display: none;
}

#calendarDiv .selectBox,
#calendarDiv .selectBoxOver {
padding: 1px;
cursor: pointer;
padding-left: 2px;
font-size: 14px;

font-weight: bold;
position: relative;
padding-left: 6px;
padding-right: 6px;
border-radius: 2px;
}

#calendarDiv .selectBox {
background-color: #FBFBFB;
border: 1px solid #CCC;
}

#calendarDiv .selectBoxOver {
background-color: #426FD9;
border: 1px solid #FFF;
color: #FFF;
}

#calendarDiv .selectBoxTime,
#calendarDiv .selectBoxTimeOver {
padding: 1px;
cursor: pointer;
padding-left: 2px;
position: relative;
border-radius: 2px;
height: 1.3em;
}

#calendarDiv .selectBoxTime {
background-color: #FBFBFB;
border: 1px solid #CCC;
color: #262626;
}

#calendarDiv .selectBoxTimeOver {
background-color: #426FD9;
border: 1px solid #FFF;
color: #FFF;
}

#calendarDiv .topBar {
height: 1.6em;
padding: 2px;
margin-top: 18px;
margin-bottom: 8px;
text-align: center;
}

#calendarDiv .topBar img {
cursor: pointer;
}

#calendarDiv .topBar div {
display: inline-block;
margin-right: 1px;
}

#calendarDiv .topBar div:first-child img {
position: absolute;
left: 10px;
top: 32px;
}

#calendarDiv .topBar div:nth-of-type(2) img {
position: absolute;
right: 10px;
top: 32px;
}

#calendarDiv .topBar img:nth-of-type(1) {
position: absolute;
right: 10px;
top: 3px;
}

#calendarDiv .activeDay { /* Active day in the calendar */
color: #FFFFFF;
background-color: #426FD9;
}

#calendarDiv .todaysDate {
text-align: left;
position: absolute;
bottom: 4px;
right: 10px;
left: 10px;
}

#calendarDiv .todaysDate div {
float: left;
}

#calendarDiv #todaysDateString:before{
content: "Today (";
}
#calendarDiv #todaysDateString:after{
content: ")";
}

#calendarDiv .timeBar {
position: absolute;
right: -4px;
bottom: -2px;
}

#calendarDiv .timeBar div {
float: left;
margin-left: 1px;
cursor: pointer;
}

#calendarDiv #monthSelect {
margin-right: 4px;
}

#calendar_hour_txt, #calendar_minute_txt {
padding-left: 3px;
}

#calendarDiv #yearDropDown {
margin-top: -2px;
margin-left: 35px;
}

#calendarDiv #monthDropDown {
margin-top: -2px;
margin-left: 45px;
}

#calendarDiv #hourDropDown,
#calendarDiv #minuteDropDown {
margin-top: 35px;
margin-left: 10px;
}

#calendarDiv .monthYearPicker {
background-color: #FBFBFB;
border: 1px solid #CCC;
border-radius: 6px;
font-size: 12px;
padding: 3px;
position: absolute;
left: 0px;
top: 15px;
z-index: 1000;
display: none;
}

#calendarDiv .monthYearPicker div {
float: none;
clear: both;
padding: 1px;
margin: 1px;
cursor: pointer;
}

#calendarDiv .monthYearActive {
background-color: #426FD9;
color: #FFFFFF;
}

#calendarDiv table {
table-layout: fixed;
border-collapse: collapse;
background-color: #CCC;
}

#calendarDiv td[colspan]{
background-color: #CCC;
cursor: default;
}

#calendarDiv td {
border: 1px solid #CCC;
text-align: center;
cursor: pointer;
padding: 1px;
height: 1.6em;
line-height: 1.6em;
white-space: nowrap;
margin: 0px;
font-size: 12px;
color: #06C;
background-color: white;
}

#calendarDiv tr.calendar_week_row {
padding: 0;
border: 0;
}

#calendarDiv tr.calendar_week_row td {
border: 0;
height: 2em;
color: black;
background-color: #F2F2F2;
cursor: default;
}

Here is the link to download a zip containing the css and images. Enjoy!

Download Calendar

Comments (1)

Troy Elliott
The images and basic styling idea for this reskin came from the YUI calendar control. http://developer.yahoo.com/yui/examples/calendar/quickstart.html