Search

Rss Posts

Rss Comments

Login

 

dateSlider jQuery plugin

Feb 18

This is actually the third version, as the first two didn’t quite hit the mark on the requirements… This jQuery dateslider is based on the slick and cool Ajaxorized version, originally written for Prototype.js. What I didn’t like was that it’s zoom in/out feature didn’t really scale very well (what unit I am looking at? and why don’t these lines line up?), and made it hard to pick a date and time.

I used jQueryUI because it handily had all the things I need: draggable, resizable things. The hardest part was, of course, the actual date-times (as anyone who has to work with Javascript dates knows). I used the super handy date.js like the original as well.

To use it, include jquery, jqueryui, date.js, and the plugin:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
<script src="date.js"></script>
<script src="jquery.dateRanger.js" type="text/javascript"></script>

Then, initalize the plugin:


<script type="text/javascript">
$(function(){
$("#my-date-slider").dateRanger({
"dateFormat": "MMM dd yyyy HH:mm:ss",
"timeStart": Date.today().set({
day: 1,
month: 0,
year: 2011
}),"timeEnd": Date.today().set({
day: 30,
month: 11,
year: 2012
}),
"startDate": Date.parse("1/17/2011"),
"endDate": Date.parse("4/2/2011"),
"zoomLevel": "month",
"unitwidth": 20
})
});
</script>
The options:

  • dateFormat : a string or date.js format specified (check it!) (i.e. “MMM dd”, “u”, “yyyy-MM-d ht”)
  • timeStart: the first available date; You can pass in anything recognized by date.js (i.e. “today”, “yesterday”, “+2weeks”, “-18years”, etc.)
  • timeEnd: the last available date
  • startDate: beginning of the selected range
  • endDate: the end of the selected range
  • zoomLevel: what time unit you see on load: accepts “year”,”month”,”day”, or “hour”
  • unitwidth: how wide is a unit? If you change this, be sure to change the css for .day to match

