From 0df8157b01317fbb7afbebbd6e12e1902dcd7924 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 18 Apr 2013 08:11:11 -0700 Subject: [PATCH] Upgraded Full Calendar, now things are left aligned --- UI/Calendar/CalendarCollectionView.js | 2 +- UI/Content/fullcalendar.css | 907 +++++++++++++------------- UI/JsLibraries/fullcalendar.js | 637 +++++++++++------- 3 files changed, 841 insertions(+), 705 deletions(-) diff --git a/UI/Calendar/CalendarCollectionView.js b/UI/Calendar/CalendarCollectionView.js index fb1b89142..0e34dacf5 100644 --- a/UI/Calendar/CalendarCollectionView.js +++ b/UI/Calendar/CalendarCollectionView.js @@ -15,7 +15,7 @@ define(['app', 'Calendar/CalendarItemView'], function () { this.calendar = new NzbDrone.Calendar.CalendarCollection(); }, onCompositeCollectionRendered: function () { - $(this.ui.calendar).fullCalendar({ + $(this.ui.calendar).empty().fullCalendar({ allDayDefault : false, ignoreTimezone: false, weekMode : 'variable', diff --git a/UI/Content/fullcalendar.css b/UI/Content/fullcalendar.css index 41171e961..acae725d2 100644 --- a/UI/Content/fullcalendar.css +++ b/UI/Content/fullcalendar.css @@ -1,600 +1,579 @@ -/* - * FullCalendar v1.5.4 Stylesheet - * - * Copyright (c) 2011 Adam Shaw - * Dual licensed under the MIT and GPL licenses, located in - * MIT-LICENSE.txt and GPL-LICENSE.txt respectively. - * - * Date: Tue Sep 4 23:38:33 2012 -0700 - * +/*! + * FullCalendar v1.6.1 Stylesheet + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw */ -.fc { - direction: ltr; - text-align: left; -} +.fc { + direction: ltr; + text-align: left; + } + .fc table { - border-collapse: collapse; - border-spacing: 0; -} - + border-collapse: collapse; + border-spacing: 0; + } + html .fc, .fc table { - font-size: 1em; -} - + font-size: 1em; + } + .fc td, .fc th { - padding: 0; - vertical-align: top; -} + padding: 0; + vertical-align: top; + } + + /* Header ------------------------------------------------------------------------*/ .fc-header td { - white-space: nowrap; -} + white-space: nowrap; + } .fc-header-left { - width: 25%; - text-align: left; -} - + width: 25%; + text-align: left; + } + .fc-header-center { - text-align: center; -} - + text-align: center; + } + .fc-header-right { - width: 25%; - text-align: right; -} - + width: 25%; + text-align: right; + } + .fc-header-title { - display: inline-block; - vertical-align: top; -} - + display: inline-block; + vertical-align: top; + } + .fc-header-title h2 { - margin-top: 0; - white-space: nowrap; -} - + margin-top: 0; + white-space: nowrap; + } + .fc .fc-header-space { - padding-left: 10px; -} - + padding-left: 10px; + } + .fc-header .fc-button { - margin-bottom: 1em; - vertical-align: top; -} - + margin-bottom: 1em; + vertical-align: top; + } + /* buttons edges butting together */ .fc-header .fc-button { - margin-right: -1px; -} - -.fc-header .fc-corner-right { - margin-right: 1px; /* back to normal */ -} - -.fc-header .ui-corner-right { - margin-right: 0; /* back to normal */ -} - + margin-right: -1px; + } + +.fc-header .fc-corner-right, /* non-theme */ +.fc-header .ui-corner-right { /* theme */ + margin-right: 0; /* back to normal */ + } + /* button layering (for border precedence) */ - + .fc-header .fc-state-hover, .fc-header .ui-state-hover { - z-index: 2; -} - + z-index: 2; + } + .fc-header .fc-state-down { - z-index: 3; -} + z-index: 3; + } .fc-header .fc-state-active, .fc-header .ui-state-active { - z-index: 4; -} - + z-index: 4; + } + + + /* Content ------------------------------------------------------------------------*/ - + .fc-content { - clear: both; -} - + clear: both; + } + .fc-view { - width: 100%; /* needed for view switching (when view is absolute) */ - /*overflow: hidden;*/ -} + width: 100%; /* needed for view switching (when view is absolute) */ + overflow: hidden; + } + + /* Cell Styles ------------------------------------------------------------------------*/ -.fc-widget-header, /* , usually */ -.fc-widget-content { - /* , usually */ - border: 1px solid #cccccc; -} - -.fc-state-highlight { - /* today cell */ - /* TODO: add .fc-today to */ - background: #ffffcc; -} - -.fc-cell-overlay { - /* semi-transparent rectangle while dragging */ - background: #99ccff; - opacity: .2; - filter: alpha(opacity=20); /* for IE */ -} +.fc-widget-header, /* , usually */ +.fc-widget-content { /* , usually */ + border: 1px solid #ddd; + } + +.fc-state-highlight { /* today cell */ /* TODO: add .fc-today to */ + background: #fcf8e3; + } + +.fc-cell-overlay { /* semi-transparent rectangle while dragging */ + background: #bce8f1; + opacity: .3; + filter: alpha(opacity=30); /* for IE */ + } + + /* Buttons ------------------------------------------------------------------------*/ .fc-button { - position: relative; - display: inline-block; - cursor: pointer; -} - -.fc-state-default { - /* non-theme */ - border-style: solid; - border-width: 1px 0; -} - -.fc-button-inner { - position: relative; - float: left; - overflow: hidden; -} - -.fc-state-default .fc-button-inner { - /* non-theme */ - border-style: solid; - border-width: 0 1px; -} - -.fc-button-content { - position: relative; - float: left; - height: 1.9em; - line-height: 1.9em; - padding: 0 .6em; - white-space: nowrap; -} - + position: relative; + display: inline-block; + padding: 0 .6em; + overflow: hidden; + height: 1.9em; + line-height: 1.9em; + white-space: nowrap; + cursor: pointer; + } + +.fc-state-default { /* non-theme */ + border: 1px solid; + } + +.fc-state-default.fc-corner-left { /* non-theme */ + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + } + +.fc-state-default.fc-corner-right { /* non-theme */ + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + } + +/* + Our default prev/next buttons use HTML entities like ‹ › « » + and we'll try to make them look good cross-browser. +*/ + +.fc-text-arrow { + margin: 0 .1em; + font-size: 2em; + font-family: "Courier New", Courier, monospace; + vertical-align: baseline; /* for IE7 */ + } + +.fc-button-prev .fc-text-arrow, +.fc-button-next .fc-text-arrow { /* for ‹ › */ + font-weight: bold; + } + /* icon (for jquery ui) */ + +.fc-button .fc-icon-wrap { + position: relative; + float: left; + top: 50%; + } + +.fc-button .ui-icon { + position: relative; + float: left; + margin-top: -50%; + *margin-top: 0; + *top: -50%; + } + +/* + button states + borrowed from twitter bootstrap (http://twitter.github.com/bootstrap/) +*/ -.fc-button-content .fc-icon-wrap { - position: relative; - float: left; - top: 50%; -} - -.fc-button-content .ui-icon { - position: relative; - float: left; - margin-top: -50%; - *margin-top: 0; - *top: -50%; -} - -/* gloss effect */ - -.fc-state-default .fc-button-effect { - position: absolute; - top: 50%; - left: 0; -} - -.fc-state-default .fc-button-effect span { - position: absolute; - top: -100px; - left: 0; - width: 500px; - height: 100px; - border-width: 100px 0 0 1px; - border-style: solid; - border-color: #ffffff; - background: #444444; - opacity: .09; - filter: alpha(opacity=9); -} - -/* button states (determines colors) */ - -.fc-state-default, -.fc-state-default .fc-button-inner { - border-style: solid; - border-color: #cccccc #bbbbbb #aaaaaa; - background: #f3f3f3; - color: #000000; -} +.fc-state-default { + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + color: #333; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + } .fc-state-hover, -.fc-state-hover .fc-button-inner { - border-color: #999999; -} - .fc-state-down, -.fc-state-down .fc-button-inner { - border-color: #555555; - background: #777777; -} - .fc-state-active, -.fc-state-active .fc-button-inner { - border-color: #555555; - background: #777777; - color: #ffffff; -} - -.fc-state-disabled, -.fc-state-disabled .fc-button-inner { - color: #999999; - border-color: #dddddd; -} +.fc-state-disabled { + color: #333333; + background-color: #e6e6e6; + } + +.fc-state-hover { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; + } + +.fc-state-down, +.fc-state-active { + background-color: #cccccc; + background-image: none; + outline: 0; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + } .fc-state-disabled { - cursor: default; -} + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + box-shadow: none; + } -.fc-state-disabled .fc-button-effect { - display: none; -} + /* Global Event Styles ------------------------------------------------------------------------*/ - + .fc-event { - border-style: solid; - border-width: 0; - font-size: .85em; - cursor: default; -} + border: 1px solid #3a87ad; /* default BORDER color */ + background-color: #3a87ad; /* default BACKGROUND color */ + color: #fff; /* default TEXT color */ + font-size: .85em; + cursor: default; + } +a.fc-event { + text-decoration: none; + } + a.fc-event, .fc-event-draggable { - cursor: pointer; -} - -a.fc-event { - text-decoration: none; -} - + cursor: pointer; + } + .fc-rtl .fc-event { - text-align: right; -} - -.fc-event-skin { - border-color: #3366cc; /* default BORDER color */ - background-color: #3366cc; /* default BACKGROUND color */ - color: #ffffff; /* default TEXT color */ -} + text-align: right; + } .fc-event-inner { - position: relative; - width: 100%; - height: 100%; - border-style: solid; - border-width: 0; - overflow: hidden; -} - + width: 100%; + height: 100%; + overflow: hidden; + } + .fc-event-time, .fc-event-title { - padding: 0 1px; -} - + padding: 0 1px; + } + .fc .ui-resizable-handle { - /*** TODO: don't use ui-resizable anymore, change class ***/ - display: block; - position: absolute; - z-index: 99999; - overflow: hidden; /* hacky spaces (IE6/7) */ - font-size: 300%; /* */ - line-height: 50%; /* */ -} - + display: block; + position: absolute; + z-index: 99999; + overflow: hidden; /* hacky spaces (IE6/7) */ + font-size: 300%; /* */ + line-height: 50%; /* */ + } + + + /* Horizontal Events ------------------------------------------------------------------------*/ .fc-event-hori { - border-width: 1px 0; - margin-bottom: 1px; -} - + border-width: 1px 0; + margin-bottom: 1px; + } + +.fc-ltr .fc-event-hori.fc-event-start, +.fc-rtl .fc-event-hori.fc-event-end { + border-left-width: 1px; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + } + +.fc-ltr .fc-event-hori.fc-event-end, +.fc-rtl .fc-event-hori.fc-event-start { + border-right-width: 1px; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } + /* resizable */ - + .fc-event-hori .ui-resizable-e { - top: 0 !important; /* importants override pre jquery ui 1.7 styles */ - right: -3px !important; - width: 7px !important; - height: 100% !important; - cursor: e-resize; -} - + top: 0 !important; /* importants override pre jquery ui 1.7 styles */ + right: -3px !important; + width: 7px !important; + height: 100% !important; + cursor: e-resize; + } + .fc-event-hori .ui-resizable-w { - top: 0 !important; - left: -3px !important; - width: 7px !important; - height: 100% !important; - cursor: w-resize; -} - + top: 0 !important; + left: -3px !important; + width: 7px !important; + height: 100% !important; + cursor: w-resize; + } + .fc-event-hori .ui-resizable-handle { - _padding-bottom: 14px; /* IE6 had 0 height */ -} - -/* Fake Rounded Corners (for buttons and events) -------------------------------------------------------------*/ - -.fc-corner-left { - margin-left: 1px; -} - -.fc-corner-left .fc-button-inner, -.fc-corner-left .fc-event-inner { - margin-left: -1px; -} - -.fc-corner-right { - margin-right: 1px; -} - -.fc-corner-right .fc-button-inner, -.fc-corner-right .fc-event-inner { - margin-right: -1px; -} - -.fc-corner-top { - margin-top: 1px; -} - -.fc-corner-top .fc-event-inner { - margin-top: -1px; -} - -.fc-corner-bottom { - margin-bottom: 1px; -} - -.fc-corner-bottom .fc-event-inner { - margin-bottom: -1px; -} - -/* Fake Rounded Corners SPECIFICALLY FOR EVENTS ------------------------------------------------------------------*/ - -.fc-corner-left .fc-event-inner { - border-left-width: 1px; -} - -.fc-corner-right .fc-event-inner { - border-right-width: 1px; -} - -.fc-corner-top .fc-event-inner { - border-top-width: 1px; -} - -.fc-corner-bottom .fc-event-inner { - border-bottom-width: 1px; -} - + _padding-bottom: 14px; /* IE6 had 0 height */ + } + + + /* Reusable Separate-border Table ------------------------------------------------------------*/ table.fc-border-separate { - border-collapse: separate; -} - + border-collapse: separate; + } + .fc-border-separate th, .fc-border-separate td { - border-width: 1px 0 0 1px; -} - + border-width: 1px 0 0 1px; + } + .fc-border-separate th.fc-last, .fc-border-separate td.fc-last { - border-right-width: 1px; -} - + border-right-width: 1px; + } + .fc-border-separate tr.fc-last th, .fc-border-separate tr.fc-last td { - border-bottom-width: 1px; -} - + border-bottom-width: 1px; + } + .fc-border-separate tbody tr.fc-first td, .fc-border-separate tbody tr.fc-first th { - border-top-width: 0; -} + border-top-width: 0; + } + + /* Month View, Basic Week View, Basic Day View ------------------------------------------------------------------------*/ .fc-grid th { - text-align: center; -} - + text-align: center; + } + +.fc .fc-week-number { + width: 22px; + text-align: center; + } + +.fc .fc-week-number div { + padding: 0 2px; + } + .fc-grid .fc-day-number { - float: right; - padding: 0 2px; -} - + float: right; + padding: 0 2px; + } + .fc-grid .fc-other-month .fc-day-number { - opacity: 0.3; - filter: alpha(opacity=30); /* for IE */ - /* opacity with small font can sometimes look too faded - might want to set the 'color' property instead - making day-numbers bold also fixes the problem */ -} - + opacity: 0.3; + filter: alpha(opacity=30); /* for IE */ + /* opacity with small font can sometimes look too faded + might want to set the 'color' property instead + making day-numbers bold also fixes the problem */ + } + .fc-grid .fc-day-content { - clear: both; - padding: 2px 2px 1px; /* distance between events and day edges */ -} - + clear: both; + padding: 2px 2px 1px; /* distance between events and day edges */ + } + /* event styles */ - + .fc-grid .fc-event-time { - font-weight: bold; -} - + font-weight: bold; + } + /* right-to-left */ - + .fc-rtl .fc-grid .fc-day-number { - float: left; -} - + float: left; + } + .fc-rtl .fc-grid .fc-event-time { - float: right; -} + float: right; + } + + /* Agenda Week View, Agenda Day View ------------------------------------------------------------------------*/ .fc-agenda table { - border-collapse: separate; -} - + border-collapse: separate; + } + .fc-agenda-days th { - text-align: center; -} - + text-align: center; + } + .fc-agenda .fc-agenda-axis { - width: 50px; - padding: 0 4px; - vertical-align: middle; - text-align: right; - white-space: nowrap; - font-weight: normal; -} - + width: 50px; + padding: 0 4px; + vertical-align: middle; + text-align: right; + white-space: nowrap; + font-weight: normal; + } + +.fc-agenda .fc-week-number { + font-weight: bold; + } + .fc-agenda .fc-day-content { - padding: 2px 2px 1px; -} - + padding: 2px 2px 1px; + } + /* make axis border take precedence */ - + .fc-agenda-days .fc-agenda-axis { - border-right-width: 1px; -} - + border-right-width: 1px; + } + .fc-agenda-days .fc-col0 { - border-left-width: 0; -} - + border-left-width: 0; + } + /* all-day area */ - + .fc-agenda-allday th { - border-width: 0 1px; -} - + border-width: 0 1px; + } + .fc-agenda-allday .fc-day-content { - min-height: 34px; /* TODO: doesnt work well in quirksmode */ - _height: 34px; -} - + min-height: 34px; /* TODO: doesnt work well in quirksmode */ + _height: 34px; + } + /* divider (between all-day and slots) */ - + .fc-agenda-divider-inner { - height: 2px; - overflow: hidden; -} - + height: 2px; + overflow: hidden; + } + .fc-widget-header .fc-agenda-divider-inner { - background: #eeeeee; -} - + background: #eee; + } + /* slot rows */ - + .fc-agenda-slots th { - border-width: 1px 1px 0; -} - + border-width: 1px 1px 0; + } + .fc-agenda-slots td { - border-width: 1px 0 0; - background: none; -} - + border-width: 1px 0 0; + background: none; + } + .fc-agenda-slots td div { - height: 20px; -} - + height: 20px; + } + .fc-agenda-slots tr.fc-slot0 th, .fc-agenda-slots tr.fc-slot0 td { - border-top-width: 0; -} + border-top-width: 0; + } .fc-agenda-slots tr.fc-minor th, .fc-agenda-slots tr.fc-minor td { - border-top-style: dotted; -} - + border-top-style: dotted; + } + .fc-agenda-slots tr.fc-minor th.ui-widget-header { - *border-top-style: solid; /* doesn't work with background in IE6/7 */ -} + *border-top-style: solid; /* doesn't work with background in IE6/7 */ + } + + /* Vertical Events ------------------------------------------------------------------------*/ .fc-event-vert { - border-width: 0 1px; -} - -.fc-event-vert .fc-event-head, -.fc-event-vert .fc-event-content { - position: relative; - z-index: 2; - width: 100%; - overflow: hidden; -} - + border-width: 0 1px; + } + +.fc-event-vert.fc-event-start { + border-top-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + +.fc-event-vert.fc-event-end { + border-bottom-width: 1px; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } + .fc-event-vert .fc-event-time { - white-space: nowrap; - font-size: 10px; -} - -.fc-event-vert .fc-event-bg { - /* makes the event lighter w/ a semi-transparent overlay */ - position: absolute; - z-index: 1; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: #ffffff; - opacity: .3; - filter: alpha(opacity=30); -} - + white-space: nowrap; + font-size: 10px; + } + +.fc-event-vert .fc-event-inner { + position: relative; + z-index: 2; + } + +.fc-event-vert .fc-event-bg { /* makes the event lighter w/ a semi-transparent overlay */ + position: absolute; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #fff; + opacity: .25; + filter: alpha(opacity=25); + } + .fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */ .fc-select-helper .fc-event-bg { - display: none\9; /* for IE6/7/8. nested opacity filters while dragging don't work */ -} - + display: none\9; /* for IE6/7/8. nested opacity filters while dragging don't work */ + } + /* resizable */ - + .fc-event-vert .ui-resizable-s { - bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */ - width: 100% !important; - height: 8px !important; - overflow: hidden !important; - line-height: 8px !important; - font-size: 11px !important; - font-family: monospace; - text-align: center; - cursor: s-resize; -} - -.fc-agenda .ui-resizable-resizing { - /* TODO: better selector */ - _overflow: hidden; -} + bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */ + width: 100% !important; + height: 8px !important; + overflow: hidden !important; + line-height: 8px !important; + font-size: 11px !important; + font-family: monospace; + text-align: center; + cursor: s-resize; + } + +.fc-agenda .ui-resizable-resizing { /* TODO: better selector */ + _overflow: hidden; + } + + diff --git a/UI/JsLibraries/fullcalendar.js b/UI/JsLibraries/fullcalendar.js index eb72a039e..0d43e5866 100644 --- a/UI/JsLibraries/fullcalendar.js +++ b/UI/JsLibraries/fullcalendar.js @@ -1,23 +1,20 @@ -/** - * @preserve - * FullCalendar v1.5.4 - * http://arshaw.com/fullcalendar/ - * +/*! + * FullCalendar v1.6.1 + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */ + +/* * Use fullcalendar.css for basic styling. * For event drag & drop, requires jQuery UI draggable. * For event resizing, requires jQuery UI resizable. - * - * Copyright (c) 2011 Adam Shaw - * Dual licensed under the MIT and GPL licenses, located in - * MIT-LICENSE.txt and GPL-LICENSE.txt respectively. - * - * Date: Tue Sep 4 23:38:33 2012 -0700 - * */ (function($, undefined) { +;; + var defaults = { // display @@ -29,6 +26,9 @@ var defaults = { right: 'today prev,next' }, weekends: true, + weekNumbers: false, + weekNumberCalculation: 'iso', + weekNumberTitle: 'W', // editing //editable: false, @@ -66,10 +66,10 @@ var defaults = { dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], buttonText: { - prev: ' ◄ ', - next: ' ► ', - prevYear: ' << ', - nextYear: ' >> ', + prev: "", + next: "", + prevYear: "«", + nextYear: "»", today: 'today', month: 'month', week: 'week', @@ -98,10 +98,10 @@ var rtlDefaults = { right: 'title' }, buttonText: { - prev: ' ► ', - next: ' ◄ ', - prevYear: ' >> ', - nextYear: ' << ' + prev: "", + next: "", + prevYear: "»", + nextYear: "«" }, buttonIcons: { prev: 'circle-triangle-e', @@ -111,7 +111,9 @@ var rtlDefaults = { -var fc = $.fullCalendar = { version: "1.5.4" }; +;; + +var fc = $.fullCalendar = { version: "1.6.1" }; var fcViews = fc.views = {}; @@ -177,6 +179,8 @@ function setDefaults(d) { +;; + function Calendar(element, options, eventSources) { var t = this; @@ -253,12 +257,14 @@ function Calendar(element, options, eventSources) { function initialRender() { - element.html(''); tm = options.theme ? 'ui' : 'fc'; element.addClass('fc'); if (options.isRTL) { element.addClass('fc-rtl'); } + else { + element.addClass('fc-ltr'); + } if (options.theme) { element.addClass('ui-widget'); } @@ -675,6 +681,8 @@ function Calendar(element, options, eventSources) { } +;; + function Header(calendar, options) { var t = this; @@ -748,54 +756,47 @@ function Header(calendar, options) { var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here? var button = $( "" + - "" + - "" + - (icon ? - "" + - "" + - "" : - text - ) + - "" + - "" + - "" + + (icon ? + "" + + "" + + "" : + text + ) + "" - ); - if (button) { - button - .click(function() { - if (!button.hasClass(tm + '-state-disabled')) { - buttonClick(); - } - }) - .mousedown(function() { + ) + .click(function() { + if (!button.hasClass(tm + '-state-disabled')) { + buttonClick(); + } + }) + .mousedown(function() { + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-down'); + }) + .mouseup(function() { + button.removeClass(tm + '-state-down'); + }) + .hover( + function() { button .not('.' + tm + '-state-active') .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-down'); - }) - .mouseup(function() { - button.removeClass(tm + '-state-down'); - }) - .hover( - function() { - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-hover'); - }, - function() { - button - .removeClass(tm + '-state-hover') - .removeClass(tm + '-state-down'); - } - ) - .appendTo(e); - if (!prevButton) { - button.addClass(tm + '-corner-left'); - } - prevButton = button; + .addClass(tm + '-state-hover'); + }, + function() { + button + .removeClass(tm + '-state-hover') + .removeClass(tm + '-state-down'); + } + ) + .appendTo(e); + disableTextSelection(button); + if (!prevButton) { + button.addClass(tm + '-corner-left'); } + prevButton = button; } } }); @@ -840,6 +841,8 @@ function Header(calendar, options) { } +;; + fc.sourceNormalizers = []; fc.sourceFetchers = []; @@ -915,6 +918,16 @@ function EventManager(options, _sources) { _fetchEventSource(source, function(events) { if (fetchID == currentFetchID) { if (events) { + + if (options.eventDataTransform) { + events = $.map(events, options.eventDataTransform); + } + if (source.eventDataTransform) { + events = $.map(events, source.eventDataTransform); + } + // TODO: this technique is not ideal for static array event sources. + // For arrays, we'll want to process all events right in the beginning, then never again. + for (var i=0; i") + .appendTo(element); + } + + + + function buildTable(showNumbers) { + var html = ''; + var i, j; var headerClass = tm + "-widget-header"; var contentClass = tm + "-widget-content"; - var i, j; - var table; - - s = - "" + - "" + - ""; + var month = t.start.getMonth(); + var today = clearTime(new Date()); + var cellDate; // not to be confused with local function. TODO: better names + var cellClasses; + var cell; + + html += "
" + + "" + + ""; + + if (showWeekNumbers) { + html += "" + - "" + - ""; - for (i=0; i"; + + html += "" + + "" + + ""; + + for (i=0; i" + + "
" + + ""; + } + for (j=0; j" + // need fc- for setDayID - "
" + - (showNumbers ? - "
" : - '' - ) + - "
" + - "
 
" + - "
" + - "
" + - ""; + cellDate = _cellDate(i, j); // a little confusing. cellDate is local variable. _cellDate is private function + + cellClasses = [ + 'fc-day', + 'fc-' + dayIDs[cellDate.getDay()], + contentClass + ]; + if (cellDate.getMonth() != month) { + cellClasses.push('fc-other-month'); + } + if (+cellDate == +today) { + cellClasses.push('fc-today'); + cellClasses.push(tm + '-state-highlight'); + } + + html += "" + + "
"; + if (showNumbers) { + html += "
" + cellDate.getDate() + "
"; + } + html += "
" + + "
 
" + + "
" + + "
" + + ""; } - s += - ""; + + html += ""; } - s += - "
" + - "
"; + } + for (i=0; i"; // need fc- for setDayID + cellDate = _cellDate(0, i); // a little confusing. cellDate is local variable. _cellDate is private function + html += ""; } - s += - "
"; - table = $(s).appendTo(element); - + html += "" + + ""; + + lockHeight(); // the unlock happens later, in setHeight()... + if (table) { + table.remove(); + } + table = $(html).appendTo(element); + head = table.find('thead'); - headCells = head.find('th'); + headCells = head.find('.fc-day-header'); body = table.find('tbody'); bodyRows = body.find('tr'); - bodyCells = body.find('td'); - bodyFirstCells = bodyCells.filter(':first-child'); - bodyCellTopInners = bodyRows.eq(0).find('div.fc-day-content div'); + bodyCells = body.find('.fc-day'); + bodyFirstCells = bodyRows.find('td:first-child'); + bodyCellTopInners = bodyRows.eq(0).find('.fc-day-content > div'); markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's markFirstLast(bodyRows); // marks first+last td's - bodyRows.eq(0).addClass('fc-first'); // fc-last is done in updateCells - - dayBind(bodyCells); - - daySegmentContainer = - $("
") - .appendTo(element); - } + bodyRows.eq(0).addClass('fc-first'); + bodyRows.filter(':last').addClass('fc-last'); - - - function updateCells(firstTime) { - var dowDirty = firstTime || rowCnt == 1; // could the cells' day-of-weeks need updating? - var month = t.start.getMonth(); - var today = clearTime(new Date()); - var cell; - var date; - var row; - - if (dowDirty) { - headCells.each(function(i, _cell) { - cell = $(_cell); - date = indexDate(i); - cell.html(formatDate(date, colFormat)); - setDayID(cell, date); + if (showWeekNumbers) { + head.find('.fc-week-number').text(weekNumberTitle); + } + + headCells.each(function(i, _cell) { + var date = indexDate(i); + $(_cell).text(formatDate(date, colFormat)); + }); + + if (showWeekNumbers) { + body.find('.fc-week-number > div').each(function(i, _cell) { + var weekStart = _cellDate(i, 0); + $(_cell).text(formatDate(weekStart, weekNumberFormat)); }); } bodyCells.each(function(i, _cell) { - cell = $(_cell); - date = indexDate(i); - if (date.getMonth() == month) { - cell.removeClass('fc-other-month'); - }else{ - cell.addClass('fc-other-month'); - } - if (+date == +today) { - cell.addClass(tm + '-state-highlight fc-today'); - }else{ - cell.removeClass(tm + '-state-highlight fc-today'); - } - cell.find('div.fc-day-number').text(date.getDate()); - if (dowDirty) { - setDayID(cell, date); - } - }); - - bodyRows.each(function(i, _row) { - row = $(_row); - if (i < rowCnt) { - row.show(); - if (i == rowCnt-1) { - row.addClass('fc-last'); - }else{ - row.removeClass('fc-last'); - } - }else{ - row.hide(); - } + var date = indexDate(i); + trigger('dayRender', t, date, $(_cell)); }); + + dayBind(bodyCells); } @@ -2349,13 +2423,20 @@ function BasicView(element, calendar, viewName) { } }); + unlockHeight(); } function setWidth(width) { viewWidth = width; colContentPositions.clear(); - colWidth = Math.floor(viewWidth / colCnt); + + weekNumberWidth = 0; + if (showWeekNumbers) { + weekNumberWidth = head.find('th.fc-week-number').outerWidth(); + } + + colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt); setOuterWidth(headCells.slice(0, -1), colWidth); } @@ -2373,8 +2454,7 @@ function BasicView(element, calendar, viewName) { function dayClick(ev) { if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick - var index = parseInt(this.className.match(/fc\-day(\d+)/)[1]); // TODO: maybe use .data - var date = indexDate(index); + var date = parseISO8601($(this).data('date')); trigger('dayClick', this, date, true, ev); } } @@ -2565,15 +2645,34 @@ function BasicView(element, calendar, viewName) { function allDayBounds(i) { + var left = 0; + if (showWeekNumbers) { + left += weekNumberWidth; + } return { - left: 0, + left: left, right: viewWidth }; } - + + + + // makes sure height doesn't collapse while we destroy/render new cells + // (this causes a bad end-user scrollbar jump) + // TODO: generalize this for all view rendering. (also in Calendar.js) + + function lockHeight() { + setMinHeight(element, element.height()); + } + + function unlockHeight() { + setMinHeight(element, 1); + } } +;; + function BasicEventRenderer() { var t = this; @@ -2616,6 +2715,7 @@ function BasicEventRenderer() { function renderEvents(events, modifiedEventId) { reportEvents(events); renderDaySegs(compileSegs(events), modifiedEventId); + trigger('eventAfterAllRender'); } @@ -2715,6 +2815,8 @@ function BasicEventRenderer() { } +;; + fcViews.agendaWeek = AgendaWeekView; function AgendaWeekView(element, calendar) { @@ -2761,6 +2863,8 @@ function AgendaWeekView(element, calendar) { } +;; + fcViews.agendaDay = AgendaDayView; function AgendaDayView(element, calendar) { @@ -2797,6 +2901,8 @@ function AgendaDayView(element, calendar) { } +;; + setDefaults({ allDaySlot: true, allDayText: 'all-day', @@ -2848,7 +2954,8 @@ function AgendaView(element, calendar, viewName) { t.getRowCnt = function() { return 1 }; t.getColCnt = function() { return colCnt }; t.getColWidth = function() { return colWidth }; - t.getSlotHeight = function() { return slotHeight }; + t.getSnapHeight = function() { return snapHeight }; + t.getSnapMinutes = function() { return snapMinutes }; t.defaultSelectionEnd = defaultSelectionEnd; t.renderDayOverlay = renderDayOverlay; t.renderSelection = renderSelection; @@ -2904,7 +3011,10 @@ function AgendaView(element, calendar, viewName) { var colWidth; var gutterWidth; var slotHeight; // TODO: what if slotHeight changes? (see issue 650) - var savedScrollTop; + + var snapMinutes; + var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4) + var snapHeight; // holds the pixel hight of a "selection" slot var colCnt; var slotCnt; @@ -2912,6 +3022,7 @@ function AgendaView(element, calendar, viewName) { var hoverListener; var colContentPositions; var slotTopCache = {}; + var savedScrollTop; var tm; var firstDay; @@ -2919,6 +3030,9 @@ function AgendaView(element, calendar, viewName) { var rtl, dis, dit; // day index sign / translate var minMinute, maxMinute; var colFormat; + var showWeekNumbers; + var weekNumberTitle; + var weekNumberFormat; @@ -2956,6 +3070,18 @@ function AgendaView(element, calendar, viewName) { minMinute = parseTime(opt('minTime')); maxMinute = parseTime(opt('maxTime')); colFormat = opt('columnFormat'); + + // week # options. (TODO: bad, logic also in other views) + showWeekNumbers = opt('weekNumbers'); + weekNumberTitle = opt('weekNumberTitle'); + if (opt('weekNumberCalculation') != 'iso') { + weekNumberFormat = "w"; + } + else { + weekNumberFormat = "W"; + } + + snapMinutes = opt('snapMinutes') || opt('slotMinutes'); } @@ -2973,8 +3099,15 @@ function AgendaView(element, calendar, viewName) { s = "" + "" + - "" + - ""; + ""; + + if (showWeekNumbers) { + s += ""; + } + for (i=0; i"; // fc- needed for setDayID @@ -3108,6 +3241,18 @@ function AgendaView(element, calendar, viewName) { var bodyCell; var date; var today = clearTime(new Date()); + + if (showWeekNumbers) { + var weekText = formatDate(colDate(0), weekNumberFormat); + if (rtl) { + weekText = weekText + weekNumberTitle; + } + else { + weekText = weekNumberTitle + weekText; + } + dayHead.find('.fc-week-number').text(weekText); + } + for (i=0; i= 0) { - addMinutes(d, minMinute + slotIndex * opt('slotMinutes')); + addMinutes(d, minMinute + slotIndex * snapMinutes); } return d; } @@ -3541,9 +3689,9 @@ function AgendaView(element, calendar, viewName) { var d2 = cellDate(cell); dates = [ d1, - addMinutes(cloneDate(d1), opt('slotMinutes')), + addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes d2, - addMinutes(cloneDate(d2), opt('slotMinutes')) + addMinutes(cloneDate(d2), snapMinutes) ].sort(cmp); renderSlotSelection(dates[0], dates[3]); }else{ @@ -3600,6 +3748,8 @@ function AgendaView(element, calendar, viewName) { } +;; + function AgendaEventRenderer() { var t = this; @@ -3636,7 +3786,8 @@ function AgendaEventRenderer() { var resizableDayEvent = t.resizableDayEvent; // TODO: streamline binding architecture var getColCnt = t.getColCnt; var getColWidth = t.getColWidth; - var getSlotHeight = t.getSlotHeight; + var getSnapHeight = t.getSnapHeight; + var getSnapMinutes = t.getSnapMinutes; var getBodyContent = t.getBodyContent; var reportEventElement = t.reportEventElement; var showEvents = t.showEvents; @@ -3672,6 +3823,7 @@ function AgendaEventRenderer() { setHeight(); // no params means set to viewHeight } renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId); + trigger('eventAfterAllRender'); } @@ -3757,7 +3909,7 @@ function AgendaEventRenderer() { vsideCache={}, hsideCache={}, key, val, - contentElement, + titleElement, height, slotSegmentContainer = getSlotSegmentContainer(), rtl, dis, dit, @@ -3846,9 +3998,9 @@ function AgendaEventRenderer() { seg.vsides = val === undefined ? (vsideCache[key] = vsides(eventElement, true)) : val; val = hsideCache[key]; seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement, true)) : val; - contentElement = eventElement.find('div.fc-event-content'); - if (contentElement.length) { - seg.contentTop = contentElement[0].offsetTop; + titleElement = eventElement.find('.fc-event-title'); + if (titleElement.length) { + seg.contentTop = titleElement[0].offsetTop; } } } @@ -3862,7 +4014,7 @@ function AgendaEventRenderer() { eventElement[0].style.height = height + 'px'; event = seg.event; if (seg.contentTop !== undefined && height - seg.contentTop < 10) { - // not enough room for title, put it in the time header + // not enough room for title, put it in the time (TODO: maybe make both display:inline instead) eventElement.find('div.fc-event-time') .text(formatDate(event.start, opt('timeFormat')) + ' - ' + event.title); eventElement.find('div.fc-event-title') @@ -3879,16 +4031,15 @@ function AgendaEventRenderer() { var html = "<"; var url = event.url; var skinCss = getSkinCss(event, opt); - var skinCssAttr = (skinCss ? " style='" + skinCss + "'" : ''); - var classes = ['fc-event', 'fc-event-skin', 'fc-event-vert']; + var classes = ['fc-event', 'fc-event-vert']; if (isEventDraggable(event)) { classes.push('fc-event-draggable'); } if (seg.isStart) { - classes.push('fc-corner-top'); + classes.push('fc-event-start'); } if (seg.isEnd) { - classes.push('fc-corner-bottom'); + classes.push('fc-event-end'); } classes = classes.concat(event.className); if (event.source) { @@ -3903,19 +4054,15 @@ function AgendaEventRenderer() { " class='" + classes.join(' ') + "'" + " style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" + ">" + - "
" + - "
" + + "
" + "
" + htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + "
" + - "
" + - "
" + "
" + htmlEscape(event.title) + "
" + "
" + - "
" + - "
"; // close inner + "
"; if (seg.isEnd && isEventResizable(event)) { html += "
=
"; @@ -3965,7 +4112,8 @@ function AgendaEventRenderer() { var dis = opt('isRTL') ? -1 : 1; var hoverListener = getHoverListener(); var colWidth = getColWidth(); - var slotHeight = getSlotHeight(); + var snapHeight = getSnapHeight(); + var snapMinutes = getSnapMinutes(); var minMinute = getMinMinute(); eventElement.draggable({ zIndex: 9, @@ -3996,9 +4144,9 @@ function AgendaEventRenderer() { eventElement.width(colWidth - 10); // don't use entire width setOuterHeight( eventElement, - slotHeight * Math.round( - (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) - / opt('slotMinutes') + snapHeight * Math.round( + (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) / + snapMinutes ) ); eventElement.draggable('option', 'grid', [colWidth, 1]); @@ -4030,8 +4178,8 @@ function AgendaEventRenderer() { // changed! var minuteDelta = 0; if (!allDay) { - minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight) - * opt('slotMinutes') + minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / snapHeight) + * snapMinutes + minMinute - (event.start.getHours() * 60 + event.start.getMinutes()); } @@ -4064,11 +4212,12 @@ function AgendaEventRenderer() { var hoverListener = getHoverListener(); var colCnt = getColCnt(); var colWidth = getColWidth(); - var slotHeight = getSlotHeight(); + var snapHeight = getSnapHeight(); + var snapMinutes = getSnapMinutes(); eventElement.draggable({ zIndex: 9, scroll: false, - grid: [colWidth, slotHeight], + grid: [colWidth, snapHeight], axis: colCnt==1 ? 'y' : false, opacity: opt('dragOpacity'), revertDuration: opt('dragRevertDuration'), @@ -4102,7 +4251,7 @@ function AgendaEventRenderer() { }, ev, 'drag'); }, drag: function(ev, ui) { - minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * opt('slotMinutes'); + minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes; if (minuteDelta != prevMinuteDelta) { if (!allDay) { updateTimeText(minuteDelta); @@ -4139,7 +4288,7 @@ function AgendaEventRenderer() { // convert back to original slot-event if (allDay) { timeElement.css('display', ''); // show() was causing display=inline - eventElement.draggable('option', 'grid', [colWidth, slotHeight]); + eventElement.draggable('option', 'grid', [colWidth, snapHeight]); allDay = false; } } @@ -4152,38 +4301,39 @@ function AgendaEventRenderer() { function resizableSlotEvent(event, eventElement, timeElement) { - var slotDelta, prevSlotDelta; - var slotHeight = getSlotHeight(); + var snapDelta, prevSnapDelta; + var snapHeight = getSnapHeight(); + var snapMinutes = getSnapMinutes(); eventElement.resizable({ handles: { - s: 'div.ui-resizable-s' + s: '.ui-resizable-handle' }, - grid: slotHeight, + grid: snapHeight, start: function(ev, ui) { - slotDelta = prevSlotDelta = 0; + snapDelta = prevSnapDelta = 0; hideEvents(event, eventElement); eventElement.css('z-index', 9); trigger('eventResizeStart', this, event, ev, ui); }, resize: function(ev, ui) { // don't rely on ui.size.height, doesn't take grid into account - slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight); - if (slotDelta != prevSlotDelta) { + snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight); + if (snapDelta != prevSnapDelta) { timeElement.text( formatDates( event.start, - (!slotDelta && !event.end) ? null : // no change, so don't display time range - addMinutes(eventEnd(event), opt('slotMinutes')*slotDelta), + (!snapDelta && !event.end) ? null : // no change, so don't display time range + addMinutes(eventEnd(event), snapMinutes*snapDelta), opt('timeFormat') ) ); - prevSlotDelta = slotDelta; + prevSnapDelta = snapDelta; } }, stop: function(ev, ui) { trigger('eventResizeStop', this, event, ev, ui); - if (slotDelta) { - eventResize(this, event, 0, opt('slotMinutes')*slotDelta, ev, ui); + if (snapDelta) { + eventResize(this, event, 0, snapMinutes*snapDelta, ev, ui); }else{ eventElement.css('z-index', 8); showEvents(event, eventElement); @@ -4215,6 +4365,8 @@ function countForwardSegs(levels) { +;; + function View(element, calendar, viewName) { var t = this; @@ -4469,6 +4621,8 @@ function View(element, calendar, viewName) { } +;; + function DayEventRenderer() { var t = this; @@ -4606,28 +4760,22 @@ function DayEventRenderer() { for (i=0; i" + - ""; + "
"; if (!event.allDay && seg.isStart) { html += "" + @@ -4813,7 +4958,7 @@ function DayEventRenderer() { var rowDivs = []; for (i=0; i div'); // optimal selector? + .find('div.fc-day-content > div'); // optimal selector? } return rowDivs; } @@ -4856,7 +5001,7 @@ function DayEventRenderer() { function resizableDayEvent(event, element, seg) { var rtl = opt('isRTL'); var direction = rtl ? 'w' : 'e'; - var handle = element.find('div.ui-resizable-' + direction); + var handle = element.find('.ui-resizable-' + direction); // TODO: stop using this class because we aren't using jqui for this var isResizing = false; // TODO: look into using jquery-ui mouse widget for this stuff @@ -4952,6 +5097,8 @@ function DayEventRenderer() { } +;; + //BUG: unselect needs to be triggered when events are dragged+dropped function SelectionManager() { @@ -5049,6 +5196,8 @@ function SelectionManager() { } + +;; function OverlayManager() { var t = this; @@ -5087,6 +5236,8 @@ function OverlayManager() { } +;; + function CoordinateGrid(buildFunc) { var t = this; @@ -5133,6 +5284,8 @@ function CoordinateGrid(buildFunc) { } +;; + function HoverListener(coordinateGrid) { @@ -5191,6 +5344,8 @@ function _fixUIEvent(event) { // for issue 1168 event.pageY = event.originalEvent.pageY; } } +;; + function HorizontalPositionCache(getElement) { var t = this, @@ -5218,4 +5373,6 @@ function HorizontalPositionCache(getElement) { } -})(jQuery); +;; + +})(jQuery); \ No newline at end of file
 
"; + } + else { + s += "