annotate bootstrap-year-calendar/bootstrap-year-calendar.js @ 107:24a967efbf3e

add dmd
author paulo
date Fri, 03 Apr 2020 23:58:24 -0700
parents
children
rev   line source
paulo@103 1 /* =========================================================
paulo@103 2 * Bootstrap year calendar v1.1.0
paulo@103 3 * Repo: https://github.com/Paul-DS/bootstrap-year-calendar
paulo@103 4 * =========================================================
paulo@103 5 * Created by Paul David-Sivelle
paulo@103 6 *
paulo@103 7 * Licensed under the Apache License, Version 2.0 (the "License");
paulo@103 8 * you may not use this file except in compliance with the License.
paulo@103 9 * You may obtain a copy of the License at
paulo@103 10 *
paulo@103 11 * http://www.apache.org/licenses/LICENSE-2.0
paulo@103 12 *
paulo@103 13 * Unless required by applicable law or agreed to in writing, software
paulo@103 14 * distributed under the License is distributed on an "AS IS" BASIS,
paulo@103 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
paulo@103 16 * See the License for the specific language governing permissions and
paulo@103 17 * limitations under the License.
paulo@103 18 * ========================================================= */
paulo@103 19
paulo@103 20 (function($) {
paulo@103 21 var Calendar = function(element, options) {
paulo@103 22 this.element = element;
paulo@103 23 this.element.addClass('calendar');
paulo@103 24
paulo@103 25 this._initializeEvents(options);
paulo@103 26 this._initializeOptions(options);
paulo@103 27 this._render();
paulo@103 28 };
paulo@103 29
paulo@103 30 Calendar.prototype = {
paulo@103 31 constructor: Calendar,
paulo@103 32 _initializeOptions: function(opt) {
paulo@103 33 if(opt == null) {
paulo@103 34 opt = [];
paulo@103 35 }
paulo@103 36
paulo@103 37 this.options = {
paulo@103 38 startYear: !isNaN(parseInt(opt.startYear)) ? parseInt(opt.startYear) : new Date().getFullYear(),
paulo@103 39 minDate: opt.minDate instanceof Date ? opt.minDate : null,
paulo@103 40 maxDate: opt.maxDate instanceof Date ? opt.maxDate : null,
paulo@103 41 language: (opt.language != null && dates[opt.language] != null) ? opt.language : 'en',
paulo@103 42 allowOverlap: opt.allowOverlap != null ? opt.allowOverlap : true,
paulo@103 43 displayWeekNumber: opt.displayWeekNumber != null ? opt.displayWeekNumber : false,
paulo@103 44 alwaysHalfDay: opt.alwaysHalfDay != null ? opt.alwaysHalfDay : false,
paulo@103 45 enableRangeSelection: opt.enableRangeSelection != null ? opt.enableRangeSelection : false,
paulo@103 46 disabledDays: opt.disabledDays instanceof Array ? opt.disabledDays : [],
paulo@103 47 roundRangeLimits: opt.roundRangeLimits != null ? opt.roundRangeLimits : false,
paulo@103 48 dataSource: opt.dataSource instanceof Array != null ? opt.dataSource : [],
paulo@103 49 style: opt.style == 'background' || opt.style == 'border' || opt.style == 'custom' ? opt.style : 'border',
paulo@103 50 enableContextMenu: opt.enableContextMenu != null ? opt.enableContextMenu : false,
paulo@103 51 contextMenuItems: opt.contextMenuItems instanceof Array ? opt.contextMenuItems : [],
paulo@103 52 customDayRenderer : $.isFunction(opt.customDayRenderer) ? opt.customDayRenderer : null,
paulo@103 53 customDataSourceRenderer : $.isFunction(opt.customDataSourceRenderer) ? opt.customDataSourceRenderer : null
paulo@103 54 };
paulo@103 55
paulo@103 56 this._initializeDatasourceColors();
paulo@103 57 },
paulo@103 58 _initializeEvents: function(opt) {
paulo@103 59 if(opt == null) {
paulo@103 60 opt = [];
paulo@103 61 }
paulo@103 62
paulo@103 63 if(opt.renderEnd) { this.element.bind('renderEnd', opt.renderEnd); }
paulo@103 64 if(opt.clickDay) { this.element.bind('clickDay', opt.clickDay); }
paulo@103 65 if(opt.dayContextMenu) { this.element.bind('dayContextMenu', opt.dayContextMenu); }
paulo@103 66 if(opt.selectRange) { this.element.bind('selectRange', opt.selectRange); }
paulo@103 67 if(opt.mouseOnDay) { this.element.bind('mouseOnDay', opt.mouseOnDay); }
paulo@103 68 if(opt.mouseOutDay) { this.element.bind('mouseOutDay', opt.mouseOutDay); }
paulo@103 69 },
paulo@103 70 _initializeDatasourceColors: function() {
paulo@103 71 for(var i in this.options.dataSource) {
paulo@103 72 if(this.options.dataSource[i].color == null) {
paulo@103 73 this.options.dataSource[i].color = colors[i % colors.length];
paulo@103 74 }
paulo@103 75 }
paulo@103 76 },
paulo@103 77 _render: function() {
paulo@103 78 this.element.empty();
paulo@103 79
paulo@103 80 this._renderHeader();
paulo@103 81 this._renderBody();
paulo@103 82 this._renderDataSource();
paulo@103 83
paulo@103 84 this._applyEvents();
paulo@103 85 this.element.find('.months-container').fadeIn(500);
paulo@103 86
paulo@103 87 this._triggerEvent('renderEnd', { currentYear: this.options.startYear });
paulo@103 88 },
paulo@103 89 _renderHeader: function() {
paulo@103 90 var header = $(document.createElement('div'));
paulo@103 91 header.addClass('calendar-header panel panel-default');
paulo@103 92
paulo@103 93 var headerTable = $(document.createElement('table'));
paulo@103 94
paulo@103 95 var prevDiv = $(document.createElement('th'));
paulo@103 96 prevDiv.addClass('prev');
paulo@103 97
paulo@103 98 if(this.options.minDate != null && this.options.minDate > new Date(this.options.startYear - 1, 11, 31)) {
paulo@103 99 prevDiv.addClass('disabled');
paulo@103 100 }
paulo@103 101
paulo@103 102 var prevIcon = $(document.createElement('span'));
paulo@103 103 prevIcon.addClass('glyphicon glyphicon-chevron-left');
paulo@103 104
paulo@103 105 prevDiv.append(prevIcon);
paulo@103 106
paulo@103 107 headerTable.append(prevDiv);
paulo@103 108
paulo@103 109 var prev2YearDiv = $(document.createElement('th'));
paulo@103 110 prev2YearDiv.addClass('year-title year-neighbor2 hidden-sm hidden-xs');
paulo@103 111 prev2YearDiv.text(this.options.startYear - 2);
paulo@103 112
paulo@103 113 if(this.options.minDate != null && this.options.minDate > new Date(this.options.startYear - 2, 11, 31)) {
paulo@103 114 prev2YearDiv.addClass('disabled');
paulo@103 115 }
paulo@103 116
paulo@103 117 headerTable.append(prev2YearDiv);
paulo@103 118
paulo@103 119 var prevYearDiv = $(document.createElement('th'));
paulo@103 120 prevYearDiv.addClass('year-title year-neighbor hidden-xs');
paulo@103 121 prevYearDiv.text(this.options.startYear - 1);
paulo@103 122
paulo@103 123 if(this.options.minDate != null && this.options.minDate > new Date(this.options.startYear - 1, 11, 31)) {
paulo@103 124 prevYearDiv.addClass('disabled');
paulo@103 125 }
paulo@103 126
paulo@103 127 headerTable.append(prevYearDiv);
paulo@103 128
paulo@103 129 var yearDiv = $(document.createElement('th'));
paulo@103 130 yearDiv.addClass('year-title');
paulo@103 131 yearDiv.text(this.options.startYear);
paulo@103 132
paulo@103 133 headerTable.append(yearDiv);
paulo@103 134
paulo@103 135 var nextYearDiv = $(document.createElement('th'));
paulo@103 136 nextYearDiv.addClass('year-title year-neighbor hidden-xs');
paulo@103 137 nextYearDiv.text(this.options.startYear + 1);
paulo@103 138
paulo@103 139 if(this.options.maxDate != null && this.options.maxDate < new Date(this.options.startYear + 1, 0, 1)) {
paulo@103 140 nextYearDiv.addClass('disabled');
paulo@103 141 }
paulo@103 142
paulo@103 143 headerTable.append(nextYearDiv);
paulo@103 144
paulo@103 145 var next2YearDiv = $(document.createElement('th'));
paulo@103 146 next2YearDiv.addClass('year-title year-neighbor2 hidden-sm hidden-xs');
paulo@103 147 next2YearDiv.text(this.options.startYear + 2);
paulo@103 148
paulo@103 149 if(this.options.maxDate != null && this.options.maxDate < new Date(this.options.startYear + 2, 0, 1)) {
paulo@103 150 next2YearDiv.addClass('disabled');
paulo@103 151 }
paulo@103 152
paulo@103 153 headerTable.append(next2YearDiv);
paulo@103 154
paulo@103 155 var nextDiv = $(document.createElement('th'));
paulo@103 156 nextDiv.addClass('next');
paulo@103 157
paulo@103 158 if(this.options.maxDate != null && this.options.maxDate < new Date(this.options.startYear + 1, 0, 1)) {
paulo@103 159 nextDiv.addClass('disabled');
paulo@103 160 }
paulo@103 161
paulo@103 162 var nextIcon = $(document.createElement('span'));
paulo@103 163 nextIcon.addClass('glyphicon glyphicon-chevron-right');
paulo@103 164
paulo@103 165 nextDiv.append(nextIcon);
paulo@103 166
paulo@103 167 headerTable.append(nextDiv);
paulo@103 168
paulo@103 169 header.append(headerTable);
paulo@103 170
paulo@103 171 this.element.append(header);
paulo@103 172 },
paulo@103 173 _renderBody: function() {
paulo@103 174 var monthsDiv = $(document.createElement('div'));
paulo@103 175 monthsDiv.addClass('months-container');
paulo@103 176
paulo@103 177 for(var m = 0; m < 12; m++) {
paulo@103 178 /* Container */
paulo@103 179 var monthDiv = $(document.createElement('div'));
paulo@103 180 monthDiv.addClass('month-container');
paulo@103 181 monthDiv.data('month-id', m);
paulo@103 182
paulo@103 183 var firstDate = new Date(this.options.startYear, m, 1);
paulo@103 184
paulo@103 185 var table = $(document.createElement('table'));
paulo@103 186 table.addClass('month');
paulo@103 187
paulo@103 188 /* Month header */
paulo@103 189 var thead = $(document.createElement('thead'));
paulo@103 190
paulo@103 191 var titleRow = $(document.createElement('tr'));
paulo@103 192
paulo@103 193 var titleCell = $(document.createElement('th'));
paulo@103 194 titleCell.addClass('month-title');
paulo@103 195 titleCell.attr('colspan', this.options.displayWeekNumber ? 8 : 7);
paulo@103 196 titleCell.text(dates[this.options.language].months[m]);
paulo@103 197
paulo@103 198 titleRow.append(titleCell);
paulo@103 199 thead.append(titleRow);
paulo@103 200
paulo@103 201 var headerRow = $(document.createElement('tr'));
paulo@103 202
paulo@103 203 if(this.options.displayWeekNumber) {
paulo@103 204 var weekNumberCell = $(document.createElement('th'));
paulo@103 205 weekNumberCell.addClass('week-number');
paulo@103 206 weekNumberCell.text(dates[this.options.language].weekShort);
paulo@103 207 headerRow.append(weekNumberCell);
paulo@103 208 }
paulo@103 209
paulo@103 210 var d = dates[this.options.language].weekStart;
paulo@103 211 do
paulo@103 212 {
paulo@103 213 var headerCell = $(document.createElement('th'));
paulo@103 214 headerCell.addClass('day-header');
paulo@103 215 headerCell.text(dates[this.options.language].daysMin[d]);
paulo@103 216
paulo@103 217 headerRow.append(headerCell);
paulo@103 218
paulo@103 219 d++;
paulo@103 220 if(d >= 7)
paulo@103 221 d = 0;
paulo@103 222 }
paulo@103 223 while(d != dates[this.options.language].weekStart)
paulo@103 224
paulo@103 225 thead.append(headerRow);
paulo@103 226 table.append(thead);
paulo@103 227
paulo@103 228 /* Days */
paulo@103 229 var currentDate = new Date(firstDate.getTime());
paulo@103 230 var lastDate = new Date(this.options.startYear, m + 1, 0);
paulo@103 231
paulo@103 232 var weekStart = dates[this.options.language].weekStart
paulo@103 233
paulo@103 234 while(currentDate.getDay() != weekStart)
paulo@103 235 {
paulo@103 236 currentDate.setDate(currentDate.getDate() - 1);
paulo@103 237 }
paulo@103 238
paulo@103 239 while(currentDate <= lastDate)
paulo@103 240 {
paulo@103 241 var row = $(document.createElement('tr'));
paulo@103 242
paulo@103 243 if(this.options.displayWeekNumber) {
paulo@103 244 var weekNumberCell = $(document.createElement('td'));
paulo@103 245 weekNumberCell.addClass('week-number');
paulo@103 246 weekNumberCell.text(this.getWeekNumber(currentDate));
paulo@103 247 row.append(weekNumberCell);
paulo@103 248 }
paulo@103 249
paulo@103 250 do
paulo@103 251 {
paulo@103 252 var cell = $(document.createElement('td'));
paulo@103 253 cell.addClass('day');
paulo@103 254
paulo@103 255 if(currentDate < firstDate) {
paulo@103 256 cell.addClass('old');
paulo@103 257 }
paulo@103 258 else if(currentDate > lastDate) {
paulo@103 259 cell.addClass('new');
paulo@103 260 }
paulo@103 261 else {
paulo@103 262 if((this.options.minDate != null && currentDate < this.options.minDate) || (this.options.maxDate != null && currentDate > this.options.maxDate))
paulo@103 263 {
paulo@103 264 cell.addClass('disabled');
paulo@103 265 }
paulo@103 266 else if(this.options.disabledDays.length > 0) {
paulo@103 267 for(var d in this.options.disabledDays){
paulo@103 268 if(currentDate.getTime() == this.options.disabledDays[d].getTime()) {
paulo@103 269 cell.addClass('disabled');
paulo@103 270 break;
paulo@103 271 }
paulo@103 272 }
paulo@103 273 }
paulo@103 274
paulo@103 275 var cellContent = $(document.createElement('div'));
paulo@103 276 cellContent.addClass('day-content');
paulo@103 277 cellContent.text(currentDate.getDate());
paulo@103 278 cell.append(cellContent);
paulo@103 279
paulo@103 280 if(this.options.customDayRenderer) {
paulo@103 281 this.options.customDayRenderer(cellContent, currentDate);
paulo@103 282 }
paulo@103 283 }
paulo@103 284
paulo@103 285 row.append(cell);
paulo@103 286
paulo@103 287 currentDate.setDate(currentDate.getDate() + 1);
paulo@103 288 }
paulo@103 289 while(currentDate.getDay() != weekStart)
paulo@103 290
paulo@103 291 table.append(row);
paulo@103 292 }
paulo@103 293
paulo@103 294 monthDiv.append(table);
paulo@103 295
paulo@103 296 monthsDiv.append(monthDiv);
paulo@103 297 }
paulo@103 298
paulo@103 299 this.element.append(monthsDiv);
paulo@103 300 },
paulo@103 301 _renderDataSource: function() {
paulo@103 302 var _this = this;
paulo@103 303 if(this.options.dataSource != null && this.options.dataSource.length > 0) {
paulo@103 304 this.element.find('.month-container').each(function() {
paulo@103 305 var month = $(this).data('month-id');
paulo@103 306
paulo@103 307 var firstDate = new Date(_this.options.startYear, month, 1);
paulo@103 308 var lastDate = new Date(_this.options.startYear, month + 1, 0);
paulo@103 309
paulo@103 310 if((_this.options.minDate == null || lastDate >= _this.options.minDate) && (_this.options.maxDate == null || firstDate <= _this.options.maxDate))
paulo@103 311 {
paulo@103 312 var monthData = [];
paulo@103 313
paulo@103 314 for(var i in _this.options.dataSource) {
paulo@103 315 if(!(_this.options.dataSource[i].startDate > lastDate) || (_this.options.dataSource[i].endDate < firstDate)) {
paulo@103 316 monthData.push(_this.options.dataSource[i]);
paulo@103 317 }
paulo@103 318 }
paulo@103 319
paulo@103 320 if(monthData.length > 0) {
paulo@103 321 $(this).find('.day-content').each(function() {
paulo@103 322 var currentDate = new Date(_this.options.startYear, month, $(this).text());
paulo@103 323
paulo@103 324 var dayData = [];
paulo@103 325
paulo@103 326 if((_this.options.minDate == null || currentDate >= _this.options.minDate) && (_this.options.maxDate == null || currentDate <= _this.options.maxDate))
paulo@103 327 {
paulo@103 328 for(var i in monthData) {
paulo@103 329 if(monthData[i].startDate <= currentDate && monthData[i].endDate >= currentDate) {
paulo@103 330 dayData.push(monthData[i]);
paulo@103 331 }
paulo@103 332 }
paulo@103 333
paulo@103 334 if(dayData.length > 0)
paulo@103 335 {
paulo@103 336 _this._renderDataSourceDay($(this), currentDate, dayData);
paulo@103 337 }
paulo@103 338 }
paulo@103 339 });
paulo@103 340 }
paulo@103 341 }
paulo@103 342 });
paulo@103 343 }
paulo@103 344 },
paulo@103 345 _renderDataSourceDay: function(elt, currentDate, events) {
paulo@103 346 switch(this.options.style)
paulo@103 347 {
paulo@103 348 case 'border':
paulo@103 349 var weight = 0;
paulo@103 350
paulo@103 351 if(events.length == 1) {
paulo@103 352 weight = 4;
paulo@103 353 }
paulo@103 354 else if(events.length <= 3) {
paulo@103 355 weight = 2;
paulo@103 356 }
paulo@103 357 else {
paulo@103 358 elt.parent().css('box-shadow', 'inset 0 -4px 0 0 black');
paulo@103 359 }
paulo@103 360
paulo@103 361 if(weight > 0)
paulo@103 362 {
paulo@103 363 var boxShadow = '';
paulo@103 364
paulo@103 365 for(var i in events)
paulo@103 366 {
paulo@103 367 if(boxShadow != '') {
paulo@103 368 boxShadow += ",";
paulo@103 369 }
paulo@103 370
paulo@103 371 boxShadow += 'inset 0 -' + (parseInt(i) + 1) * weight + 'px 0 0 ' + events[i].color;
paulo@103 372 }
paulo@103 373
paulo@103 374 elt.parent().css('box-shadow', boxShadow);
paulo@103 375 }
paulo@103 376 break;
paulo@103 377
paulo@103 378 case 'background':
paulo@103 379 elt.parent().css('background-color', events[events.length - 1].color);
paulo@103 380
paulo@103 381 var currentTime = currentDate.getTime();
paulo@103 382
paulo@103 383 if(events[events.length - 1].startDate.getTime() == currentTime)
paulo@103 384 {
paulo@103 385 elt.parent().addClass('day-start');
paulo@103 386
paulo@103 387 if(events[events.length - 1].startHalfDay || this.options.alwaysHalfDay) {
paulo@103 388 elt.parent().addClass('day-half');
paulo@103 389
paulo@103 390 // Find color for other half
paulo@103 391 var otherColor = 'transparent';
paulo@103 392 for(var i = events.length - 2; i >= 0; i--) {
paulo@103 393 if(events[i].startDate.getTime() != currentTime || (!events[i].startHalfDay && !this.options.alwaysHalfDay)) {
paulo@103 394 otherColor = events[i].color;
paulo@103 395 break;
paulo@103 396 }
paulo@103 397 }
paulo@103 398
paulo@103 399 elt.parent().css('background', 'linear-gradient(-45deg, ' + events[events.length - 1].color + ', ' + events[events.length - 1].color + ' 49%, ' + otherColor + ' 51%, ' + otherColor + ')');
paulo@103 400 }
paulo@103 401 else if(this.options.roundRangeLimits) {
paulo@103 402 elt.parent().addClass('round-left');
paulo@103 403 }
paulo@103 404 }
paulo@103 405 else if(events[events.length - 1].endDate.getTime() == currentTime)
paulo@103 406 {
paulo@103 407 elt.parent().addClass('day-end');
paulo@103 408
paulo@103 409 if(events[events.length - 1].endHalfDay || this.options.alwaysHalfDay) {
paulo@103 410 elt.parent().addClass('day-half');
paulo@103 411
paulo@103 412 // Find color for other half
paulo@103 413 var otherColor = 'transparent';
paulo@103 414 for(var i = events.length - 2; i >= 0; i--) {
paulo@103 415 if(events[i].endDate.getTime() != currentTime || (!events[i].endHalfDay && !this.options.alwaysHalfDay)) {
paulo@103 416 otherColor = events[i].color;
paulo@103 417 break;
paulo@103 418 }
paulo@103 419 }
paulo@103 420
paulo@103 421 elt.parent().css('background', 'linear-gradient(135deg, ' + events[events.length - 1].color + ', ' + events[events.length - 1].color + ' 49%, ' + otherColor + ' 51%, ' + otherColor + ')');
paulo@103 422 }
paulo@103 423 else if(this.options.roundRangeLimits) {
paulo@103 424 elt.parent().addClass('round-right');
paulo@103 425 }
paulo@103 426 }
paulo@103 427 break;
paulo@103 428
paulo@103 429 case 'custom':
paulo@103 430 if(this.options.customDataSourceRenderer) {
paulo@103 431 this.options.customDataSourceRenderer.call(this, elt, currentDate, events);
paulo@103 432 }
paulo@103 433 break;
paulo@103 434 }
paulo@103 435 },
paulo@103 436 _applyEvents: function () {
paulo@103 437 var _this = this;
paulo@103 438
paulo@103 439 /* Header buttons */
paulo@103 440 this.element.find('.year-neighbor, .year-neighbor2').click(function() {
paulo@103 441 if(!$(this).hasClass('disabled')) {
paulo@103 442 _this.setYear(parseInt($(this).text()));
paulo@103 443 }
paulo@103 444 });
paulo@103 445
paulo@103 446 this.element.find('.calendar-header .prev').click(function() {
paulo@103 447 if(!$(this).hasClass('disabled')) {
paulo@103 448 _this.element.find('.months-container').animate({'margin-left':'100%'},100, function() {
paulo@103 449 _this.element.find('.months-container').hide();
paulo@103 450 _this.element.find('.months-container').css('margin-left', '0');
paulo@103 451 setTimeout(function() { _this.setYear(_this.options.startYear - 1) }, 50);
paulo@103 452 });
paulo@103 453 }
paulo@103 454 });
paulo@103 455
paulo@103 456 this.element.find('.calendar-header .next').click(function() {
paulo@103 457 if(!$(this).hasClass('disabled')) {
paulo@103 458 _this.element.find('.months-container').animate({'margin-left':'-100%'},100, function() {
paulo@103 459 _this.element.find('.months-container').hide();
paulo@103 460 _this.element.find('.months-container').css('margin-left', '0');
paulo@103 461 setTimeout(function() { _this.setYear(_this.options.startYear + 1) }, 50);
paulo@103 462 });
paulo@103 463 }
paulo@103 464 });
paulo@103 465
paulo@103 466 var cells = this.element.find('.day:not(.old, .new, .disabled)');
paulo@103 467
paulo@103 468 /* Click on date */
paulo@103 469 cells.click(function(e) {
paulo@103 470 e.stopPropagation();
paulo@103 471 var date = _this._getDate($(this));
paulo@103 472 _this._triggerEvent('clickDay', {
paulo@103 473 element: $(this),
paulo@103 474 which: e.which,
paulo@103 475 date: date,
paulo@103 476 events: _this.getEvents(date)
paulo@103 477 });
paulo@103 478 });
paulo@103 479
paulo@103 480 /* Click right on date */
paulo@103 481
paulo@103 482 cells.bind('contextmenu', function(e) {
paulo@103 483 if(_this.options.enableContextMenu)
paulo@103 484 {
paulo@103 485 e.preventDefault();
paulo@103 486 if(_this.options.contextMenuItems.length > 0)
paulo@103 487 {
paulo@103 488 _this._openContextMenu($(this));
paulo@103 489 }
paulo@103 490 }
paulo@103 491
paulo@103 492 var date = _this._getDate($(this));
paulo@103 493 _this._triggerEvent('dayContextMenu', {
paulo@103 494 element: $(this),
paulo@103 495 date: date,
paulo@103 496 events: _this.getEvents(date)
paulo@103 497 });
paulo@103 498 });
paulo@103 499
paulo@103 500 /* Range selection */
paulo@103 501 if(this.options.enableRangeSelection) {
paulo@103 502 cells.mousedown(function (e) {
paulo@103 503 if(e.which == 1) {
paulo@103 504 var currentDate = _this._getDate($(this));
paulo@103 505
paulo@103 506 if(_this.options.allowOverlap || _this.getEvents(currentDate).length == 0)
paulo@103 507 {
paulo@103 508 _this._mouseDown = true;
paulo@103 509 _this._rangeStart = _this._rangeEnd = currentDate;
paulo@103 510 _this._refreshRange();
paulo@103 511 }
paulo@103 512 }
paulo@103 513 });
paulo@103 514
paulo@103 515 cells.mouseenter(function (e) {
paulo@103 516 if (_this._mouseDown) {
paulo@103 517 var currentDate = _this._getDate($(this));
paulo@103 518
paulo@103 519 if(!_this.options.allowOverlap)
paulo@103 520 {
paulo@103 521 var newDate = new Date(_this._rangeStart.getTime());
paulo@103 522
paulo@103 523 if(newDate < currentDate) {
paulo@103 524 var nextDate = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate() + 1);
paulo@103 525 while(newDate < currentDate) {
paulo@103 526 if(_this.getEvents(nextDate).length > 0)
paulo@103 527 {
paulo@103 528 break;
paulo@103 529 }
paulo@103 530
paulo@103 531 newDate.setDate(newDate.getDate() + 1);
paulo@103 532 nextDate.setDate(nextDate.getDate() + 1);
paulo@103 533 }
paulo@103 534 }
paulo@103 535 else {
paulo@103 536 var nextDate = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate() - 1);
paulo@103 537 while(newDate > currentDate) {
paulo@103 538 if(_this.getEvents(nextDate).length > 0)
paulo@103 539 {
paulo@103 540 break;
paulo@103 541 }
paulo@103 542
paulo@103 543 newDate.setDate(newDate.getDate() - 1);
paulo@103 544 nextDate.setDate(nextDate.getDate() - 1);
paulo@103 545 }
paulo@103 546 }
paulo@103 547
paulo@103 548 currentDate = newDate;
paulo@103 549 }
paulo@103 550
paulo@103 551 var oldValue = _this._rangeEnd;
paulo@103 552 _this._rangeEnd = currentDate;
paulo@103 553
paulo@103 554 if (oldValue.getTime() != _this._rangeEnd.getTime()) {
paulo@103 555 _this._refreshRange();
paulo@103 556 }
paulo@103 557 }
paulo@103 558 });
paulo@103 559
paulo@103 560 $(window).mouseup(function (e) {
paulo@103 561 if (_this._mouseDown) {
paulo@103 562 _this._mouseDown = false;
paulo@103 563 _this._refreshRange();
paulo@103 564
paulo@103 565 var minDate = _this._rangeStart < _this._rangeEnd ? _this._rangeStart : _this._rangeEnd;
paulo@103 566 var maxDate = _this._rangeEnd > _this._rangeStart ? _this._rangeEnd : _this._rangeStart;
paulo@103 567
paulo@103 568 _this._triggerEvent('selectRange', { startDate: minDate, endDate: maxDate });
paulo@103 569 }
paulo@103 570 });
paulo@103 571 }
paulo@103 572
paulo@103 573 /* Hover date */
paulo@103 574 cells.mouseenter(function(e) {
paulo@103 575 if(!_this._mouseDown)
paulo@103 576 {
paulo@103 577 var date = _this._getDate($(this));
paulo@103 578 _this._triggerEvent('mouseOnDay', {
paulo@103 579 element: $(this),
paulo@103 580 date: date,
paulo@103 581 events: _this.getEvents(date)
paulo@103 582 });
paulo@103 583 }
paulo@103 584 });
paulo@103 585
paulo@103 586 cells.mouseleave(function(e) {
paulo@103 587 var date = _this._getDate($(this));
paulo@103 588 _this._triggerEvent('mouseOutDay', {
paulo@103 589 element: $(this),
paulo@103 590 date: date,
paulo@103 591 events: _this.getEvents(date)
paulo@103 592 });
paulo@103 593 });
paulo@103 594
paulo@103 595 /* Responsive management */
paulo@103 596
paulo@103 597 setInterval(function() {
paulo@103 598 var calendarSize = $(_this.element).width();
paulo@103 599 var monthSize = $(_this.element).find('.month').first().width() + 10;
paulo@103 600 var monthContainerClass = 'month-container';
paulo@103 601
paulo@103 602 if(monthSize * 6 < calendarSize) {
paulo@103 603 monthContainerClass += ' col-xs-2';
paulo@103 604 }
paulo@103 605 else if(monthSize * 4 < calendarSize) {
paulo@103 606 monthContainerClass += ' col-xs-3';
paulo@103 607 }
paulo@103 608 else if(monthSize * 3 < calendarSize) {
paulo@103 609 monthContainerClass += ' col-xs-4';
paulo@103 610 }
paulo@103 611 else if(monthSize * 2 < calendarSize) {
paulo@103 612 monthContainerClass += ' col-xs-6';
paulo@103 613 }
paulo@103 614 else {
paulo@103 615 monthContainerClass += ' col-xs-12';
paulo@103 616 }
paulo@103 617
paulo@103 618 $(_this.element).find('.month-container').attr('class', monthContainerClass);
paulo@103 619 }, 300);
paulo@103 620 },
paulo@103 621 _refreshRange: function () {
paulo@103 622 var _this = this;
paulo@103 623
paulo@103 624 this.element.find('td.day.range').removeClass('range')
paulo@103 625 this.element.find('td.day.range-start').removeClass('range-start');
paulo@103 626 this.element.find('td.day.range-end').removeClass('range-end');
paulo@103 627
paulo@103 628 if (this._mouseDown) {
paulo@103 629 var beforeRange = true;
paulo@103 630 var afterRange = false;
paulo@103 631 var minDate = _this._rangeStart < _this._rangeEnd ? _this._rangeStart : _this._rangeEnd;
paulo@103 632 var maxDate = _this._rangeEnd > _this._rangeStart ? _this._rangeEnd : _this._rangeStart;
paulo@103 633
paulo@103 634 this.element.find('.month-container').each(function () {
paulo@103 635 var monthId = $(this).data('month-id');
paulo@103 636 if (minDate.getMonth() <= monthId && maxDate.getMonth() >= monthId) {
paulo@103 637 $(this).find('td.day:not(.old, .new)').each(function () {
paulo@103 638 var date = _this._getDate($(this));
paulo@103 639 if (date >= minDate && date <= maxDate) {
paulo@103 640 $(this).addClass('range');
paulo@103 641
paulo@103 642 if (date.getTime() == minDate.getTime()) {
paulo@103 643 $(this).addClass('range-start');
paulo@103 644 }
paulo@103 645
paulo@103 646 if (date.getTime() == maxDate.getTime()) {
paulo@103 647 $(this).addClass('range-end');
paulo@103 648 }
paulo@103 649 }
paulo@103 650 });
paulo@103 651 }
paulo@103 652 });
paulo@103 653 }
paulo@103 654 },
paulo@103 655 _openContextMenu: function(elt) {
paulo@103 656 var contextMenu = $('.calendar-context-menu');
paulo@103 657
paulo@103 658 if(contextMenu.length > 0) {
paulo@103 659 contextMenu.hide();
paulo@103 660 contextMenu.empty();
paulo@103 661 }
paulo@103 662 else {
paulo@103 663 contextMenu = $(document.createElement('div'));
paulo@103 664 contextMenu.addClass('calendar-context-menu');
paulo@103 665 $('body').append(contextMenu);
paulo@103 666 }
paulo@103 667
paulo@103 668 var date = this._getDate(elt);
paulo@103 669 var events = this.getEvents(date);
paulo@103 670
paulo@103 671 for(var i in events) {
paulo@103 672 var eventItem = $(document.createElement('div'));
paulo@103 673 eventItem.addClass('item');
paulo@103 674 eventItem.css('border-left', '4px solid ' + events[i].color);
paulo@103 675
paulo@103 676 var eventItemContent = $(document.createElement('div'));
paulo@103 677 eventItemContent.addClass('content');
paulo@103 678 eventItemContent.text(events[i].name);
paulo@103 679
paulo@103 680 eventItem.append(eventItemContent);
paulo@103 681
paulo@103 682 var icon = $(document.createElement('span'));
paulo@103 683 icon.addClass('glyphicon glyphicon-chevron-right');
paulo@103 684
paulo@103 685 eventItem.append(icon);
paulo@103 686
paulo@103 687 this._renderContextMenuItems(eventItem, this.options.contextMenuItems, events[i]);
paulo@103 688
paulo@103 689 contextMenu.append(eventItem);
paulo@103 690 }
paulo@103 691
paulo@103 692 if(contextMenu.children().length > 0)
paulo@103 693 {
paulo@103 694 contextMenu.css('left', elt.offset().left + 25 + 'px');
paulo@103 695 contextMenu.css('top', elt.offset().top + 25 + 'px');
paulo@103 696 contextMenu.show();
paulo@103 697
paulo@103 698 $(window).one('mouseup', function() {
paulo@103 699 contextMenu.hide();
paulo@103 700 });
paulo@103 701 }
paulo@103 702 },
paulo@103 703 _renderContextMenuItems: function(parent, items, evt) {
paulo@103 704 var subMenu = $(document.createElement('div'));
paulo@103 705 subMenu.addClass('submenu');
paulo@103 706
paulo@103 707 for(var i in items) {
paulo@103 708 if(!items[i].visible || items[i].visible(evt)) {
paulo@103 709 var menuItem = $(document.createElement('div'));
paulo@103 710 menuItem.addClass('item');
paulo@103 711
paulo@103 712 var menuItemContent = $(document.createElement('div'));
paulo@103 713 menuItemContent.addClass('content');
paulo@103 714 menuItemContent.text(items[i].text);
paulo@103 715
paulo@103 716 menuItem.append(menuItemContent);
paulo@103 717
paulo@103 718 if(items[i].click) {
paulo@103 719 (function(index) {
paulo@103 720 menuItem.click(function() {
paulo@103 721 items[index].click(evt);
paulo@103 722 });
paulo@103 723 })(i);
paulo@103 724 }
paulo@103 725
paulo@103 726 var icon = $(document.createElement('span'));
paulo@103 727 icon.addClass('glyphicon glyphicon-chevron-right');
paulo@103 728
paulo@103 729 menuItem.append(icon);
paulo@103 730
paulo@103 731 if(items[i].items && items[i].items.length > 0) {
paulo@103 732 this._renderContextMenuItems(menuItem, items[i].items, evt);
paulo@103 733 }
paulo@103 734
paulo@103 735 subMenu.append(menuItem);
paulo@103 736 }
paulo@103 737 }
paulo@103 738
paulo@103 739 if(subMenu.children().length > 0)
paulo@103 740 {
paulo@103 741 parent.append(subMenu);
paulo@103 742 }
paulo@103 743 },
paulo@103 744 _getColor: function(colorString) {
paulo@103 745 var div = $('<div />');
paulo@103 746 div.css('color', colorString);
paulo@103 747
paulo@103 748 },
paulo@103 749 _getDate: function(elt) {
paulo@103 750 var day = elt.children('.day-content').text();
paulo@103 751 var month = elt.closest('.month-container').data('month-id');
paulo@103 752 var year = this.options.startYear;
paulo@103 753
paulo@103 754 return new Date(year, month, day);
paulo@103 755 },
paulo@103 756 _triggerEvent: function(eventName, parameters) {
paulo@103 757 var event = $.Event(eventName);
paulo@103 758
paulo@103 759 for(var i in parameters) {
paulo@103 760 event[i] = parameters[i];
paulo@103 761 }
paulo@103 762
paulo@103 763 this.element.trigger(event);
paulo@103 764 },
paulo@103 765 getWeekNumber: function(date) {
paulo@103 766 var tempDate = new Date(date.getTime());
paulo@103 767 tempDate.setHours(0, 0, 0, 0);
paulo@103 768 tempDate.setDate(tempDate.getDate() + 3 - (tempDate.getDay() + 6) % 7);
paulo@103 769 var week1 = new Date(tempDate.getFullYear(), 0, 4);
paulo@103 770 return 1 + Math.round(((tempDate.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
paulo@103 771 },
paulo@103 772 getEvents: function(date) {
paulo@103 773 var events = [];
paulo@103 774
paulo@103 775 if(this.options.dataSource && date) {
paulo@103 776 for(var i in this.options.dataSource) {
paulo@103 777 if(this.options.dataSource[i].startDate <= date && this.options.dataSource[i].endDate >= date) {
paulo@103 778 events.push(this.options.dataSource[i]);
paulo@103 779 }
paulo@103 780 }
paulo@103 781 }
paulo@103 782
paulo@103 783 return events;
paulo@103 784 },
paulo@103 785 getYear: function() {
paulo@103 786 return this.options.startYear;
paulo@103 787 },
paulo@103 788 setYear: function(year) {
paulo@103 789 var parsedYear = parseInt(year);
paulo@103 790 if(!isNaN(parsedYear)) {
paulo@103 791 this.options.startYear = parsedYear;
paulo@103 792 this._render();
paulo@103 793 }
paulo@103 794 },
paulo@103 795 getMinDate: function() {
paulo@103 796 return this.options.minDate;
paulo@103 797 },
paulo@103 798 setMinDate: function(date) {
paulo@103 799 if(date instanceof Date) {
paulo@103 800 this.options.minDate = date;
paulo@103 801 this._render();
paulo@103 802 }
paulo@103 803 },
paulo@103 804 getMaxDate: function() {
paulo@103 805 return this.options.maxDate;
paulo@103 806 },
paulo@103 807 setMaxDate: function(date) {
paulo@103 808 if(date instanceof Date) {
paulo@103 809 this.options.maxDate = date;
paulo@103 810 this._render();
paulo@103 811 }
paulo@103 812 },
paulo@103 813 getStyle: function() {
paulo@103 814 return this.options.style;
paulo@103 815 },
paulo@103 816 setStyle: function(style) {
paulo@103 817 this.options.style = style == 'background' || style == 'border' || style == 'custom' ? style : 'border';
paulo@103 818 this._render();
paulo@103 819 },
paulo@103 820 getAllowOverlap: function() {
paulo@103 821 return this.options.allowOverlap;
paulo@103 822 },
paulo@103 823 setAllowOverlap: function(allowOverlap) {
paulo@103 824 this.options.allowOverlap = allowOverlap;
paulo@103 825 },
paulo@103 826 getDisplayWeekNumber: function() {
paulo@103 827 return this.options.displayWeekNumber;
paulo@103 828 },
paulo@103 829 setDisplayWeekNumber: function(displayWeekNumber) {
paulo@103 830 this.options.displayWeekNumber = displayWeekNumber;
paulo@103 831 this._render();
paulo@103 832 },
paulo@103 833 getAlwaysHalfDay: function() {
paulo@103 834 return this.options.alwaysHalfDay;
paulo@103 835 },
paulo@103 836 setAlwaysHalfDay: function(alwaysHalfDay) {
paulo@103 837 this.options.alwaysHalfDay = alwaysHalfDay;
paulo@103 838 this._render();
paulo@103 839 },
paulo@103 840 getEnableRangeSelection: function() {
paulo@103 841 return this.options.enableRangeSelection;
paulo@103 842 },
paulo@103 843 setEnableRangeSelection: function(enableRangeSelection) {
paulo@103 844 this.options.enableRangeSelection = enableRangeSelection;
paulo@103 845 this._render();
paulo@103 846 },
paulo@103 847 getDisabledDays: function() {
paulo@103 848 return this.options.disabledDays;
paulo@103 849 },
paulo@103 850 setDisabledDays: function(disabledDays) {
paulo@103 851 this.options.disabledDays = disabledDays instanceof Array ? disabledDays : [];
paulo@103 852 this._render();
paulo@103 853 },
paulo@103 854 getRoundRangeLimits: function() {
paulo@103 855 return this.options.roundRangeLimits;
paulo@103 856 },
paulo@103 857 setRoundRangeLimits: function(roundRangeLimits) {
paulo@103 858 this.options.roundRangeLimits = roundRangeLimits;
paulo@103 859 this._render();
paulo@103 860 },
paulo@103 861 getEnableContextMenu: function() {
paulo@103 862 return this.options.enableContextMenu;
paulo@103 863 },
paulo@103 864 setEnableContextMenu: function(enableContextMenu) {
paulo@103 865 this.options.enableContextMenu = enableContextMenu;
paulo@103 866 this._render();
paulo@103 867 },
paulo@103 868 getContextMenuItems: function() {
paulo@103 869 return this.options.contextMenuItems;
paulo@103 870 },
paulo@103 871 setContextMenuItems: function(contextMenuItems) {
paulo@103 872 this.options.contextMenuItems = contextMenuItems instanceof Array ? contextMenuItems : [];
paulo@103 873 this._render();
paulo@103 874 },
paulo@103 875 getCustomDayRenderer: function() {
paulo@103 876 return this.options.customDayRenderer;
paulo@103 877 },
paulo@103 878 setCustomDayRenderer: function(customDayRenderer) {
paulo@103 879 this.options.customDayRenderer = $.isFunction(customDayRenderer) ? customDayRenderer : null;
paulo@103 880 this._render();
paulo@103 881 },
paulo@103 882 getCustomDataSourceRenderer: function() {
paulo@103 883 return this.options.customDataSourceRenderer;
paulo@103 884 },
paulo@103 885 setCustomDataSourceRenderer: function(customDataSourceRenderer) {
paulo@103 886 this.options.customDataSourceRenderer = $.isFunction(customDataSourceRenderer) ? customDataSourceRenderer : null;
paulo@103 887 this._render();
paulo@103 888 },
paulo@103 889 getLanguage: function() {
paulo@103 890 return this.options.language;
paulo@103 891 },
paulo@103 892 setLanguage: function(language) {
paulo@103 893 if(language != null && dates[language] != null) {
paulo@103 894 this.options.language = language;
paulo@103 895 this._render();
paulo@103 896 }
paulo@103 897 },
paulo@103 898 getDataSource: function() {
paulo@103 899 return this.options.dataSource;
paulo@103 900 },
paulo@103 901 setDataSource: function(dataSource) {
paulo@103 902 this.options.dataSource = dataSource instanceof Array ? dataSource : [];
paulo@103 903 this._initializeDatasourceColors();
paulo@103 904 this._render();
paulo@103 905 },
paulo@103 906 addEvent: function(evt) {
paulo@103 907 this.options.dataSource.push(evt);
paulo@103 908 this._render();
paulo@103 909 }
paulo@103 910 }
paulo@103 911
paulo@103 912 $.fn.calendar = function (options) {
paulo@103 913 var calendar = new Calendar($(this) ,options);
paulo@103 914 $(this).data('calendar', calendar);
paulo@103 915 return calendar;
paulo@103 916 }
paulo@103 917
paulo@103 918 /* Events binding management */
paulo@103 919 $.fn.renderEnd = function(fct) { $(this).bind('renderEnd', fct); }
paulo@103 920 $.fn.clickDay = function(fct) { $(this).bind('clickDay', fct); }
paulo@103 921 $.fn.dayContextMenu = function(fct) { $(this).bind('dayContextMenu', fct); }
paulo@103 922 $.fn.selectRange = function(fct) { $(this).bind('selectRange', fct); }
paulo@103 923 $.fn.mouseOnDay = function(fct) { $(this).bind('mouseOnDay', fct); }
paulo@103 924 $.fn.mouseOutDay = function(fct) { $(this).bind('mouseOutDay', fct); }
paulo@103 925
paulo@103 926 var dates = $.fn.calendar.dates = {
paulo@103 927 en: {
paulo@103 928 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
paulo@103 929 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
paulo@103 930 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
paulo@103 931 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
paulo@103 932 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
paulo@103 933 weekShort: 'W',
paulo@103 934 weekStart:0
paulo@103 935 }
paulo@103 936 };
paulo@103 937
paulo@103 938 var colors = $.fn.calendar.colors = ['#2C8FC9', '#9CB703', '#F5BB00', '#FF4A32', '#B56CE2', '#45A597'];
paulo@103 939
paulo@103 940 $(function(){
paulo@103 941 $('[data-provide="calendar"]').each(function() {
paulo@103 942 $(this).calendar();
paulo@103 943 });
paulo@103 944 });
paulo@103 945 }(window.jQuery));