1/* 2 Datetator jQuery Plugin 3 Datetator is a jQuery-based replacement for input boxes, making them date pickers. 4 version 1.2, Dec 11th, 2015 5 by Ingi á Steinamørk 6 7 The MIT License (MIT) 8 9 Copyright (c) 2014 QODIO 10 11 Permission is hereby granted, free of charge, to any person obtaining a copy of 12 this software and associated documentation files (the "Software"), to deal in 13 the Software without restriction, including without limitation the rights to 14 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 15 the Software, and to permit persons to whom the Software is furnished to do so, 16 subject to the following conditions: 17 18 The above copyright notice and this permission notice shall be included in all 19 copies or substantial portions of the Software. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 23 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 24 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 25 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 */ 28 29(function($) { 30 /** 31 * Datetator 32 * @param element 33 * @param options 34 */ 35 $.datetator = function (element, options) { 36 //// ================== SETTINGS ================== //// 37 var defaults = { 38 prefix: 'datetator_', 39 height: 'auto', 40 useDimmer: false, 41 useRemove: true, 42 class: '', 43 language: 'en', 44 labels: { 45 en: { 46 week: 'Wk', 47 dayNames: [ 48 'Mo', 49 'Tu', 50 'We', 51 'Th', 52 'Fr', 53 'Sa', 54 'Su' 55 ], 56 monthNames: [ 57 'January', 58 'February', 59 'March', 60 'April', 61 'May', 62 'June', 63 'July', 64 'August', 65 'September', 66 'October', 67 'November', 68 'December' 69 ], 70 previousMonth: '«', 71 nextMonth: '»', 72 previousYear: '«', 73 nextYear: '»', 74 empty: 'Remove', 75 today: 'Today', 76 previousMonthTooltip: 'Show previous month', 77 nextMonthTooltip: 'Show next month', 78 previousYearTooltip: 'Show previous year', 79 nextYearTooltip: 'Show next year', 80 emptyTooltip: 'Remove date', 81 todayTooltip: 'Show and choose today\'s date' 82 } 83 } 84 }; 85 86 //// ================== PREPARE VARIABLES ================== //// 87 var plugin = this; 88 var $element = $(element); 89 var $holder_element = null; 90 var $input_element = null; 91 var $picker_element = null; 92 var reloadDate = function () { 93 if (!isNaN(new Date($element.val()).getDate())) { 94 return new Date(new Date($element.val()).getFullYear(), new Date($element.val()).getMonth(), 1, 0, 0, 0); 95 } else { 96 return new Date(new Date().getFullYear(), new Date().getMonth(), 1, 0, 0, 0); 97 } 98 }; 99 var currentDate = reloadDate(); 100 /* For use later when adding support for keyboard 101 var key = { 102 backspace: 8, 103 enter: 13, 104 escape: 27, 105 left: 37, 106 up: 38, 107 right: 39, 108 down: 40 109 };*/ 110 plugin.settings = {}; 111 112 113 114 //// ================== "INITIALIZE PLUGIN" METHOD ================== //// 115 plugin.init = function () { 116 if (typeof datetator_labels !== 'undefined') { 117 $.extend(defaults.labels, datetator_labels); 118 } 119 plugin.settings = $.extend({}, defaults, options); 120 121 //// CREATE ELEMENTS 122 // dimmer 123 if (plugin.settings.useDimmer) { 124 if ($('#' + plugin.settings.prefix + 'dimmer').length === 0) { 125 var dimmer_element = document.createElement('div'); 126 $(dimmer_element).attr('id', plugin.settings.prefix + 'dimmer'); 127 $(dimmer_element).hide(); 128 $(document.body).prepend(dimmer_element); 129 } 130 } 131 // holder element 132 $holder_element = $(document.createElement('div')); 133 $holder_element.addClass(plugin.settings.prefix + 'holder picker-hidden'); 134 $holder_element.css({ 135 display: $element.css('display') 136 }); 137 $element.after($holder_element); 138 139 // input element 140 $input_element = $(document.createElement('input')); 141 $input_element.attr('type', 'text');
142 if (element.id !== undefined) { 143 $input_element.attr('id', plugin.settings.prefix + element.id); 144 } 145 if (element.hasAttribute('tabindex')) { 146 $input_element.attr('tabindex', $element.attr('tabindex')); 147 } 148 if (element.hasAttribute('placeholder')) { 149 $input_element.attr('placeholder', $element.attr('placeholder')); 150 } 151 $input_element.addClass(plugin.settings.prefix + 'element picker-hidden ' + plugin.settings.class); 152 $input_element.css({ 153 width: $element.css('width'), 154 padding: $element.css('padding'), 155 position: 'relative' 156 }); 157 $input_element.attr('readonly', true); 158 if (plugin.settings.height === 'element') { 159 $input_element.css({ 160 height: $element.outerHeight() + 'px' 161 }); 162 } 163 $holder_element.append($input_element); 164 $element.hide(); 165 166 // picker element 167 $picker_element = $(document.createElement('div')); 168 $picker_element.addClass(plugin.settings.prefix + 'picker'); 169 $holder_element.append($picker_element); 170 171 //// BIND ELEMENTS EVENTS 172 // source element 173 $element.on('change', function () { 174 currentDate = reloadDate(); 175 refreshPicker(); 176 }); 177 $input_element.on('mouseup', function (e) { 178 e.preventDefault(); 179 e.stopPropagation(); 180 $input_element.trigger('focus'); 181 }); 182 $input_element.focus(function () { 183 currentDate = reloadDate(); 184 refreshPicker(); 185 //$input_element.select(); 186 }); 187 $input_element.blur(function () { 188 hidePicker(); 189 }); 190 refreshPicker(); 191 }; 192 193 194 //// ================== "REFRESH PICKER" METHOD ================== //// 195 plugin.changeSetting = function (settingToChange, change) { 196 plugin.settings['settingToChange'] = change; 197 }; 198 plugin.refresh = function () { 199 refreshPicker(); 200 }; 201 var refreshPicker = function () { 202 $input_element.val(formatDate(new Date($element.val()), 2)); 203 204 $picker_element.empty(); 205 $picker_element.on('mousedown', function (e) { 206 e.preventDefault(); 207 e.stopPropagation(); 208 }); 209 210 // NAVIGATION HOLDER 211 var $nav_element = $(document.createElement('div')); 212 $nav_element.addClass(plugin.settings.prefix + 'navigation'); 213 $picker_element.append($nav_element); 214 215 // LINE 1 216 // previous month button 217 var $nav_prev_month_element = $(document.createElement('div')); 218 $nav_prev_month_element.addClass(plugin.settings.prefix + 'button'); 219 $nav_prev_month_element.addClass(plugin.settings.prefix + 'previous_month'); 220 $nav_prev_month_element.html(plugin.settings.labels[plugin.settings.language].previousMonth.replace('{month}', plugin.settings.labels[plugin.settings.language].monthNames[new Date(new Date(currentDate).setMonth(currentDate.getMonth() - 1)).getMonth()].substring(0, 3))); 221 $nav_prev_month_element.attr('title', plugin.settings.labels[plugin.settings.language].previousMonthTooltip); 222 $nav_prev_month_element.on('click', function (e) { 223 e.preventDefault(); 224 e.stopPropagation(); 225 previousMonth(); 226 }); 227 $nav_element.append($nav_prev_month_element); 228 // month holder 229 var $nav_month_element = $(document.createElement('div')); 230 $nav_month_element.addClass(plugin.settings.prefix + 'month'); 231 $nav_month_element.html(plugin.settings.labels[plugin.settings.language].monthNames[currentDate.getMonth()]); 232 $nav_element.append($nav_month_element); 233 // next month button 234 var $nav_next_month_element = $(document.createElement('div')); 235 $nav_next_month_element.addClass(plugin.settings.prefix + 'button'); 236 $nav_next_month_element.addClass(plugin.settings.prefix + 'next_month'); 237 $nav_next_month_element.html(plugin.settings.labels[plugin.settings.language].nextMonth.replace('{month}', plugin.settings.labels[plugin.settings.language].monthNames[new Date(new Date(currentDate).setMonth(currentDate.getMonth() + 1)).getMonth()].substring(0, 3))); 238 $nav_next_month_element.attr('title', plugin.settings.labels[plugin.settings.language].nextMonthTooltip); 239 $nav_next_month_element.on('click', function (e) { 240 e.preventDefault(); 241 e.stopPropagation(); 242 nextMonth(); 243 }); 244 $nav_element.append($nav_next_month_element); 245 246 // LINE 2 247 // previous year button
248 var $nav_prev_year_element = $(document.createElement('div')); 249 $nav_prev_year_element.addClass(plugin.settings.prefix + 'button'); 250 $nav_prev_year_element.addClass(plugin.settings.prefix + 'previous_year'); 251 $nav_prev_year_element.html(plugin.settings.labels[plugin.settings.language].previousYear.replace('{year}', new Date(new Date(currentDate).setYear(currentDate.getFullYear() - 1)).getFullYear().toString())); 252 $nav_prev_year_element.attr('title', plugin.settings.labels[plugin.settings.language].previousYearTooltip); 253 $nav_prev_year_element.on('click', function (e) { 254 e.preventDefault(); 255 e.stopPropagation(); 256 previousYear(); 257 }); 258 $nav_element.append($nav_prev_year_element); 259 // year holder 260 var $nav_year_element = $(document.createElement('div')); 261 $nav_year_element.addClass(plugin.settings.prefix + 'year'); 262 $nav_year_element.html(currentDate.getFullYear()); 263 $nav_element.append($nav_year_element); 264 // next year button 265 var $nav_next_year_element = $(document.createElement('div')); 266 $nav_next_year_element.addClass(plugin.settings.prefix + 'button'); 267 $nav_next_year_element.addClass(plugin.settings.prefix + 'next_year'); 268 $nav_next_year_element.html(plugin.settings.labels[plugin.settings.language].nextYear.replace('{year}', new Date(new Date(currentDate).setYear(currentDate.getFullYear() + 1)).getFullYear().toString())); 269 $nav_next_year_element.attr('title', plugin.settings.labels[plugin.settings.language].nextYearTooltip); 270 $nav_next_year_element.on('click', function (e) { 271 e.preventDefault(); 272 e.stopPropagation(); 273 nextYear(); 274 }); 275 $nav_element.append($nav_next_year_element); 276 277 // CALENDAR HOLDER 278 var $calendar_element = $(document.createElement('table')); 279 $calendar_element.addClass(plugin.settings.prefix + 'calendar'); 280 $picker_element.append($calendar_element); 281 // tr element (header) 282 var $calendar_tr_element = $(document.createElement('tr')); 283 $calendar_element.append($calendar_tr_element); 284 // td element (week header) 285 var $calendar_td_element = $(document.createElement('th')); 286 $calendar_td_element.addClass(plugin.settings.prefix + 'week_header'); 287 $calendar_td_element.html(plugin.settings.labels[plugin.settings.language].week); 288 $calendar_tr_element.append($calendar_td_element); 289 for (var i = 0; i < 7; i++) { 290 // td element (day header) 291 $calendar_td_element = $(document.createElement('th')); 292 $calendar_td_element.addClass(plugin.settings.prefix + 'day_header'); 293 $calendar_td_element.html(plugin.settings.labels[plugin.settings.language].dayNames[i]); 294 $calendar_tr_element.append($calendar_td_element); 295 } 296 297 // DATES 298 var firstDayOfMonth = currentDate; 299 var lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, currentDate.getDate() - 1); 300 var firstWeekAndYear = getWeekAndYear(firstDayOfMonth); 301 var lastWeekAndYear = getWeekAndYear(lastDayOfMonth); 302 var mondayFirstWeek = getDateOfISOWeek(firstWeekAndYear.week, firstWeekAndYear.year); 303 var sundayLastWeek = getDateOfISOWeek(lastWeekAndYear.week, lastWeekAndYear.year, 6); 304 var initDate = new Date(); 305 306 var rows = Math.ceil((getDateDifferenceInDays(mondayFirstWeek, sundayLastWeek)) / 7); 307 for (var row = 0; row < rows; row++) { 308 // tr element 309 var $tr_element = $(document.createElement('tr')); 310 $calendar_element.append($tr_element); 311 // td element (week) 312 var $td_element = $(document.createElement('td')); 313 $td_element.addClass(plugin.settings.prefix + 'week'); 314 $td_element.html(getWeekAndYear(new Date(firstDayOfMonth).setDate(firstDayOfMonth.getDate() + (row * 7))).week); 315 $tr_element.append($td_element); 316 for (var x = 0; x < 7; x++) { 317 // td element (day) 318 $td_element = $(document.createElement('td')); 319 var date = new Date(mondayFirstWeek.getFullYear(), mondayFirstWeek.getMonth(), mondayFirstWeek.getDate() + (x + (row * 7))); 320 $td_element.html(date.getDate()); 321 $td_element.data('date', date); 322 $td_element.on('click', function () { 323 selectDate($(this).data('date')); 324 });
325 $td_element.addClass(plugin.settings.prefix + 'day'); 326 if (formatDate(date, 0) === $element.val()) { 327 $td_element.addClass(plugin.settings.prefix + 'day_active'); 328 } 329 if (date < firstDayOfMonth || date > lastDayOfMonth) { 330 $td_element.addClass(plugin.settings.prefix + 'day_other'); 331 } 332 if (date.getDay() === 0 || date.getDay() === 6) { 333 $td_element.addClass(plugin.settings.prefix + 'day_weekend'); 334 } 335 if (date.toDateString() === initDate.toDateString()) { 336 $td_element.addClass(plugin.settings.prefix + 'day_today'); 337 } 338 $tr_element.append($td_element); 339 } 340 } 341 342 // operations holder 343 var $operations_element = $(document.createElement('div')); 344 $operations_element.addClass(plugin.settings.prefix + 'operations'); 345 $picker_element.append($operations_element); 346 // "remove" button 347 if (plugin.settings.useRemove) { 348 var $opr_delete_element = $(document.createElement('div')); 349 $opr_delete_element.addClass(plugin.settings.prefix + 'button'); 350 $opr_delete_element.addClass(plugin.settings.prefix + 'empty'); 351 $opr_delete_element.html(plugin.settings.labels[plugin.settings.language].empty); 352 $opr_delete_element.attr('title', plugin.settings.labels[plugin.settings.language].emptyTooltip); 353 $opr_delete_element.on('click', function (e) { 354 e.preventDefault(); 355 e.stopPropagation(); 356 empty(); 357 }); 358 $operations_element.append($opr_delete_element); 359 } 360 // "today" button 361 var $opr_today_element = $(document.createElement('div')); 362 $opr_today_element.addClass(plugin.settings.prefix + 'button'); 363 $opr_today_element.addClass(plugin.settings.prefix + 'today'); 364 $opr_today_element.html(plugin.settings.labels[plugin.settings.language].today); 365 $opr_today_element.attr('title', plugin.settings.labels[plugin.settings.language].todayTooltip); 366 $opr_today_element.on('click', function (e) { 367 e.preventDefault(); 368 e.stopPropagation(); 369 today(); 370 }); 371 $operations_element.append($opr_today_element); 372 373 if (document.activeElement === $input_element[0]) { 374 showPicker(); 375 } 376 377 // place popup 378 if (($holder_element[0].getBoundingClientRect().bottom + 250) < Math.max(document.documentElement.clientHeight, window.innerHeight || 0)) { 379 $picker_element.css({ 380 top: $input_element.outerHeight() + 3, 381 bottom: 'auto' 382 }); 383 } else { 384 $picker_element.css({ 385 top: 'auto', 386 bottom: $input_element.outerHeight() + 3 387 }); 388 } 389 390 }; 391 392 393 //// ================== DATE CHOOSING METHODS ================== //// 394 var today = function () { 395 currentDate.setYear(new Date().getFullYear()); 396 currentDate.setMonth(new Date().getMonth()); 397 $element.val(formatDate(new Date(), 0)); 398 $element.trigger('change'); 399 hidePicker(); 400 }; 401 402 var previousMonth = function () { 403 currentDate.setMonth(currentDate.getMonth() - 1); 404 refreshPicker(); 405 }; 406 var nextMonth = function () { 407 currentDate.setMonth(currentDate.getMonth() + 1); 408 refreshPicker(); 409 }; 410 411 var previousYear = function () { 412 currentDate.setYear(currentDate.getFullYear() - 1); 413 refreshPicker(); 414 }; 415 var nextYear = function () { 416 currentDate.setYear(currentDate.getFullYear() + 1); 417 refreshPicker(); 418 }; 419 420 var selectDate = function (date) { 421 $element.val(formatDate(date, 0)); 422 $element.trigger('change'); 423 hidePicker(); 424 }; 425 426 427 //// ================== OTHER METHODS ================== //// 428 var empty = function () { 429 $element.val(''); 430 $element.trigger('change'); 431 hidePicker(); 432 }; 433 434 var showPicker = function () { 435 $holder_element.removeClass('picker-hidden').addClass('picker-visible'); 436 if (plugin.settings.useDimmer) { 437 $('#' + plugin.settings.prefix + 'dimmer').show(); 438 } 439 }; 440 441 var hidePicker = function () { 442 $holder_element.removeClass('picker-visible').addClass('picker-hidden'); 443 if (plugin.settings.useDimmer) { 444 $('#' + plugin.settings.prefix + 'dimmer').hide(); 445 } 446 }; 447 448 var formatDate = function (date, type) { 449 type = type !== undefined ? type : 1; 450 date = typeof date === 'object' ? date : new Date(date); 451 if (isNaN(date.getDate())) { 452 return ''; 453 } 454 var day = date.getDate();
455 var month = date.getMonth() + 1; 456 var year = date.getFullYear(); 457 switch (type) { 458 case 0: 459 return year + '-' + (month > 9 ? month : '0' + month) + '-' + (day > 9 ? day : '0' + day); 460 break; 461 case 1: 462 return (day > 9 ? day : '0' + day) + '-' + (month > 9 ? month : '0' + month) + '-' + year; 463 break; 464 case 2: 465 return day + '. ' + plugin.settings.labels[plugin.settings.language].monthNames[month - 1] + ' ' + year; 466 break; 467 case 3: 468 return day + '. ' + plugin.settings.labels[plugin.settings.language].monthNames[month - 1].substring(0, 3) + '. ' + year; 469 break; 470 } 471 return 'Invalid Type'; 472 }; 473 474 var getWeekAndYear = function(date) { 475 date = new Date(date); 476 date.setHours(0,0,0); 477 date.setDate(date.getDate() + 4 - (date.getDay() || 7)); 478 var yearStart = new Date(date.getFullYear(), 0, 1); 479 var weekNo = Math.ceil((((date - yearStart) / 86400000) + 1) / 7); 480 return { week: weekNo, year: date.getFullYear() }; 481 }; 482 483 var getDateOfISOWeek = function(week, year, weekDay) { 484 weekDay = weekDay ? weekDay : 0; 485 var simple = new Date(year, 0, 1 + (week - 1) * 7); 486 var date = new Date(simple.setDate((simple.getDay() <= 4) ? (simple.getDate() - simple.getDay() + 1) : (simple.getDate() + 8 - simple.getDay()))); 487 return new Date(date.setDate(date.getDate() + weekDay)); 488 }; 489 490 var getDateDifferenceInDays = function (date1, date2) { 491 var dayMs = 24 * 60 * 60 * 1000; 492 var utc1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate()); 493 var utc2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate()); 494 return Math.floor((utc2 - utc1) / dayMs); 495 }; 496 497 498 //// ================== "DESTROY" METHOD ================== //// 499 plugin.destroy = function () { 500 $input_element.remove(); 501 $.removeData(element, 'datetator'); 502 $element.unbind('change', refreshPicker); 503 $element.show(); 504 if ($('.datetator').length === 0) { 505 $('#' + plugin.settings.prefix + 'dimmer').remove(); 506 } 507 }; 508 509 510 //// ================== INITIALIZE PLUGIN ================== //// 511 plugin.init(); 512 }; 513 514 515 //// ================== INSTANTIATE PLUGIN METHOD ================== //// 516 $.fn.datetator = function(options) { 517 options = options !== undefined ? options : {}; 518 return this.each(function () { 519 if (typeof(options) === 'object') { 520 if ($(this).data('datetator') === undefined) { 521 var plugin = new $.datetator(this, options); 522 $(this).data('datetator', plugin); 523 } 524 } else if (typeof $(this).data('datetator') !== 'undefined' && $(this).data('datetator')[options]) { 525 $(this).data('datetator')[options].apply(this, Array.prototype.slice.call(arguments, 1)); 526 } else { 527 //$.error('Method ' + options + ' does not exist in $.datetator'); 528 } 529 }); 530 }; 531 532}(jQuery)); 533 534$(function () { 535 $('.datetator').each(function () { 536 var $this = $(this); 537 var options = {}; 538 $.each($this.data(), function (key, value) { 539 if (key.substring(0, 9) === 'datetator') { 540 options[key.substring(9, 10).toLowerCase() + key.substring(10)] = value; 541 } 542 }); 543 $this.datetator(options); 544 }); 545});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.