jui.defineUI("ui.datepicker", [ "jquery", "util.base" ], function($, _) {
function getStartDate(date) {
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
return date;
}
/**
* @class ui.datepicker
* @extends core
* @alias Date Picker
* @requires jquery
* @requires util.base
*/
var UI = function() {
var year = null, month = null, date = null,
selDate = null, items = {}; // 헌재 페이지의 요소 엘리먼트 캐싱
var $head = null, $body = null;
var minDate = null, maxDate = null;
function setCalendarEvent(self) {
self.addEvent($head.children(".prev"), "click", function(e) {
self.prev(e);
});
self.addEvent($head.children(".next"), "click", function(e) {
self.next(e);
});
self.addEvent($head.children(".prev-year"), "click", function(e) {
self.prev(e, true);
});
self.addEvent($head.children(".next-year"), "click", function(e) {
self.next(e, true);
});
}
function setCalendarDate(self, no) {
var opts = self.options;
if(opts.type == "daily") {
var m = (month < 10) ? "0" + month : month,
d = (no < 10) ? "0" + no : no;
selDate = new Date(year + "/" + m + "/" + d);
} else if(opts.type == "monthly") {
var m = (no < 10) ? "0" + no : no;
selDate = new Date(year + "/" + m + "/01");
} else if(opts.type == "yearly") {
selDate = new Date(no + "/01/01");
}
// 0시 0분 0초 0밀리 초로 설정
selDate = getStartDate(selDate);
}
function getCalendarDate(self) {
var opts = self.options,
tmpDate = null;
if(opts.type == "daily") {
var m = (month < 10) ? "0" + month : month;
tmpDate = new Date(year + "/" + m + "/01");
} else if(opts.type == "monthly") {
tmpDate = new Date(year + "/01/01");
} else if(opts.type == "yearly") {
tmpDate = new Date();
}
return getStartDate(tmpDate);
}
function getCalendarHtml(self, obj) {
var opts = self.options,
resHtml = [],
tmpItems = [];
// 활성화 날짜 캐시 초기화
items = {};
if(self.tpl["date"]) {
for(var i = 0; i < obj.objs.length; i++) {
tmpItems.push(self.tpl["date"]({
type: obj.objs[i].type,
date: obj.objs[i].no,
day: tmpItems.length
}));
if(isNextBr(i)) {
resHtml.push("<tr>" + tmpItems.join("") + "</tr>");
tmpItems = [];
}
}
} else {
for(var i = 0; i < obj.objs.length; i++) {
tmpItems.push(obj.nums[i]);
if(isNextBr(i)) {
resHtml.push(self.tpl["dates"]({ dates: tmpItems }));
tmpItems = [];
}
}
}
var $list = $(resHtml.join(""));
$list.find("td").each(function(i) {
$(this).addClass(obj.objs[i].type);
self.addEvent(this, "click", function(e) {
if(obj.objs[i].type == "none") return;
$body.find("td").removeClass("active");
$(this).addClass("active");
setCalendarDate(self, obj.objs[i].no);
self.emit("select", [ self.getFormat(), e ]);
});
if(obj.objs[i].type != "none") {
items[obj.objs[i].no] = this;
}
});
function isNextBr(i) {
return (opts.type == "daily") ? ((i + 1) % 7 == 0) : ((i + 1) % 3 == 0);
}
return $list;
}
function getLastDate(year, month) {
if(month == 2) {
if(year % 100 != 0 && (year % 4 == 0 || year % 400 == 0))
return 29;
else
return 28;
} else {
var months = [ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
return months[month - 1];
}
}
function getDateList(self, y, m) {
var objs = [],
nums = [],
no = 1;
var d = new Date(),
start = new Date(y + "-" + ((m < 10) ? "0" + m : m)).getDay(),
ldate = getLastDate(y, m), sdate = 0;
var prevYear = (m == 1) ? y - 1 : y,
prevMonth = (m == 1) ? 12 : m - 1,
prevLastDay = getLastDate(prevYear, prevMonth);
// 최소 날짜로 시작일 설정
if(minDate && minDate.getFullYear() == y && minDate.getMonth() + 1 == m) {
sdate = minDate.getDate();
}
// 최대 날짜로 종료일 설정
if(maxDate && maxDate.getFullYear() == y && maxDate.getMonth() + 1 == m) {
ldate = maxDate.getDate();
}
for(var i = 0; i < start; i++) {
nums[i] = (prevLastDay - start) + (i + 1);
objs[i] = { type: "none", no: nums[i] };
}
for(var i = start; i < 42; i++) {
if(sdate <= no && no <= ldate) {
var type = "";
if(d.getMonth() + 1 == m && d.getDate() == no) {
type = "now";
}
if(selDate != null) {
if(selDate.getFullYear() == y && selDate.getMonth() + 1 == m && selDate.getDate() == no) {
type = "active";
}
}
nums[i] = no;
objs[i] = { type: type, no: nums[i] };
no++;
} else {
nums[i] = no - ldate;
objs[i] = { type: "none", no: nums[i] };
no++;
}
}
return { objs: objs, nums: nums };
}
function getMonthList(y) {
var objs = [],
nums = [];
var d = new Date();
for(var i = 1; i <= 12; i++) {
var type = "";
if(d.getFullYear() == y && d.getMonth() + 1 == i) {
type = "now";
}
if(selDate != null) {
if(selDate.getFullYear() == y && selDate.getMonth() + 1 == i) {
type = "active";
}
}
nums.push(i);
objs.push({ type: type, no: i });
}
return { objs: objs, nums: nums };
}
function getYearList(y) {
var objs = [],
nums = [],
startYear = y - 4;
var d = new Date();
for(var i = startYear; i < startYear + 12; i++) {
var type = "";
if(d.getFullYear() == i) {
type = "now";
}
if(selDate != null) {
if(selDate.getFullYear() == i) {
type = "active";
}
}
nums.push(i);
objs.push({ type: type, no: i });
}
return { objs: objs, nums: nums };
}
function checkDate(y, m, d) {
if(minDate) {
var minY = minDate.getFullYear(), minM = minDate.getMonth() + 1, minD = minDate.getDate();
if (y < minY || (y == minY && m < minM)) return [minY, minM, minD];
}
if(maxDate) {
var maxY = maxDate.getFullYear(),maxM = maxDate.getMonth() + 1, maxD = maxDate.getDate()
if (y > maxY || (y == maxY && m > maxM)) return [maxY, maxM, maxD];
}
return [y, m, d];
}
this.init = function() {
var opts = this.options;
$head = $(this.root).children(".head");
$body = $(this.root).children(".body");
minDate = (_.typeCheck("date", opts.minDate)) ? opts.minDate : null;
maxDate = (_.typeCheck("date", opts.maxDate)) ? opts.maxDate : null;
if(opts.type == "daily") {
// 기본 날짜가 최소 날짜나 최대 날짜보다 작거나 큰 경우
if(opts.date < minDate) {
opts.date = minDate;
} else if(opts.date < minDate) {
opts.date = maxDate;
}
// 최소 날짜와 최대 날짜가 서로 교차하는 경우
if(minDate && maxDate && maxDate < minDate) {
minDate = null;
maxDate = null;
}
}
// 이벤트 정의
setCalendarEvent(this);
// 기본 날짜 설정
this.select(opts.date);
}
/**
* @method page
* Outputs a calendar that fits the year/month entered
*
* @param {Integer} year
* @param {Integer} month
*/
this.page = function(y, m) {
if(arguments.length == 0) return;
var opts = this.options;
if(opts.type == "daily") {
year = y;
month = m;
$body.find("tr:not(:first-child)").remove();
$body.append(getCalendarHtml(this, getDateList(this, year, month)));
} else if(opts.type == "monthly") {
year = y;
$body.find("tr").remove();
$body.append(getCalendarHtml(this, getMonthList(year)));
} else if(opts.type == "yearly") {
year = y;
$body.find("tr").remove();
$body.append(getCalendarHtml(this, getYearList(year)));
}
$head.children(".title").html(_.dateFormat(getCalendarDate(this), opts.titleFormat));
}
/**
* @method prev
* Outputs a calendar that fits the previous year/month
*
*/
this.prev = function(e, moveYear) {
var opts = this.options;
if(opts.type == "daily") {
if (moveYear) {
var y = year - 1, m = month;
} else {
var y = (month == 1) ? year - 1 : year,
m = (month == 1) ? 12 : month - 1;
}
if(minDate && minDate.getFullYear() == year && minDate.getMonth() + 1 == month) {
return;
}
this.page(y, m);
} else if(opts.type == "monthly") {
this.page(year - 1);
} else if(opts.type == "yearly") {
this.page(year - 12);
}
this.emit("prev", [ e ]);
}
/**
* @method next
* Outputs a calendar that fits the next year/month
*
*/
this.next = function(e, moveYear) {
var opts = this.options;
if(opts.type == "daily") {
if (moveYear) {
var y = year + 1, m = month;
} else {
var y = (month == 12) ? year + 1 : year,
m = (month == 12) ? 1 : month + 1;
}
if(maxDate && maxDate.getFullYear() == year && maxDate.getMonth() + 1 == month) {
return;
}
this.page(y, m);
} else if(opts.type == "monthly") {
this.page(year + 1);
} else if(opts.type == "yearly") {
this.page(year + 12);
}
this.emit("next", [ e ]);
}
/**
* @method select
* Selects today if there is no value, or selects a date applicable to a timestamp or year/month/date
*
* @param {"year"/"month"/"date"/"timestamp"/"Date"}
*/
this.select = function() {
var opts = this.options,
args = arguments;
if(args.length == 0) {
y = year;
m = month;
d = date;
} else if(args.length == 3) {
y = args[0];
m = args[1];
d = args[2];
} else if(args.length == 1) {
var time = (_.typeCheck("date", args[0])) ? args[0] : new Date(args[0]);
y = time.getFullYear();
m = time.getMonth() + 1;
d = time.getDate();
}
if(opts.type == "daily") {
// 최소일과 최대일이 교차하는 경우
if(minDate || maxDate) {
var checkedDate = checkDate(y, m, d);
this.page(checkedDate[0], checkedDate[1]);
this.addTrigger(items[checkedDate[2]], "click");
}
this.page(y, m);
this.addTrigger(items[d], "click");
} else if(opts.type == "monthly") {
this.page(y);
this.addTrigger(items[m], "click");
} else if(opts.type == "yearly") {
this.page(y);
this.addTrigger(items[y], "click");
}
}
/**
* @method addTime
* Selects a date corresponding to the time added to the currently selected date
*
* @param {"Integer"/"Date"} time Timestamp or Date
*/
this.addTime = function(time) {
selDate = new Date(this.getTime() + time);
this.select(this.getTime());
}
/**
* @method getDate
* Gets the value of the date currently selected
*
* @return {Date} Date object
*/
this.getDate = function() {
return selDate;
}
/**
* @method getTime
* Gets the timestamp value of the date currently selected
*
* @return {Integer} Timestamp
*/
this.getTime = function() {
return selDate.getTime();
}
/**
* @method getFormat
* Gets a date string that fits the format entered
*
* @return {String} format Formatted date string
*/
this.getFormat = function(format) {
return _.dateFormat(selDate, (typeof(format) == "string") ? format : this.options.format);
}
/**
* @method reload
* Reloads the datepicker
*/
this.reload = function() {
var opts = this.options;
minDate = (_.typeCheck("date", opts.minDate)) ? opts.minDate : null;
maxDate = (_.typeCheck("date", opts.maxDate)) ? opts.maxDate : null;
if(opts.type == "daily") {
// 기본 날짜가 최소 날짜나 최대 날짜보다 작거나 큰 경우
if(minDate && opts.date < minDate) {
opts.date = minDate;
} else if(maxDate && opts.date > maxDate) {
opts.date = maxDate;
}
}
this.select();
this.emit("reload");
}
}
UI.setup = function() {
var now = getStartDate(new Date());
return {
/**
* @cfg {"daily"/"monthly"/"yearly"} [type="daily"]
* Determines the type of a calendar
*/
type: "daily",
/**
* @cfg {String} [titleFormat="yyyy.MM"]
* Title format of a calendar
*/
titleFormat: "yyyy.MM",
/**
* @cfg {String} [format="yyyy-MM-dd"]
* Format of the date handed over when selecting a specific date
*/
format: "yyyy-MM-dd",
/**
* @cfg {Date} [date="now"]
* Selects a specific date as a basic
*/
date: now,
/**
* @cfg {Boolean} [animate=false]
* @deprecated
*/
animate: false,
/**
* @cfg {Date} [minDate="null"]
* Selects a specific minimum date
*/
minDate: null,
/**
* @cfg {Date} [maxDate="null"]
* Selects a specific maximum date
*/
maxDate: null
};
}
/**
* @event select
* Event that occurs when selecting a specific date
*
* @param {String} value Formatted date string
* @param {EventObject} e The event object
*/
/**
* @event prev
* Event that occurs when clicking on the previous button
*
* @param {EventObject} e The event object
*/
/**
* @event next
* Event that occurs when clicking on the next button
*
* @param {EventObject} e The event object
*/
return UI;
});