/*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);