/*jslint eqeq: true, plusplus: true, undef: true, sloppy: true, vars: true, forin: true */
(function ($) {
var ms = $.mobiscroll,
date = new date(),
defaults = {
dateformat: 'mm/dd/yy',
dateorder: 'mmddy',
timewheels: 'hhiia',
timeformat: 'hh:ii a',
startyear: date.getfullyear() - 100,
endyear: date.getfullyear() + 1,
monthnames: ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'],
monthnamesshort: ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'],
daynames: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'],
daynamesshort: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
shortyearcutoff: '+10',
monthtext: 'month',
daytext: 'day',
yeartext: 'year',
hourtext: 'hours',
minutetext: 'minutes',
sectext: 'seconds',
ampmtext: ' ',
nowtext: 'now',
shownow: false,
stephour: 1,
stepminute: 1,
stepsecond: 1,
separator: ' '
},
preset = function (inst) {
var that = $(this),
html5def = {},
format;
// force format for html5 date inputs (experimental)
if (that.is('input')) {
switch (that.attr('type')) {
case 'date':
format = 'yy-mm-dd';
break;
case 'datetime':
format = 'yy-mm-ddthh:ii:ssz';
break;
case 'datetime-local':
format = 'yy-mm-ddthh:ii:ss';
break;
case 'month':
format = 'yy-mm';
html5def.dateorder = 'mmyy';
break;
case 'time':
format = 'hh:ii:ss';
break;
}
// check for min/max attributes
var min = that.attr('min'),
max = that.attr('max');
if (min) {
html5def.mindate = ms.parsedate(format, min);
}
if (max) {
html5def.maxdate = ms.parsedate(format, max);
}
}
// set year-month-day order
var s = $.extend({}, defaults, html5def, inst.settings),
offset = 0,
wheels = [],
ord = [],
o = {},
i,
k,
f = { y: 'getfullyear', m: 'getmonth', d: 'getdate', h: gethour, i: getminute, s: getsecond, a: getampm },
p = s.preset,
dord = s.dateorder,
tord = s.timewheels,
regen = dord.match(/d/),
ampm = tord.match(/a/i),
hampm = tord.match(/h/),
hformat = p == 'datetime' ? s.dateformat + s.separator + s.timeformat : p == 'time' ? s.timeformat : s.dateformat,
defd = new date(),
steph = s.stephour,
stepm = s.stepminute,
steps = s.stepsecond,
mind = s.mindate || new date(s.startyear, 0, 1),
maxd = s.maxdate || new date(s.endyear, 11, 31, 23, 59, 59);
inst.settings = s;
format = format || hformat;
if (p.match(/date/i)) {
// determine the order of year, month, day wheels
$.each(['y', 'm', 'd'], function (j, v) {
i = dord.search(new regexp(v, 'i'));
if (i > -1) {
ord.push({ o: i, v: v });
}
});
ord.sort(function (a, b) { return a.o > b.o ? 1 : -1; });
$.each(ord, function (i, v) {
o[v.v] = i;
});
var w = {};
for (k = 0; k < 3; k++) {
if (k == o.y) {
offset++;
w[s.yeartext] = {};
var start = mind.getfullyear(),
end = maxd.getfullyear();
for (i = start; i <= end; i++) {
w[s.yeartext][i] = dord.match(/yy/i) ? i : (i + '').substr(2, 2);
}
} else if (k == o.m) {
offset++;
w[s.monthtext] = {};
for (i = 0; i < 12; i++) {
var str = dord.replace(/[dy]/gi, '').replace(/mm/, i < 9 ? '0' + (i + 1) : i + 1).replace(/m/, (i + 1));
w[s.monthtext][i] = str.match(/mm/) ? str.replace(/mm/, '' + s.monthnames[i] + '') : str.replace(/m/, '' + s.monthnamesshort[i] + '');
}
} else if (k == o.d) {
offset++;
w[s.daytext] = {};
for (i = 1; i < 32; i++) {
w[s.daytext][i] = dord.match(/dd/i) && i < 10 ? '0' + i : i;
}
}
}
wheels.push(w);
}
if (p.match(/time/i)) {
// determine the order of hours, minutes, seconds wheels
ord = [];
$.each(['h', 'i', 's', 'a'], function (i, v) {
i = tord.search(new regexp(v, 'i'));
if (i > -1) {
ord.push({ o: i, v: v });
}
});
ord.sort(function (a, b) {
return a.o > b.o ? 1 : -1;
});
$.each(ord, function (i, v) {
o[v.v] = offset + i;
});
w = {};
for (k = offset; k < offset + 4; k++) {
if (k == o.h) {
offset++;
w[s.hourtext] = {};
for (i = 0; i < (hampm ? 12 : 24); i += steph) {
w[s.hourtext][i] = hampm && i == 0 ? 12 : tord.match(/hh/i) && i < 10 ? '0' + i : i;
}
} else if (k == o.i) {
offset++;
w[s.minutetext] = {};
for (i = 0; i < 60; i += stepm) {
w[s.minutetext][i] = tord.match(/ii/) && i < 10 ? '0' + i : i;
}
} else if (k == o.s) {
offset++;
w[s.sectext] = {};
for (i = 0; i < 60; i += steps) {
w[s.sectext][i] = tord.match(/ss/) && i < 10 ? '0' + i : i;
}
} else if (k == o.a) {
offset++;
var upper = tord.match(/a/);
w[s.ampmtext] = { 0: upper ? 'am' : 'am', 1: upper ? 'pm' : 'pm' };
}
}
wheels.push(w);
}
function get(d, i, def) {
if (o[i] !== undefined) {
return +d[o[i]];
}
if (def !== undefined) {
return def;
}
return defd[f[i]] ? defd[f[i]]() : f[i](defd);
}
function step(v, st) {
return math.floor(v / st) * st;
}
function gethour(d) {
var hour = d.gethours();
hour = hampm && hour >= 12 ? hour - 12 : hour;
return step(hour, steph);
}
function getminute(d) {
return step(d.getminutes(), stepm);
}
function getsecond(d) {
return step(d.getseconds(), steps);
}
function getampm(d) {
return ampm && d.gethours() > 11 ? 1 : 0;
}
function getdate(d) {
var hour = get(d, 'h', 0);
return new date(get(d, 'y'), get(d, 'm'), get(d, 'd', 1), get(d, 'a') ? hour + 12 : hour, get(d, 'i', 0), get(d, 's', 0));
}
inst.setdate = function (d, fill, time, temp) {
var i;
// set wheels
for (i in o) {
this.temp[o[i]] = d[f[i]] ? d[f[i]]() : f[i](d);
}
this.setvalue(true, fill, time, temp);
};
inst.getdate = function (d) {
return getdate(d);
};
return {
button3text: s.shownow ? s.nowtext : undefined,
button3: s.shownow ? function () { inst.setdate(new date(), false, 0.3, true); } : undefined,
wheels: wheels,
headertext: function (v) {
return ms.formatdate(hformat, getdate(inst.temp), s);
},
/**
* builds a date object from the wheel selections and formats it to the given date/time format
* @param {array} d - an array containing the selected wheel values
* @return {string} - the formatted date string
*/
formatresult: function (d) {
return ms.formatdate(format, getdate(d), s);
},
/**
* builds a date object from the input value and returns an array to set wheel values
* @return {array} - an array containing the wheel values to set
*/
parsevalue: function (val) {
var d = new date(),
i,
result = [];
try {
d = ms.parsedate(format, val, s);
} catch (e) {
}
// set wheels
for (i in o) {
result[o[i]] = d[f[i]] ? d[f[i]]() : f[i](d);
}
return result;
},
/**
* validates the selected date to be in the mindate / maxdate range and sets unselectable values to disabled
* @param {object} dw - jquery object containing the generated html
* @param {integer} [i] - index of the changed wheel, not set for initial validation
*/
validate: function (dw, i) {
var temp = inst.temp, //.slice(0),
mins = { y: mind.getfullyear(), m: 0, d: 1, h: 0, i: 0, s: 0, a: 0 },
maxs = { y: maxd.getfullyear(), m: 11, d: 31, h: step(hampm ? 11 : 23, steph), i: step(59, stepm), s: step(59, steps), a: 1 },
minprop = true,
maxprop = true;
$.each(['y', 'm', 'd', 'a', 'h', 'i', 's'], function (x, i) {
if (o[i] !== undefined) {
var min = mins[i],
max = maxs[i],
maxdays = 31,
val = get(temp, i),
t = $('.dw-ul', dw).eq(o[i]),
y,
m;
if (i == 'd') {
y = get(temp, 'y');
m = get(temp, 'm');
maxdays = 32 - new date(y, m, 32).getdate();
max = maxdays;
if (regen) {
$('.dw-li', t).each(function () {
var that = $(this),
d = that.data('val'),
w = new date(y, m, d).getday(),
str = dord.replace(/[my]/gi, '').replace(/dd/, d < 10 ? '0' + d : d).replace(/d/, d);
$('.dw-i', that).html(str.match(/dd/) ? str.replace(/dd/, '' + s.daynames[w] + '') : str.replace(/d/, '' + s.daynamesshort[w] + ''));
});
}
}
if (minprop && mind) {
min = mind[f[i]] ? mind[f[i]]() : f[i](mind);
}
if (maxprop && maxd) {
max = maxd[f[i]] ? maxd[f[i]]() : f[i](maxd);
}
if (i != 'y') {
var i1 = $('.dw-li', t).index($('.dw-li[data-val="' + min + '"]', t)),
i2 = $('.dw-li', t).index($('.dw-li[data-val="' + max + '"]', t));
$('.dw-li', t).removeclass('dw-v').slice(i1, i2 + 1).addclass('dw-v');
if (i == 'd') { // hide days not in month
$('.dw-li', t).removeclass('dw-h').slice(maxdays).addclass('dw-h');
}
}
if (val < min) {
val = min;
}
if (val > max) {
val = max;
}
if (minprop) {
minprop = val == min;
}
if (maxprop) {
maxprop = val == max;
}
// disable some days
if (s.invalid && i == 'd') {
var idx = [];
// disable exact dates
if (s.invalid.dates) {
$.each(s.invalid.dates, function (i, v) {
if (v.getfullyear() == y && v.getmonth() == m) {
idx.push(v.getdate() - 1);
}
});
}
// disable days of week
if (s.invalid.daysofweek) {
var first = new date(y, m, 1).getday(),
j;
$.each(s.invalid.daysofweek, function (i, v) {
for (j = v - first; j < maxdays; j += 7) {
if (j >= 0) {
idx.push(j);
}
}
});
}
// disable days of month
if (s.invalid.daysofmonth) {
$.each(s.invalid.daysofmonth, function (i, v) {
v = (v + '').split('/');
if (v[1]) {
if (v[0] - 1 == m) {
idx.push(v[1] - 1);
}
} else {
idx.push(v[0] - 1);
}
});
}
$.each(idx, function (i, v) {
$('.dw-li', t).eq(v).removeclass('dw-v');
});
}
// set modified value
temp[o[i]] = val;
}
});
},
methods: {
/**
* returns the currently selected date.
* @param {boolean} temp - if true, return the currently shown date on the picker, otherwise the last selected one
* @return {date}
*/
getdate: function (temp) {
var inst = $(this).mobiscroll('getinst');
if (inst) {
return inst.getdate(temp ? inst.temp : inst.values);
}
},
/**
* sets the selected date
* @param {date} d - date to select.
* @param {boolean} [fill] - also set the value of the associated input element. default is true.
* @return {object} - jquery object to maintain chainability
*/
setdate: function (d, fill, time, temp) {
if (fill == undefined) {
fill = false;
}
return this.each(function () {
var inst = $(this).mobiscroll('getinst');
if (inst) {
inst.setdate(d, fill, time, temp);
}
});
}
}
};
};
$.each(['date', 'time', 'datetime'], function (i, v) {
ms.presets[v] = preset;
ms.presetshort(v);
});
/**
* format a date into a string value with a specified format.
* @param {string} format - output format.
* @param {date} date - date to format.
* @param {object} settings - settings.
* @return {string} - returns the formatted date string.
*/
ms.formatdate = function (format, date, settings) {
if (!date) {
return null;
}
var s = $.extend({}, defaults, settings),
look = function (m) { // check whether a format character is doubled
var n = 0;
while (i + 1 < format.length && format.charat(i + 1) == m) {
n++;
i++;
}
return n;
},
f1 = function (m, val, len) { // format a number, with leading zero if necessary
var n = '' + val;
if (look(m)) {
while (n.length < len) {
n = '0' + n;
}
}
return n;
},
f2 = function (m, val, s, l) { // format a name, short or long as requested
return (look(m) ? l[val] : s[val]);
},
i,
output = '',
literal = false;
for (i = 0; i < format.length; i++) {
if (literal) {
if (format.charat(i) == "'" && !look("'")) {
literal = false;
} else {
output += format.charat(i);
}
} else {
switch (format.charat(i)) {
case 'd':
output += f1('d', date.getdate(), 2);
break;
case 'd':
output += f2('d', date.getday(), s.daynamesshort, s.daynames);
break;
case 'o':
output += f1('o', (date.gettime() - new date(date.getfullyear(), 0, 0).gettime()) / 86400000, 3);
break;
case 'm':
output += f1('m', date.getmonth() + 1, 2);
break;
case 'm':
output += f2('m', date.getmonth(), s.monthnamesshort, s.monthnames);
break;
case 'y':
output += (look('y') ? date.getfullyear() : (date.getyear() % 100 < 10 ? '0' : '') + date.getyear() % 100);
break;
case 'h':
var h = date.gethours();
output += f1('h', (h > 12 ? (h - 12) : (h == 0 ? 12 : h)), 2);
break;
case 'h':
output += f1('h', date.gethours(), 2);
break;
case 'i':
output += f1('i', date.getminutes(), 2);
break;
case 's':
output += f1('s', date.getseconds(), 2);
break;
case 'a':
output += date.gethours() > 11 ? 'pm' : 'am';
break;
case 'a':
output += date.gethours() > 11 ? 'pm' : 'am';
break;
case "'":
if (look("'")) {
output += "'";
} else {
literal = true;
}
break;
default:
output += format.charat(i);
}
}
}
return output;
};
/**
* extract a date from a string value with a specified format.
* @param {string} format - input format.
* @param {string} value - string to parse.
* @param {object} settings - settings.
* @return {date} - returns the extracted date.
*/
ms.parsedate = function (format, value, settings) {
var def = new date();
if (!format || !value) {
return def;
}
value = (typeof value == 'object' ? value.tostring() : value + '');
var s = $.extend({}, defaults, settings),
shortyearcutoff = s.shortyearcutoff,
year = def.getfullyear(),
month = def.getmonth() + 1,
day = def.getdate(),
doy = -1,
hours = def.gethours(),
minutes = def.getminutes(),
seconds = 0, //def.getseconds(),
ampm = -1,
literal = false, // check whether a format character is doubled
lookahead = function (match) {
var matches = (iformat + 1 < format.length && format.charat(iformat + 1) == match);
if (matches) {
iformat++;
}
return matches;
},
getnumber = function (match) { // extract a number from the string value
lookahead(match);
var size = (match == '@' ? 14 : (match == '!' ? 20 : (match == 'y' ? 4 : (match == 'o' ? 3 : 2)))),
digits = new regexp('^\\d{1,' + size + '}'),
num = value.substr(ivalue).match(digits);
if (!num) {
return 0;
}
//throw 'missing number at position ' + ivalue;
ivalue += num[0].length;
return parseint(num[0], 10);
},
getname = function (match, s, l) { // extract a name from the string value and convert to an index
var names = (lookahead(match) ? l : s),
i;
for (i = 0; i < names.length; i++) {
if (value.substr(ivalue, names[i].length).tolowercase() == names[i].tolowercase()) {
ivalue += names[i].length;
return i + 1;
}
}
return 0;
//throw 'unknown name at position ' + ivalue;
},
checkliteral = function () {
//if (value.charat(ivalue) != format.charat(iformat))
//throw 'unexpected literal at position ' + ivalue;
ivalue++;
},
ivalue = 0,
iformat;
for (iformat = 0; iformat < format.length; iformat++) {
if (literal) {
if (format.charat(iformat) == "'" && !lookahead("'")) {
literal = false;
} else {
checkliteral();
}
} else {
switch (format.charat(iformat)) {
case 'd':
day = getnumber('d');
break;
case 'd':
getname('d', s.daynamesshort, s.daynames);
break;
case 'o':
doy = getnumber('o');
break;
case 'm':
month = getnumber('m');
break;
case 'm':
month = getname('m', s.monthnamesshort, s.monthnames);
break;
case 'y':
year = getnumber('y');
break;
case 'h':
hours = getnumber('h');
break;
case 'h':
hours = getnumber('h');
break;
case 'i':
minutes = getnumber('i');
break;
case 's':
seconds = getnumber('s');
break;
case 'a':
ampm = getname('a', ['am', 'pm'], ['am', 'pm']) - 1;
break;
case 'a':
ampm = getname('a', ['am', 'pm'], ['am', 'pm']) - 1;
break;
case "'":
if (lookahead("'")) {
checkliteral();
} else {
literal = true;
}
break;
default:
checkliteral();
}
}
}
if (year < 100) {
year += new date().getfullyear() - new date().getfullyear() % 100 +
(year <= (typeof shortyearcutoff != 'string' ? shortyearcutoff : new date().getfullyear() % 100 + parseint(shortyearcutoff, 10)) ? 0 : -100);
}
if (doy > -1) {
month = 1;
day = doy;
do {
var dim = 32 - new date(year, month - 1, 32).getdate();
if (day <= dim) {
break;
}
month++;
day -= dim;
} while (true);
}
hours = (ampm == -1) ? hours : ((ampm && hours < 12) ? (hours + 12) : (!ampm && hours == 12 ? 0 : hours));
var date = new date(year, month - 1, day, hours, minutes, seconds);
if (date.getfullyear() != year || date.getmonth() + 1 != month || date.getdate() != day) {
throw 'invalid date';
}
return date;
};
})(jquery);