Commit 69a6ff19 authored by Rob Tomsick's avatar Rob Tomsick
Browse files

Initial commit

parents
rjcc.js
Copyright (c) 2014, Robert Tomsick <robert@tomsick.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
.rjcc-wrapper
{
float : left;
border : 1pt black solid;
font-family : sans-serif;
}
.rjcc-wrapper table
{
border-collapse : collapse;
border-spacing : 0;
}
.rjcc-arrow-left, .rjcc-arrow-right
{
font-weight : bold;
border : 1pt #569 solid;
border-top : 1pt transparent solid;
border-left : 1pt #abf solid;
height : 10pt;
line-height : 10pt;
background : #78b;
color : #000;
text-decoration : none;
margin : 1pt 0;
padding : 0 3pt;
display : block;
}
.rjcc-arrow-left:hover, .rjcc-arrow-right:hover
{
background : #abf;
}
.rjcc-arrow-left
{
float : left;
}
.rjcc-arrow-right
{
float : right;
}
.rjcc-year, .rjcc-month
{
margin : 0;
padding : 0;
height : 14pt;
text-align : center;
}
.rjcc-year
{
background : #89c;
}
.rjcc-month
{
background : #abe;
}
.rjcc-day
{
font-size : 9pt;
padding : 3pt;
}
.rjcc-week:nth-child(even)
{
background:#eef;
}
/*
*
* rjcc.js
* revision 01
*
* Copyright (c) 2014 Robert Tomsick <robert@tomsick.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
(function(_init) {
if (typeof define === "function" && define.amd)
{
define(_init);
}
else if (window !== undefined)
{
window.RJCC = _init();
}
})(function(root) {
/**
* @constructor
* @param {Object} options
*/
var RJCC = function(options)
{
/**
*
* @param {Object} overrides
* @param {Object} defaults
*/
var _applyDefaults = function(overrides, defaults)
{
var ret = {};
var given = (overrides === undefined ? {} : overrides);
for (var k in defaults)
{
if (! given.hasOwnProperty(k))
{
ret[k] = defaults[k];
}
else
{
ret[k] = given[k];
}
}
return ret;
};
var opts = _applyDefaults(options, RJCC.DEFAULTS);
var _yearHeader = undefined,
_yearElem = undefined,
_monthHeader = undefined,
_dateContainer = undefined,
_dispYear = undefined,
_dispMonth = undefined;
if (opts.selectionCallback === undefined)
{
opts.selectionCallback = (function(){});
}
/* TODO: do type validation here */
/* TODO: min/max->start and min->max range checks */
var _pickDay = function(day)
{
var result =
{
"year" : _dispYear,
"month" : _dispMonth + 1,
"day" : day
};
opts.selectionCallback.call(function(){}, result);
};
var _pickYear = function(year)
{
var result =
{
"year" : year
};
opts.selectionCallback.call(function(){}, result);
};
var _pickMonth = function(month)
{
var result =
{
"year" : _dispYear,
"month" : month
};
opts.selectionCallback.call(function(){}, result);
};
/**
* @param {Number} contents
* @param {Boolean} canSelect
* @param {String} cls
* @returns {Element}
*/
var _mkDayCell = function(contents, canSelect)
{
var elem = document.createElement("td");
if (opts.dayCellClass !== undefined)
{
elem.setAttribute("class", opts.dayCellClass);
}
if (contents === undefined)
{
elem.appendChild(document.createTextNode(""));
return elem;
}
if (canSelect === false)
{
elem.appendChild(document.createTextNode(contents));
return elem;
}
var l = document.createElement("a");
l.setAttribute("href", "javascript:void(0);");
l.appendChild(document.createTextNode(contents));
l.onclick = (function(){ _pickDay(contents); });
elem.appendChild(l);
return elem;
};
/**
* @param {Number} startDay
* @param {Number} paddingCells
* @param {String} cls
* @returns {Element}
*/
var _mkWeekRow = function(startDay, paddingCells)
{
var elem = document.createElement("tr");
var emptyCount = paddingCells;
var numDays = 7 - Math.abs(paddingCells);
if (opts.weekRowClass !== undefined)
{
elem.setAttribute("class", opts.weekRowClass);
}
/* leading empty cells */
if (emptyCount < 0)
{
for (; emptyCount < 0; emptyCount++)
{
elem.appendChild(_mkDayCell());
}
}
for (var i = 0; i < numDays; i++)
{
elem.appendChild(_mkDayCell(new String(startDay + i)));
}
/* trailing empty count */
if (emptyCount > 0)
{
for(; emptyCount > 0; emptyCount--)
{
elem.appendChild(_mkDayCell());
}
}
return elem;
};
var _mkMonthRows = function(startOffset, totalDays)
{
if (totalDays < 28)
{
throw new Error("Months must have at least 28 days");
}
if (startOffset < 0)
{
throw new Error("Start offset cannot be negative");
}
var count = totalDays;
var row = 7 - startOffset;
var elem = document.createElement("table");
elem.appendChild(_mkWeekRow(1, -startOffset));
count -= (7 - startOffset);
while (count > 0)
{
row = (count >= 7) ? 7 : count;
elem.appendChild(_mkWeekRow(totalDays - count + 1, (7-row)));
/* decrement by whatever we just rendered */
count -= row;
}
return elem;
};
var _changeYear = function(change)
{
_dispYear += change;
if (! _ymAllowed(_dispYear, _dispMonth))
{
var bound = opts.maxDate;
if (change <= 0)
{
bound = opts.minDate;
}
if (bound === undefined)
{
throw new Error("bug");
}
_dispYear = bound.getFullYear();
_dispMonth = bound.getMonth();
}
_renderAll();
};
var _changeMonth = function(change)
{
if (_dispMonth + change < 0)
{
_dispMonth = 11;
_changeYear(-1);
}
else if (_dispMonth + change > 11)
{
_dispMonth = 0;
_changeYear(1);
}
else
{
_dispMonth += change;
}
if (! _ymAllowed(_dispYear, _dispMonth))
{
var bound = opts.maxDate;
if (change <= 0)
{
bound = opts.minDate;
}
if (bound === undefined)
{
throw new Error("bug");
}
_dispYear = bound.getFullYear();
_dispMonth = bound.getMonth();
}
_renderAll();
};
var _renderYear = function()
{
_yearHeader.innerHTML = "";
if (opts.yearHeaderClass !== undefined)
{
_yearHeader.setAttribute("class", opts.yearHeaderClass);
}
var yearElem = document.createElement("span");
var e;
if (opts.allowOnlyYear)
{
e = document.createElement("a");
e.setAttribute("href", "javascript:void(0);");
e.appendChild(document.createTextNode(_dispYear));
e.onclick = function(){_pickYear(_dispYear);};
}
else
{
e = document.createTextNode(_dispYear);
}
yearElem.appendChild(e);
if (opts.allowYearChoice && _yearAllowed(_dispYear - 1))
{
var fn = (function(){
_changeYear(-1); });
_yearHeader.appendChild(_mkArrow(true, fn));
}
_yearHeader.appendChild(yearElem);
if (opts.allowYearChoice && _yearAllowed(_dispYear + 1))
{
var fn = (function(){
_changeYear(1); });
_yearHeader.appendChild(_mkArrow(false, fn));
}
};
var _mkArrow = function(left, callback)
{
var elem = document.createElement("a");
elem.setAttribute("href", "javascript:void(0);");
if (left)
{
elem.setAttribute("class", opts.leftArrowClass);
elem.appendChild(document.createTextNode("<"));
}
else
{
elem.setAttribute("class", opts.rightArrowClass);
elem.appendChild(document.createTextNode(">"));
}
elem.onclick = callback;
return elem;
};
var _renderMonth = function()
{
var clsMonth = opts.monthHeaderClass;
_monthHeader.innerHTML = "";
if (clsMonth !== undefined)
{
_monthHeader.setAttribute("class", clsMonth);
}
var monthStr = opts.monthNames[_dispMonth];
var monthElem = document.createElement("span");
var e;
if (opts.allowMonthYear)
{
e = document.createElement("a");
e.setAttribute("href", "javascript:void(0);");
e.appendChild(document.createTextNode(monthStr));
e.onclick = function(){_pickMonth(_dispMonth + 1);};
}
else
{
e = document.createTextNode(monthStr);
}
monthElem.appendChild(e);
/* render left arrow iff month choice is allowed and doing so
* wouldn't allow selection of OOB value
*/
if (opts.allowMonthChoice && _ymAllowed(_dispYear, _dispMonth - 1))
{
var fn = (function(){
_changeMonth(-1); });
_monthHeader.appendChild(_mkArrow(true, fn));
}
/* month name */
_monthHeader.appendChild(monthElem);
/* render right arrow iff month choice is allowed and doing so
* wouldn't allow selection of OOB value
*/
if (opts.allowMonthChoice && _ymAllowed(_dispYear, _dispMonth + 1))
{
var fn = (function(){
_changeMonth(1); });
_monthHeader.appendChild(_mkArrow(false, fn));
}
};
var _yearAllowed = function(year)
{
var y = year;
if (opts.minDate !== undefined)
{
if (y < opts.minDate.getFullYear())
{
return false;
}
}
if (opts.maxDate !== undefined)
{
if (y < opts.maxDate.getFullYear())
{
return true;
}
if (y > opts.maxDate.getFullYear())
{
return false;
}
}
return true;
};
var _ymAllowed = function(year, month)
{
var y = year, m = month;
if (m < 0)
{
y--;
m = 11;
}
if (opts.minDate !== undefined)
{
if (y < opts.minDate.getFullYear())
{
return false;
}
if (y === opts.minDate.getFullYear() &&
m < opts.minDate.getMonth())
{
return false;
}
}
if (opts.maxDate !== undefined)
{
if (y < opts.maxDate.getFullYear())
{
return true;
}
if (y === opts.maxDate.getFullYear())
{
return m <= opts.maxDate.getMonth();
}
if (y > opts.maxDate.getFullYear())
{
return false;
}
}
return true;
};
var _renderDayTable = function()
{
var monthOffset = (new Date(_dispYear, _dispMonth, 1)).getDay();
var dateTable = _mkMonthRows(monthOffset,
_calcDaysInMonth(_dispYear, _dispMonth));
_dateContainer.innerHTML = "";
_dateContainer.appendChild(dateTable);
};
var _renderAll = function()
{
_renderYear();
_renderMonth();
_renderDayTable();
};
var _mkHeader = function(year, month)
{
var header = document.createElement("div");
_yearHeader = document.createElement("h5");
_monthHeader = document.createElement("h5");
header.appendChild(_yearHeader);
header.appendChild(_monthHeader);
return header;
};
/**
* @param {Element} header
* @param {Element} dayTable
* @param {String} cls
*
* @returns {Element}
*/
var _mkWrapper = function(header, dayTable, cls)
{
var elem = document.createElement("div");
if (cls !== undefined)
{
elem.setAttribute("class", cls);
}
elem.appendChild(header);
elem.appendChild(dayTable);
return elem;
};
var _calcStartDay = function(year, month, day)
{
var today = new Date();