Download plugin: jquery.dateRanger.js:
(function($){
$.fn.dateRanger = function(options){
var settings = {
"startDate": Date.parse('-7days'),
"endDate": Date.parse('-1days'),
"dateFormat": 'MMM dd yyyy HH:mm:ss',
"timeStart": Date.today().set({
day: 1,
month: 0,
year: 2011
}).clearTime(),
"timeEnd": Date.today().set({
day: 31,
month: 11,
year: 2012
}).clearTime(),
"zoomLevel": "day",
"unitwidth": 20
};
var config = $.extend(settings, options);
var zoom = config.zoomLevel, rangeStart = config.startDate, rangeStop = config.endDate, timeStart = config.timeStart, timeEnd = config.timeEnd, f = config.dateFormat, w = config.unitwidth, one = new Array();
one["second"] = 1000;
one["minute"] = 1000 * 60;
one["hour"] = 1000 * 60 * 60;
one["day"] = 1000 * 60 * 60 * 24;
one["month"] = 1000 * 60 * 60 * 24 * 30;
one["year"] = 1000 * 60 * 60 * 24 * 365;
function mapDates(ui){
var range = ui.width() / w;
var s = ui.position().left / w;
var e = s + range - 1;
var startDate = $j(".day").eq(s).attr("data-date");
var endDate = $j(".day").eq(e).attr("data-date");
$j("#datestart").val(startDate.toString(f))
$j("#dateend").val(inclusiveRange(endDate, zoom).toString(f))
centerSelection();
}
function centerSelection(){
$j("#dragger").css('left', -1 * ($j("#resizer").position().left - .5 * ($j("#mask").width() - $j("#resizer").width())))
}
function difference(a, b, z){
A = a.getTime() / one[z];
B = b.getTime() / one[z];
return Math.floor(B - A);
}
function unitsbetween(a, b, z){
A = a.getTime() / one[z];
B = b.getTime() / one[z];
return (B - A);
}
function inclusiveRange(d, zoom){
var $fullrange = (Date.parse(d)) ? Date.parse(d) : Date.parse(d.substring(0, d.length - 1));
var compare = $fullrange.clone();
switch (zoom) {
case "year":
return compare.set({
month: 0,
day: 1
}).clearTime().add(1).years().add(-1).seconds();
break;
case "month":
return compare.moveToLastDayOfMonth().add(1).days().add(-1).seconds();
break;
case "day":
return compare.add(1).days().add(-1).seconds();
break;
case "hour":
return compare.add(1).hours().add(-1).seconds();
break;
case "minute":
return compare.add(1).minute().add(-1).seconds();
break;
case "second":
return compare.add(1).seconds().add(-1).milliseconds();
break;
default:
return $fullrange;
break;
}
}
function buildDates(zoom){
$j("#dragger").css('left', '0')
$j("#dragger .day").remove();
$j("#resizer").css({
left: 0,
width: w + "px"
})
var visibleArea, format1, format2, tempDate;
switch (zoom) {
case "hour":
var tempDateHS = Date.parse($j("#datestart").val()).clone().add(-1).days();
var tempDateHE = Date.parse($j("#dateend").val()).clone().clearTime().add(2).days();
visibleArea = difference(tempDateHS, tempDateHE, zoom);
format1 = "MMM dd yy ht";
format2 = "ht";
tempDate = tempDateHS.clone()
break;
case "day":
visibleArea = difference(config.timeStart, timeEnd, zoom);
format1 = "MMM dd yy";
format2 = " dd";
tempDate = timeStart.clone()
break;
case "month":
visibleArea = difference(config.timeStart, timeEnd, zoom);
format1 = "MMM 'yy";
format2 = "MMM 'yy";
tempDate = timeStart.clone()
break;
case "year":
visibleArea = Math.max(timeEnd.getFullYear() - config.timeStart.getFullYear(), 1);
format1 = "yyyy";
tempDate = timeStart.clone()
break;
}
$j("#dragger").css('width', visibleArea * w)
for (i = 0; i < visibleArea; i++) {
if ((zoom == "day" && tempDate.getDate() == 1) || (zoom == "hour" && tempDate.getHours() == 0)) {
format = format1;
cl = " fdotm"
}
else
if (zoom == "month" && tempDate.getMonth() == 0) {
format = format1;
cl = " fmoty"
}
else
if (zoom == "year") {
format = format1;
cl = " yrot"
}
else {
format = format2;
cl = "";
}
$j("#dragger").append('<div data-date="' + tempDate.toString(f) + '">' + tempDate.toString(format) + '</div>')
tempDate.add(1)[zoom]()
}
}
function setScroller(s, r){
if (r > 1)
r = Math.ceil(r);
else
r = Math.round(r);
$j("#resizer").css({
left: $j(".day[data-date='" + s + "']").position().left,
width: r * w + "px"
})
}
function u2Date(u){
if (u.indexOf("Z") != -1)
return Date.parse(u.substring(0, u.length - 1));
else
return Date.parse(u);
}
$j("#datestart").live("change", function(e){
if (Date.parse($j(this).val())) {
var inpS = Date.parse($j(this).val());
var inpE = u2Date($j("#dateend").val())
if (inpS.compareTo(inpE) == 1) {
inpE = inpS;
}
$j("#datestart").val(inpS.toString(f));
setScroller($j("#datestart").val(), Math.max(difference(inpS, inpE, zoom), 1))
mapDates($j("#resizer"));
}
else {
$j(this).addClass("error").effect("pulsate", {
times: 2
}, 500, function(){
$j(this).removeClass("error").focus()
});
}
});
$j("#dateend").live("change", function(e){
if (Date.parse($j(this).val())) {
var inpE = Date.parse($j(this).val());
var inpS = u2Date($j("#datestart").val())
if (inpS.compareTo(inpE) == 1) {
inpE = inpS;
}
$j("#dateend").val(inpE.toString(f));
setScroller($j("#datestart").val(), Math.max(difference(inpS, inpE, zoom), 1))
mapDates($j("#resizer"));
}
else {
$j(this).addClass("error").effect("pulsate", {
times: 2
}, 500, function(){
$j(this).removeClass("error").focus()
});
}
});
$j("div.zoomMenu div").live("click", function(){
$t = $j(this);
zoom = $t.html();
$j("div.zoomMenu div").removeClass("selectedZoom")
$t.addClass("selectedZoom");
buildDates(zoom)
inpS = u2Date($j("#datestart").val());
inpE = u2Date($j("#dateend").val());
switch (zoom) {
case "hour":
inpS.set({
"hour": 0
});
inpE = inclusiveRange(inpE, zoom);
break;
case "day":
inpS.set({
"hour": 0
});
inpE = inclusiveRange(inpE, zoom);
break;
case "month":
inpS.moveToFirstDayOfMonth();
inpE.moveToLastDayOfMonth();
break;
case "year":
inpS.set({
month: 0,
day: 1
});
inpE.set({
month: 0,
day: 1
}).add(1).years().add(-1).seconds();
break;
}
$j("#datestart").val(inpS.toString(f));
$j("#dateend").val(inpE.toString(f));
setScroller($j("#datestart").val(), difference(inpS, inpE, zoom))
mapDates($j("#resizer"));
});
return this.each(function(){
var zooms = {
"year": "yyyy",
"month": "MMM",
"day": "dd",
"hour": "HH"
};
var structure = '<div id="range">';
structure += '<div id="dragDate">';
structure += '<label for="datestart">Start</label><input id="datestart" name="datestart" type="text" /> ';
structure += '<label for="dateend">Stop</label><input id="dateend" type="text" name="dateend"/>';
structure += '</div>';
structure += '<div id="mask"><div id="dragger"><div id="resizer"></div></div>';
structure += '</div>';
structure += '<div>';
var cla = '';
for (b in zooms) {
cla = (b == zoom) ? '' : '';
structure += '<div' + cla + '>' + b + '</div>';
}
structure += '</div></div>';
$j(this).append(structure);
buildDates(zoom);
$j("#dragger").draggable({
grid: [w, 0],
axis: 'x',
addClasses: false
});
$j("#resizer").resizable({
handles: 'e, w',
grid: [w, 0],
containment: '#dragger',
stop: function(event, ui){
if (ui.position.left < 0) {
$j("#resizer").css('left', '0px')
}
mapDates($j(this))
},
addClasses: false
}).draggable({
grid: [w, 0],
axis: 'x',
containment: '#dragger',
stop: function(event, ui){
mapDates($j(this))
},
addClasses: false
}).css({
left: $j(".day[data-date='" + rangeStart.toString(f) + "']").position().left,
width: difference(rangeStart, rangeStop, zoom) * w + "px"
});
mapDates($j("#resizer"));
centerSelection();
});
};
})(jQuery);

And some useful CSS to finish up with:

#range {
position: relative;
}
.day {
color: #8997a5;
padding: 10px 0 0;
font-size: 9px;
width: 19px;
float: left;
height: 25px;
border-left: 1px solid #eee;
text-align: center;
}
.fdotm {
padding: 1px 0 0 0;
border-left: 1px solid #bbb;
height: 35px;
}
.fmoty {
padding: 10px 0 0 0;
border-left: 1px solid #888;
height: 25px;
}
.yrot {
font-size: 8px
}
#mask {
width: 100%;
overflow: hidden;
height: 80px;
position: absolute;
left: 0;
top: 50px;
}
#dragger{
width: 10000em;
position: absolute;
left: 0;
top: 10px;
z-index: 2;
height: 75px;
cursor: move;
padding: 10px 0 0 0;
background: none;
border: none;
color: #000;
}
#dragger #resizer{
background: rgba(0, 137, 206, 0.2);
width: 200px;
position: absolute;
top: 10px;
z-index: 6;
height: 40px;
cursor: move;
padding: 0;
margin: 0;
}
.error {
background: #ff0013;
}
.zoomMenu {
padding: 10px 0 0 0;
text-align: center;
}
.zoomMenu div {
display: inline;
color: #0089ce;
text-align: center;
cursor: pointer;
margin-right: 5px;
padding: 2px 6px;
position: relative;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.zoomMenu div:hover {
background: #e5f3fb;
color: #333;
}
.zoomMenu div.selectedZoom {
background: #0089ce;
color: #fff;
}

Post a comment

You must be logged in to post a comment.