diff --git a/3rdparty/fullcalendar/GPL-LICENSE.txt b/3rdparty/fullcalendar/GPL-LICENSE.txt
new file mode 100644
index 00000000..11dddd00
--- /dev/null
+++ b/3rdparty/fullcalendar/GPL-LICENSE.txt
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/3rdparty/fullcalendar/MIT-LICENSE.txt b/3rdparty/fullcalendar/MIT-LICENSE.txt
new file mode 100644
index 00000000..46d47544
--- /dev/null
+++ b/3rdparty/fullcalendar/MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/3rdparty/fullcalendar/css/fullcalendar.css b/3rdparty/fullcalendar/css/fullcalendar.css
new file mode 100644
index 00000000..2df45443
--- /dev/null
+++ b/3rdparty/fullcalendar/css/fullcalendar.css
@@ -0,0 +1,579 @@
+/*!
+ * FullCalendar v1.6.1 Stylesheet
+ * Docs & License: http://arshaw.com/fullcalendar/
+ * (c) 2013 Adam Shaw
+ */
+
+
+.fc {
+ direction: ltr;
+ text-align: left;
+ }
+
+.fc table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ }
+
+html .fc,
+.fc table {
+ font-size: 1em;
+ }
+
+.fc td,
+.fc th {
+ padding: 0;
+ vertical-align: top;
+ }
+
+
+
+/* Header
+------------------------------------------------------------------------*/
+
+.fc-header td {
+ white-space: nowrap;
+ }
+
+.fc-header-left {
+ width: 25%;
+ text-align: left;
+ }
+
+.fc-header-center {
+ text-align: center;
+ }
+
+.fc-header-right {
+ width: 25%;
+ text-align: right;
+ }
+
+.fc-header-title {
+ display: inline-block;
+ vertical-align: top;
+ }
+
+.fc-header-title h2 {
+ margin-top: 0;
+ white-space: nowrap;
+ }
+
+.fc .fc-header-space {
+ padding-left: 10px;
+ }
+
+.fc-header .fc-button {
+ margin-bottom: 1em;
+ vertical-align: top;
+ }
+
+/* buttons edges butting together */
+
+.fc-header .fc-button {
+ 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;
+ }
+
+.fc-header .fc-state-down {
+ z-index: 3;
+ }
+
+.fc-header .fc-state-active,
+.fc-header .ui-state-active {
+ z-index: 4;
+ }
+
+
+
+/* Content
+------------------------------------------------------------------------*/
+
+.fc-content {
+ clear: both;
+ }
+
+.fc-view {
+ 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 #ddd;
+ }
+
+.fc-state-highlight { /* today cell */ /* TODO: add .fc-today to */
+ background: #ffa;
+ }
+
+.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;
+ 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-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-down,
+.fc-state-active,
+.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;
+ background-image: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ box-shadow: none;
+ }
+
+
+
+/* Global Event Styles
+------------------------------------------------------------------------*/
+
+.fc-event {
+ 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;
+ }
+
+.fc-rtl .fc-event {
+ text-align: right;
+ }
+
+.fc-event-inner {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ }
+
+.fc-event-time,
+.fc-event-title {
+ padding: 0 1px;
+ }
+
+.fc .ui-resizable-handle {
+ 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;
+ }
+
+.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;
+ }
+
+.fc-event-hori .ui-resizable-w {
+ 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 */
+ }
+
+
+
+/* Reusable Separate-border Table
+------------------------------------------------------------*/
+
+table.fc-border-separate {
+ border-collapse: separate;
+ }
+
+.fc-border-separate th,
+.fc-border-separate td {
+ border-width: 1px 0 0 1px;
+ }
+
+.fc-border-separate th.fc-last,
+.fc-border-separate td.fc-last {
+ border-right-width: 1px;
+ }
+
+.fc-border-separate tr.fc-last th,
+.fc-border-separate tr.fc-last td {
+ border-bottom-width: 1px;
+ }
+
+.fc-border-separate tbody tr.fc-first td,
+.fc-border-separate tbody tr.fc-first th {
+ border-top-width: 0;
+ }
+
+
+
+/* Month View, Basic Week View, Basic Day View
+------------------------------------------------------------------------*/
+
+.fc-grid th {
+ 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;
+ }
+
+.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 */
+ }
+
+.fc-grid .fc-day-content {
+ clear: both;
+ padding: 2px 2px 1px; /* distance between events and day edges */
+ }
+
+/* event styles */
+
+.fc-grid .fc-event-time {
+ font-weight: bold;
+ }
+
+/* right-to-left */
+
+.fc-rtl .fc-grid .fc-day-number {
+ float: left;
+ }
+
+.fc-rtl .fc-grid .fc-event-time {
+ float: right;
+ }
+
+
+
+/* Agenda Week View, Agenda Day View
+------------------------------------------------------------------------*/
+
+.fc-agenda table {
+ border-collapse: separate;
+ }
+
+.fc-agenda-days th {
+ 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;
+ }
+
+.fc-agenda .fc-week-number {
+ font-weight: bold;
+ }
+
+.fc-agenda .fc-day-content {
+ padding: 2px 2px 1px;
+ }
+
+/* make axis border take precedence */
+
+.fc-agenda-days .fc-agenda-axis {
+ border-right-width: 1px;
+ }
+
+.fc-agenda-days .fc-col0 {
+ border-left-width: 0;
+ }
+
+/* all-day area */
+
+.fc-agenda-allday th {
+ border-width: 0 1px;
+ }
+
+.fc-agenda-allday .fc-day-content {
+ 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;
+ }
+
+.fc-widget-header .fc-agenda-divider-inner {
+ background: #eee;
+ }
+
+/* slot rows */
+
+.fc-agenda-slots th {
+ border-width: 1px 1px 0;
+ }
+
+.fc-agenda-slots td {
+ border-width: 1px 0 0;
+ background: none;
+ }
+
+.fc-agenda-slots td div {
+ height: 20px;
+ }
+
+.fc-agenda-slots tr.fc-slot0 th,
+.fc-agenda-slots tr.fc-slot0 td {
+ border-top-width: 0;
+ }
+
+.fc-agenda-slots tr.fc-minor th,
+.fc-agenda-slots tr.fc-minor td {
+ 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 */
+ }
+
+
+
+/* Vertical Events
+------------------------------------------------------------------------*/
+
+.fc-event-vert {
+ 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-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 */
+ }
+
+/* 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;
+ }
+
+
diff --git a/3rdparty/fullcalendar/css/fullcalendar.print.css b/3rdparty/fullcalendar/css/fullcalendar.print.css
new file mode 100644
index 00000000..8fea38db
--- /dev/null
+++ b/3rdparty/fullcalendar/css/fullcalendar.print.css
@@ -0,0 +1,32 @@
+/*!
+ * FullCalendar v1.6.1 Print Stylesheet
+ * Docs & License: http://arshaw.com/fullcalendar/
+ * (c) 2013 Adam Shaw
+ */
+
+/*
+ * Include this stylesheet on your page to get a more printer-friendly calendar.
+ * When including this stylesheet, use the media='print' attribute of the tag.
+ * Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
+ */
+
+
+ /* Events
+-----------------------------------------------------*/
+
+.fc-event {
+ background: #fff !important;
+ color: #000 !important;
+ }
+
+/* for vertical events */
+
+.fc-event-bg {
+ display: none !important;
+ }
+
+.fc-event .ui-resizable-handle {
+ display: none !important;
+ }
+
+
diff --git a/3rdparty/fullcalendar/js/fullcalendar.js b/3rdparty/fullcalendar/js/fullcalendar.js
new file mode 100644
index 00000000..0d43e586
--- /dev/null
+++ b/3rdparty/fullcalendar/js/fullcalendar.js
@@ -0,0 +1,5378 @@
+/*!
+ * 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.
+ */
+
+(function($, undefined) {
+
+
+;;
+
+var defaults = {
+
+ // display
+ defaultView: 'month',
+ aspectRatio: 1.35,
+ header: {
+ left: 'title',
+ center: '',
+ right: 'today prev,next'
+ },
+ weekends: true,
+ weekNumbers: false,
+ weekNumberCalculation: 'iso',
+ weekNumberTitle: 'W',
+
+ // editing
+ //editable: false,
+ //disableDragging: false,
+ //disableResizing: false,
+
+ allDayDefault: true,
+ ignoreTimezone: true,
+
+ // event ajax
+ lazyFetching: true,
+ startParam: 'start',
+ endParam: 'end',
+
+ // time formats
+ titleFormat: {
+ month: 'MMMM yyyy',
+ week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}",
+ day: 'dddd, MMM d, yyyy'
+ },
+ columnFormat: {
+ month: 'ddd',
+ week: 'ddd M/d',
+ day: 'dddd M/d'
+ },
+ timeFormat: { // for event elements
+ '': 'h(:mm)t' // default
+ },
+
+ // locale
+ isRTL: false,
+ firstDay: 0,
+ monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
+ monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
+ dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
+ dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
+ buttonText: {
+ prev: "‹ ",
+ next: "› ",
+ prevYear: "« ",
+ nextYear: "» ",
+ today: 'today',
+ month: 'month',
+ week: 'week',
+ day: 'day'
+ },
+
+ // jquery-ui theming
+ theme: false,
+ buttonIcons: {
+ prev: 'circle-triangle-w',
+ next: 'circle-triangle-e'
+ },
+
+ //selectable: false,
+ unselectAuto: true,
+
+ dropAccept: '*'
+
+};
+
+// right-to-left defaults
+var rtlDefaults = {
+ header: {
+ left: 'next,prev today',
+ center: '',
+ right: 'title'
+ },
+ buttonText: {
+ prev: "› ",
+ next: "‹ ",
+ prevYear: "» ",
+ nextYear: "« "
+ },
+ buttonIcons: {
+ prev: 'circle-triangle-e',
+ next: 'circle-triangle-w'
+ }
+};
+
+
+
+;;
+
+var fc = $.fullCalendar = { version: "1.6.1" };
+var fcViews = fc.views = {};
+
+
+$.fn.fullCalendar = function(options) {
+
+
+ // method calling
+ if (typeof options == 'string') {
+ var args = Array.prototype.slice.call(arguments, 1);
+ var res;
+ this.each(function() {
+ var calendar = $.data(this, 'fullCalendar');
+ if (calendar && $.isFunction(calendar[options])) {
+ var r = calendar[options].apply(calendar, args);
+ if (res === undefined) {
+ res = r;
+ }
+ if (options == 'destroy') {
+ $.removeData(this, 'fullCalendar');
+ }
+ }
+ });
+ if (res !== undefined) {
+ return res;
+ }
+ return this;
+ }
+
+
+ // would like to have this logic in EventManager, but needs to happen before options are recursively extended
+ var eventSources = options.eventSources || [];
+ delete options.eventSources;
+ if (options.events) {
+ eventSources.push(options.events);
+ delete options.events;
+ }
+
+
+ options = $.extend(true, {},
+ defaults,
+ (options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {},
+ options
+ );
+
+
+ this.each(function(i, _element) {
+ var element = $(_element);
+ var calendar = new Calendar(element, options, eventSources);
+ element.data('fullCalendar', calendar); // TODO: look into memory leak implications
+ calendar.render();
+ });
+
+
+ return this;
+
+};
+
+
+// function for adding/overriding defaults
+function setDefaults(d) {
+ $.extend(true, defaults, d);
+}
+
+
+
+;;
+
+
+function Calendar(element, options, eventSources) {
+ var t = this;
+
+
+ // exports
+ t.options = options;
+ t.render = render;
+ t.destroy = destroy;
+ t.refetchEvents = refetchEvents;
+ t.reportEvents = reportEvents;
+ t.reportEventChange = reportEventChange;
+ t.rerenderEvents = rerenderEvents;
+ t.changeView = changeView;
+ t.select = select;
+ t.unselect = unselect;
+ t.prev = prev;
+ t.next = next;
+ t.prevYear = prevYear;
+ t.nextYear = nextYear;
+ t.today = today;
+ t.gotoDate = gotoDate;
+ t.incrementDate = incrementDate;
+ t.formatDate = function(format, date) { return formatDate(format, date, options) };
+ t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) };
+ t.getDate = getDate;
+ t.getView = getView;
+ t.option = option;
+ t.trigger = trigger;
+
+
+ // imports
+ EventManager.call(t, options, eventSources);
+ var isFetchNeeded = t.isFetchNeeded;
+ var fetchEvents = t.fetchEvents;
+
+
+ // locals
+ var _element = element[0];
+ var header;
+ var headerElement;
+ var content;
+ var tm; // for making theme classes
+ var currentView;
+ var viewInstances = {};
+ var elementOuterWidth;
+ var suggestedViewHeight;
+ var absoluteViewElement;
+ var resizeUID = 0;
+ var ignoreWindowResize = 0;
+ var date = new Date();
+ var events = [];
+ var _dragElement;
+
+
+
+ /* Main Rendering
+ -----------------------------------------------------------------------------*/
+
+
+ setYMD(date, options.year, options.month, options.date);
+
+
+ function render(inc) {
+ if (!content) {
+ initialRender();
+ }else{
+ calcSize();
+ markSizesDirty();
+ markEventsDirty();
+ renderView(inc);
+ }
+ }
+
+
+ function initialRender() {
+ 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');
+ }
+ content = $("
")
+ .prependTo(element);
+ header = new Header(t, options);
+ headerElement = header.render();
+ if (headerElement) {
+ element.prepend(headerElement);
+ }
+ changeView(options.defaultView);
+ $(window).resize(windowResize);
+ // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
+ if (!bodyVisible()) {
+ lateRender();
+ }
+ }
+
+
+ // called when we know the calendar couldn't be rendered when it was initialized,
+ // but we think it's ready now
+ function lateRender() {
+ setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
+ if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once
+ renderView();
+ }
+ },0);
+ }
+
+
+ function destroy() {
+ $(window).unbind('resize', windowResize);
+ header.destroy();
+ content.remove();
+ element.removeClass('fc fc-rtl ui-widget');
+ }
+
+
+
+ function elementVisible() {
+ return _element.offsetWidth !== 0;
+ }
+
+
+ function bodyVisible() {
+ return $('body')[0].offsetWidth !== 0;
+ }
+
+
+
+ /* View Rendering
+ -----------------------------------------------------------------------------*/
+
+ // TODO: improve view switching (still weird transition in IE, and FF has whiteout problem)
+
+ function changeView(newViewName) {
+ if (!currentView || newViewName != currentView.name) {
+ ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached
+
+ unselect();
+
+ var oldView = currentView;
+ var newViewElement;
+
+ if (oldView) {
+ (oldView.beforeHide || noop)(); // called before changing min-height. if called after, scroll state is reset (in Opera)
+ setMinHeight(content, content.height());
+ oldView.element.hide();
+ }else{
+ setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated
+ }
+ content.css('overflow', 'hidden');
+
+ currentView = viewInstances[newViewName];
+ if (currentView) {
+ currentView.element.show();
+ }else{
+ currentView = viewInstances[newViewName] = new fcViews[newViewName](
+ newViewElement = absoluteViewElement =
+ $("
")
+ .appendTo(content),
+ t // the calendar object
+ );
+ }
+
+ if (oldView) {
+ header.deactivateButton(oldView.name);
+ }
+ header.activateButton(newViewName);
+
+ renderView(); // after height has been set, will make absoluteViewElement's position=relative, then set to null
+
+ content.css('overflow', '');
+ if (oldView) {
+ setMinHeight(content, 1);
+ }
+
+ if (!newViewElement) {
+ (currentView.afterShow || noop)(); // called after setting min-height/overflow, so in final scroll state (for Opera)
+ }
+
+ ignoreWindowResize--;
+ }
+ }
+
+
+
+ function renderView(inc) {
+ if (elementVisible()) {
+ ignoreWindowResize++; // because renderEvents might temporarily change the height before setSize is reached
+
+ unselect();
+
+ if (suggestedViewHeight === undefined) {
+ calcSize();
+ }
+
+ var forceEventRender = false;
+ if (!currentView.start || inc || date < currentView.start || date >= currentView.end) {
+ // view must render an entire new date range (and refetch/render events)
+ currentView.render(date, inc || 0); // responsible for clearing events
+ setSize(true);
+ forceEventRender = true;
+ }
+ else if (currentView.sizeDirty) {
+ // view must resize (and rerender events)
+ currentView.clearEvents();
+ setSize();
+ forceEventRender = true;
+ }
+ else if (currentView.eventsDirty) {
+ currentView.clearEvents();
+ forceEventRender = true;
+ }
+ currentView.sizeDirty = false;
+ currentView.eventsDirty = false;
+ updateEvents(forceEventRender);
+
+ elementOuterWidth = element.outerWidth();
+
+ header.updateTitle(currentView.title);
+ var today = new Date();
+ if (today >= currentView.start && today < currentView.end) {
+ header.disableButton('today');
+ }else{
+ header.enableButton('today');
+ }
+
+ ignoreWindowResize--;
+ currentView.trigger('viewDisplay', _element);
+ }
+ }
+
+
+
+ /* Resizing
+ -----------------------------------------------------------------------------*/
+
+
+ function updateSize() {
+ markSizesDirty();
+ if (elementVisible()) {
+ calcSize();
+ setSize();
+ unselect();
+ currentView.clearEvents();
+ currentView.renderEvents(events);
+ currentView.sizeDirty = false;
+ }
+ }
+
+
+ function markSizesDirty() {
+ $.each(viewInstances, function(i, inst) {
+ inst.sizeDirty = true;
+ });
+ }
+
+
+ function calcSize() {
+ if (options.contentHeight) {
+ suggestedViewHeight = options.contentHeight;
+ }
+ else if (options.height) {
+ suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content);
+ }
+ else {
+ suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
+ }
+ }
+
+
+ function setSize(dateChanged) { // todo: dateChanged?
+ ignoreWindowResize++;
+ currentView.setHeight(suggestedViewHeight, dateChanged);
+ if (absoluteViewElement) {
+ absoluteViewElement.css('position', 'relative');
+ absoluteViewElement = null;
+ }
+ currentView.setWidth(content.width(), dateChanged);
+ ignoreWindowResize--;
+ }
+
+
+ function windowResize() {
+ if (!ignoreWindowResize) {
+ if (currentView.start) { // view has already been rendered
+ var uid = ++resizeUID;
+ setTimeout(function() { // add a delay
+ if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
+ if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
+ ignoreWindowResize++; // in case the windowResize callback changes the height
+ updateSize();
+ currentView.trigger('windowResize', _element);
+ ignoreWindowResize--;
+ }
+ }
+ }, 200);
+ }else{
+ // calendar must have been initialized in a 0x0 iframe that has just been resized
+ lateRender();
+ }
+ }
+ }
+
+
+
+ /* Event Fetching/Rendering
+ -----------------------------------------------------------------------------*/
+
+
+ // fetches events if necessary, rerenders events if necessary (or if forced)
+ function updateEvents(forceRender) {
+ if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
+ refetchEvents();
+ }
+ else if (forceRender) {
+ rerenderEvents();
+ }
+ }
+
+
+ function refetchEvents() {
+ fetchEvents(currentView.visStart, currentView.visEnd); // will call reportEvents
+ }
+
+
+ // called when event data arrives
+ function reportEvents(_events) {
+ events = _events;
+ rerenderEvents();
+ }
+
+
+ // called when a single event's data has been changed
+ function reportEventChange(eventID) {
+ rerenderEvents(eventID);
+ }
+
+
+ // attempts to rerenderEvents
+ function rerenderEvents(modifiedEventID) {
+ markEventsDirty();
+ if (elementVisible()) {
+ currentView.clearEvents();
+ currentView.renderEvents(events, modifiedEventID);
+ currentView.eventsDirty = false;
+ }
+ }
+
+
+ function markEventsDirty() {
+ $.each(viewInstances, function(i, inst) {
+ inst.eventsDirty = true;
+ });
+ }
+
+
+
+ /* Selection
+ -----------------------------------------------------------------------------*/
+
+
+ function select(start, end, allDay) {
+ currentView.select(start, end, allDay===undefined ? true : allDay);
+ }
+
+
+ function unselect() { // safe to be called before renderView
+ if (currentView) {
+ currentView.unselect();
+ }
+ }
+
+
+
+ /* Date
+ -----------------------------------------------------------------------------*/
+
+
+ function prev() {
+ renderView(-1);
+ }
+
+
+ function next() {
+ renderView(1);
+ }
+
+
+ function prevYear() {
+ addYears(date, -1);
+ renderView();
+ }
+
+
+ function nextYear() {
+ addYears(date, 1);
+ renderView();
+ }
+
+
+ function today() {
+ date = new Date();
+ renderView();
+ }
+
+
+ function gotoDate(year, month, dateOfMonth) {
+ if (year instanceof Date) {
+ date = cloneDate(year); // provided 1 argument, a Date
+ }else{
+ setYMD(date, year, month, dateOfMonth);
+ }
+ renderView();
+ }
+
+
+ function incrementDate(years, months, days) {
+ if (years !== undefined) {
+ addYears(date, years);
+ }
+ if (months !== undefined) {
+ addMonths(date, months);
+ }
+ if (days !== undefined) {
+ addDays(date, days);
+ }
+ renderView();
+ }
+
+
+ function getDate() {
+ return cloneDate(date);
+ }
+
+
+
+ /* Misc
+ -----------------------------------------------------------------------------*/
+
+
+ function getView() {
+ return currentView;
+ }
+
+
+ function option(name, value) {
+ if (value === undefined) {
+ return options[name];
+ }
+ if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
+ options[name] = value;
+ updateSize();
+ }
+ }
+
+
+ function trigger(name, thisObj) {
+ if (options[name]) {
+ return options[name].apply(
+ thisObj || _element,
+ Array.prototype.slice.call(arguments, 2)
+ );
+ }
+ }
+
+
+
+ /* External Dragging
+ ------------------------------------------------------------------------*/
+
+ if (options.droppable) {
+ $(document)
+ .bind('dragstart', function(ev, ui) {
+ var _e = ev.target;
+ var e = $(_e);
+ if (!e.parents('.fc').length) { // not already inside a calendar
+ var accept = options.dropAccept;
+ if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) {
+ _dragElement = _e;
+ currentView.dragStart(_dragElement, ev, ui);
+ }
+ }
+ })
+ .bind('dragstop', function(ev, ui) {
+ if (_dragElement) {
+ currentView.dragStop(_dragElement, ev, ui);
+ _dragElement = null;
+ }
+ });
+ }
+
+
+}
+
+;;
+
+function Header(calendar, options) {
+ var t = this;
+
+
+ // exports
+ t.render = render;
+ t.destroy = destroy;
+ t.updateTitle = updateTitle;
+ t.activateButton = activateButton;
+ t.deactivateButton = deactivateButton;
+ t.disableButton = disableButton;
+ t.enableButton = enableButton;
+
+
+ // locals
+ var element = $([]);
+ var tm;
+
+
+
+ function render() {
+ tm = options.theme ? 'ui' : 'fc';
+ var sections = options.header;
+ if (sections) {
+ element = $("")
+ .append(
+ $(" ")
+ .append(renderSection('left'))
+ .append(renderSection('center'))
+ .append(renderSection('right'))
+ );
+ return element;
+ }
+ }
+
+
+ function destroy() {
+ element.remove();
+ }
+
+
+ function renderSection(position) {
+ var e = $("");
+ var buttonStr = options.header[position];
+ if (buttonStr) {
+ $.each(buttonStr.split(' '), function(i) {
+ if (i > 0) {
+ e.append("");
+ }
+ var prevButton;
+ $.each(this.split(','), function(j, buttonName) {
+ if (buttonName == 'title') {
+ e.append("");
+ if (prevButton) {
+ prevButton.addClass(tm + '-corner-right');
+ }
+ prevButton = null;
+ }else{
+ var buttonClick;
+ if (calendar[buttonName]) {
+ buttonClick = calendar[buttonName]; // calendar method
+ }
+ else if (fcViews[buttonName]) {
+ buttonClick = function() {
+ button.removeClass(tm + '-state-hover'); // forget why
+ calendar.changeView(buttonName);
+ };
+ }
+ if (buttonClick) {
+ var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here?
+ var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here?
+ var button = $(
+ "" +
+ (icon ?
+ "" +
+ " " +
+ " " :
+ text
+ ) +
+ " "
+ )
+ .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-hover');
+ },
+ function() {
+ button
+ .removeClass(tm + '-state-hover')
+ .removeClass(tm + '-state-down');
+ }
+ )
+ .appendTo(e);
+ disableTextSelection(button);
+ if (!prevButton) {
+ button.addClass(tm + '-corner-left');
+ }
+ prevButton = button;
+ }
+ }
+ });
+ if (prevButton) {
+ prevButton.addClass(tm + '-corner-right');
+ }
+ });
+ }
+ return e;
+ }
+
+
+ function updateTitle(html) {
+ element.find('h2')
+ .html(html);
+ }
+
+
+ function activateButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .addClass(tm + '-state-active');
+ }
+
+
+ function deactivateButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .removeClass(tm + '-state-active');
+ }
+
+
+ function disableButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .addClass(tm + '-state-disabled');
+ }
+
+
+ function enableButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .removeClass(tm + '-state-disabled');
+ }
+
+
+}
+
+;;
+
+fc.sourceNormalizers = [];
+fc.sourceFetchers = [];
+
+var ajaxDefaults = {
+ dataType: 'json',
+ cache: false
+};
+
+var eventGUID = 1;
+
+
+function EventManager(options, _sources) {
+ var t = this;
+
+
+ // exports
+ t.isFetchNeeded = isFetchNeeded;
+ t.fetchEvents = fetchEvents;
+ t.addEventSource = addEventSource;
+ t.removeEventSource = removeEventSource;
+ t.updateEvent = updateEvent;
+ t.renderEvent = renderEvent;
+ t.removeEvents = removeEvents;
+ t.clientEvents = clientEvents;
+ t.normalizeEvent = normalizeEvent;
+
+
+ // imports
+ var trigger = t.trigger;
+ var getView = t.getView;
+ var reportEvents = t.reportEvents;
+
+
+ // locals
+ var stickySource = { events: [] };
+ var sources = [ stickySource ];
+ var rangeStart, rangeEnd;
+ var currentFetchID = 0;
+ var pendingSourceCnt = 0;
+ var loadingLevel = 0;
+ var cache = [];
+
+
+ for (var i=0; i<_sources.length; i++) {
+ _addEventSource(_sources[i]);
+ }
+
+
+
+ /* Fetching
+ -----------------------------------------------------------------------------*/
+
+
+ function isFetchNeeded(start, end) {
+ return !rangeStart || start < rangeStart || end > rangeEnd;
+ }
+
+
+ function fetchEvents(start, end) {
+ rangeStart = start;
+ rangeEnd = end;
+ cache = [];
+ var fetchID = ++currentFetchID;
+ var len = sources.length;
+ pendingSourceCnt = len;
+ for (var i=0; i)), return null instead
+ return null;
+}
+
+
+function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
+ // derived from http://delete.me.uk/2005/03/iso8601.html
+ // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
+ var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
+ if (!m) {
+ return null;
+ }
+ var date = new Date(m[1], 0, 1);
+ if (ignoreTimezone || !m[13]) {
+ var check = new Date(m[1], 0, 1, 9, 0);
+ if (m[3]) {
+ date.setMonth(m[3] - 1);
+ check.setMonth(m[3] - 1);
+ }
+ if (m[5]) {
+ date.setDate(m[5]);
+ check.setDate(m[5]);
+ }
+ fixDate(date, check);
+ if (m[7]) {
+ date.setHours(m[7]);
+ }
+ if (m[8]) {
+ date.setMinutes(m[8]);
+ }
+ if (m[10]) {
+ date.setSeconds(m[10]);
+ }
+ if (m[12]) {
+ date.setMilliseconds(Number("0." + m[12]) * 1000);
+ }
+ fixDate(date, check);
+ }else{
+ date.setUTCFullYear(
+ m[1],
+ m[3] ? m[3] - 1 : 0,
+ m[5] || 1
+ );
+ date.setUTCHours(
+ m[7] || 0,
+ m[8] || 0,
+ m[10] || 0,
+ m[12] ? Number("0." + m[12]) * 1000 : 0
+ );
+ if (m[14]) {
+ var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
+ offset *= m[15] == '-' ? 1 : -1;
+ date = new Date(+date + (offset * 60 * 1000));
+ }
+ }
+ return date;
+}
+
+
+function parseTime(s) { // returns minutes since start of day
+ if (typeof s == 'number') { // an hour
+ return s * 60;
+ }
+ if (typeof s == 'object') { // a Date object
+ return s.getHours() * 60 + s.getMinutes();
+ }
+ var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
+ if (m) {
+ var h = parseInt(m[1], 10);
+ if (m[3]) {
+ h %= 12;
+ if (m[3].toLowerCase().charAt(0) == 'p') {
+ h += 12;
+ }
+ }
+ return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
+ }
+}
+
+
+
+/* Date Formatting
+-----------------------------------------------------------------------------*/
+// TODO: use same function formatDate(date, [date2], format, [options])
+
+
+function formatDate(date, format, options) {
+ return formatDates(date, null, format, options);
+}
+
+
+function formatDates(date1, date2, format, options) {
+ options = options || defaults;
+ var date = date1,
+ otherDate = date2,
+ i, len = format.length, c,
+ i2, formatter,
+ res = '';
+ for (i=0; ii; i2--) {
+ if (formatter = dateFormatters[format.substring(i, i2)]) {
+ if (date) {
+ res += formatter(date, options);
+ }
+ i = i2 - 1;
+ break;
+ }
+ }
+ if (i2 == i) {
+ if (date) {
+ res += c;
+ }
+ }
+ }
+ }
+ return res;
+};
+
+
+var dateFormatters = {
+ s : function(d) { return d.getSeconds() },
+ ss : function(d) { return zeroPad(d.getSeconds()) },
+ m : function(d) { return d.getMinutes() },
+ mm : function(d) { return zeroPad(d.getMinutes()) },
+ h : function(d) { return d.getHours() % 12 || 12 },
+ hh : function(d) { return zeroPad(d.getHours() % 12 || 12) },
+ H : function(d) { return d.getHours() },
+ HH : function(d) { return zeroPad(d.getHours()) },
+ d : function(d) { return d.getDate() },
+ dd : function(d) { return zeroPad(d.getDate()) },
+ ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
+ dddd: function(d,o) { return o.dayNames[d.getDay()] },
+ M : function(d) { return d.getMonth() + 1 },
+ MM : function(d) { return zeroPad(d.getMonth() + 1) },
+ MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
+ MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
+ yy : function(d) { return (d.getFullYear()+'').substring(2) },
+ yyyy: function(d) { return d.getFullYear() },
+ t : function(d) { return d.getHours() < 12 ? 'a' : 'p' },
+ tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' },
+ T : function(d) { return d.getHours() < 12 ? 'A' : 'P' },
+ TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' },
+ u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
+ S : function(d) {
+ var date = d.getDate();
+ if (date > 10 && date < 20) {
+ return 'th';
+ }
+ return ['st', 'nd', 'rd'][date%10-1] || 'th';
+ },
+ w : function(d, o) { // local
+ return o.weekNumberCalculation(d);
+ },
+ W : function(d) { // ISO
+ return iso8601Week(d);
+ }
+};
+fc.dateFormatters = dateFormatters;
+
+
+/* thanks jQuery UI (https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js)
+ *
+ * Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
+ * @param date Date - the date to get the week for
+ * @return number - the number of the week within the year that contains this date
+ */
+function iso8601Week(date) {
+ var time;
+ var checkDate = new Date(date.getTime());
+
+ // Find Thursday of this week starting on Monday
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
+
+ time = checkDate.getTime();
+ checkDate.setMonth(0); // Compare with Jan 1
+ checkDate.setDate(1);
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
+}
+
+
+;;
+
+fc.applyAll = applyAll;
+
+
+/* Event Date Math
+-----------------------------------------------------------------------------*/
+
+
+function exclEndDay(event) {
+ if (event.end) {
+ return _exclEndDay(event.end, event.allDay);
+ }else{
+ return addDays(cloneDate(event.start), 1);
+ }
+}
+
+
+function _exclEndDay(end, allDay) {
+ end = cloneDate(end);
+ return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end);
+}
+
+
+function segCmp(a, b) {
+ return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
+}
+
+
+function segsCollide(seg1, seg2) {
+ return seg1.end > seg2.start && seg1.start < seg2.end;
+}
+
+
+
+/* Event Sorting
+-----------------------------------------------------------------------------*/
+
+
+// event rendering utilities
+function sliceSegs(events, visEventEnds, start, end) {
+ var segs = [],
+ i, len=events.length, event,
+ eventStart, eventEnd,
+ segStart, segEnd,
+ isStart, isEnd;
+ for (i=0; i start && eventStart < end) {
+ if (eventStart < start) {
+ segStart = cloneDate(start);
+ isStart = false;
+ }else{
+ segStart = eventStart;
+ isStart = true;
+ }
+ if (eventEnd > end) {
+ segEnd = cloneDate(end);
+ isEnd = false;
+ }else{
+ segEnd = eventEnd;
+ isEnd = true;
+ }
+ segs.push({
+ event: event,
+ start: segStart,
+ end: segEnd,
+ isStart: isStart,
+ isEnd: isEnd,
+ msLength: segEnd - segStart
+ });
+ }
+ }
+ return segs.sort(segCmp);
+}
+
+
+// event rendering calculation utilities
+function stackSegs(segs) {
+ var levels = [],
+ i, len = segs.length, seg,
+ j, collide, k;
+ for (i=0; i=0; i--) {
+ res = obj[parts[i].toLowerCase()];
+ if (res !== undefined) {
+ return res;
+ }
+ }
+ return obj[''];
+}
+
+
+function htmlEscape(s) {
+ return s.replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/'/g, ''')
+ .replace(/"/g, '"')
+ .replace(/\n/g, ' ');
+}
+
+
+function cssKey(_element) {
+ return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
+}
+
+
+function disableTextSelection(element) {
+ element
+ .attr('unselectable', 'on')
+ .css('MozUserSelect', 'none')
+ .bind('selectstart.ui', function() { return false; });
+}
+
+
+/*
+function enableTextSelection(element) {
+ element
+ .attr('unselectable', 'off')
+ .css('MozUserSelect', '')
+ .unbind('selectstart.ui');
+}
+*/
+
+
+function markFirstLast(e) {
+ e.children()
+ .removeClass('fc-first fc-last')
+ .filter(':first-child')
+ .addClass('fc-first')
+ .end()
+ .filter(':last-child')
+ .addClass('fc-last');
+}
+
+
+function setDayID(cell, date) {
+ cell.each(function(i, _cell) {
+ _cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]);
+ // TODO: make a way that doesn't rely on order of classes
+ });
+}
+
+
+function getSkinCss(event, opt) {
+ var source = event.source || {};
+ var eventColor = event.color;
+ var sourceColor = source.color;
+ var optionColor = opt('eventColor');
+ var backgroundColor =
+ event.backgroundColor ||
+ eventColor ||
+ source.backgroundColor ||
+ sourceColor ||
+ opt('eventBackgroundColor') ||
+ optionColor;
+ var borderColor =
+ event.borderColor ||
+ eventColor ||
+ source.borderColor ||
+ sourceColor ||
+ opt('eventBorderColor') ||
+ optionColor;
+ var textColor =
+ event.textColor ||
+ source.textColor ||
+ opt('eventTextColor');
+ var statements = [];
+ if (backgroundColor) {
+ statements.push('background-color:' + backgroundColor);
+ }
+ if (borderColor) {
+ statements.push('border-color:' + borderColor);
+ }
+ if (textColor) {
+ statements.push('color:' + textColor);
+ }
+ return statements.join(';');
+}
+
+
+function applyAll(functions, thisObj, args) {
+ if ($.isFunction(functions)) {
+ functions = [ functions ];
+ }
+ if (functions) {
+ var i;
+ var ret;
+ for (i=0; i ")
+ .appendTo(element);
+ }
+
+
+
+ function buildTable(showNumbers) {
+ var html = '';
+ var i, j;
+ var headerClass = tm + "-widget-header";
+ var contentClass = tm + "-widget-content";
+ 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";
+
+ if (showWeekNumbers) {
+ html += "" +
+ "
" +
+ " ";
+ }
+
+ for (j=0; j" +
+ "";
+ if (showNumbers) {
+ html += "
" + cellDate.getDate() + "
";
+ }
+ html += "
" +
+ "
" +
+ "";
+ }
+
+ html += "";
+ }
+ html += " " +
+ "
";
+
+ lockHeight(); // the unlock happens later, in setHeight()...
+ if (table) {
+ table.remove();
+ }
+ table = $(html).appendTo(element);
+
+ head = table.find('thead');
+ headCells = head.find('.fc-day-header');
+ body = table.find('tbody');
+ bodyRows = body.find('tr');
+ 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');
+ bodyRows.filter(':last').addClass('fc-last');
+
+ 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) {
+ var date = indexDate(i);
+ trigger('dayRender', t, date, $(_cell));
+ });
+
+ dayBind(bodyCells);
+ }
+
+
+
+ function setHeight(height) {
+ viewHeight = height;
+
+ var bodyHeight = viewHeight - head.height();
+ var rowHeight;
+ var rowHeightLast;
+ var cell;
+
+ if (opt('weekMode') == 'variable') {
+ rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
+ }else{
+ rowHeight = Math.floor(bodyHeight / rowCnt);
+ rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
+ }
+
+ bodyFirstCells.each(function(i, _cell) {
+ if (i < rowCnt) {
+ cell = $(_cell);
+ setMinHeight(
+ cell.find('> div'),
+ (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
+ );
+ }
+ });
+
+ unlockHeight();
+ }
+
+
+ function setWidth(width) {
+ viewWidth = width;
+ colContentPositions.clear();
+
+ weekNumberWidth = 0;
+ if (showWeekNumbers) {
+ weekNumberWidth = head.find('th.fc-week-number').outerWidth();
+ }
+
+ colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt);
+ setOuterWidth(headCells.slice(0, -1), colWidth);
+ }
+
+
+
+ /* Day clicking and binding
+ -----------------------------------------------------------*/
+
+
+ function dayBind(days) {
+ days.click(dayClick)
+ .mousedown(daySelectionMousedown);
+ }
+
+
+ function dayClick(ev) {
+ if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
+ var date = parseISO8601($(this).data('date'));
+ trigger('dayClick', this, date, true, ev);
+ }
+ }
+
+
+
+ /* Semi-transparent Overlay Helpers
+ ------------------------------------------------------*/
+
+
+ function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
+ if (refreshCoordinateGrid) {
+ coordinateGrid.build();
+ }
+ var rowStart = cloneDate(t.visStart);
+ var rowEnd = addDays(cloneDate(rowStart), colCnt);
+ for (var i=0; i" +
+ "" +
+ "";
+
+ if (showWeekNumbers) {
+ s += "";
+ }
+ else {
+ s += "";
+ }
+
+ for (i=0; i "; // fc- needed for setDayID
+ }
+ s +=
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ "";
+ for (i=0; i" + // fc- needed for setDayID
+ "" +
+ "";
+ }
+ s +=
+ " " +
+ " " +
+ " " +
+ "";
+ dayTable = $(s).appendTo(element);
+ dayHead = dayTable.find('thead');
+ dayHeadCells = dayHead.find('th').slice(1, -1);
+ dayBody = dayTable.find('tbody');
+ dayBodyCells = dayBody.find('td').slice(0, -1);
+ dayBodyCellInners = dayBodyCells.find('div.fc-day-content div');
+ dayBodyFirstCell = dayBodyCells.eq(0);
+ dayBodyFirstCellStretcher = dayBodyFirstCell.find('> div');
+
+ markFirstLast(dayHead.add(dayHead.find('tr')));
+ markFirstLast(dayBody.add(dayBody.find('tr')));
+
+ axisFirstCells = dayHead.find('th:first');
+ gutterCells = dayTable.find('.fc-agenda-gutter');
+
+ slotLayer =
+ $("
")
+ .appendTo(element);
+
+ if (opt('allDaySlot')) {
+
+ daySegmentContainer =
+ $("
")
+ .appendTo(slotLayer);
+
+ s =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ " " +
+ "
";
+ allDayTable = $(s).appendTo(slotLayer);
+ allDayRow = allDayTable.find('tr');
+
+ dayBind(allDayRow.find('td'));
+
+ axisFirstCells = axisFirstCells.add(allDayTable.find('th:first'));
+ gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter'));
+
+ slotLayer.append(
+ ""
+ );
+
+ }else{
+
+ daySegmentContainer = $([]); // in jQuery 1.4, we can just do $()
+
+ }
+
+ slotScroller =
+ $("
")
+ .appendTo(slotLayer);
+
+ slotContent =
+ $("
")
+ .appendTo(slotScroller);
+
+ slotSegmentContainer =
+ $("
")
+ .appendTo(slotContent);
+
+ s =
+ "" +
+ "";
+ d = zeroDate();
+ maxd = addMinutes(cloneDate(d), maxMinute);
+ addMinutes(d, minMinute);
+ slotCnt = 0;
+ for (i=0; d < maxd; i++) {
+ minutes = d.getMinutes();
+ s +=
+ "" +
+ "" +
+ "" +
+ "
" +
+ " " +
+ " ";
+ addMinutes(d, opt('slotMinutes'));
+ slotCnt++;
+ }
+ s +=
+ " " +
+ "
";
+ slotTable = $(s).appendTo(slotContent);
+ slotTableFirstInner = slotTable.find('div:first');
+
+ slotBind(slotTable.find('td'));
+
+ axisFirstCells = axisFirstCells.add(slotTable.find('th:first'));
+ }
+
+
+
+ function updateCells() {
+ var i;
+ var headCell;
+ 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 * snapMinutes);
+ }
+ return d;
+ }
+
+
+ function colDate(col) { // returns dates with 00:00:00
+ return addDays(cloneDate(t.visStart), col*dis+dit);
+ }
+
+
+ function cellIsAllDay(cell) {
+ return opt('allDaySlot') && !cell.row;
+ }
+
+
+ function dayOfWeekCol(dayOfWeek) {
+ return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt)*dis+dit;
+ }
+
+
+
+
+ // get the Y coordinate of the given time on the given day (both Date objects)
+ function timePosition(day, time) { // both date objects. day holds 00:00 of current day
+ day = cloneDate(day, true);
+ if (time < addMinutes(cloneDate(day), minMinute)) {
+ return 0;
+ }
+ if (time >= addMinutes(cloneDate(day), maxMinute)) {
+ return slotTable.height();
+ }
+ var slotMinutes = opt('slotMinutes'),
+ minutes = time.getHours()*60 + time.getMinutes() - minMinute,
+ slotI = Math.floor(minutes / slotMinutes),
+ slotTop = slotTopCache[slotI];
+ if (slotTop === undefined) {
+ slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization???
+ }
+ return Math.max(0, Math.round(
+ slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
+ ));
+ }
+
+
+ function allDayBounds() {
+ return {
+ left: axisWidth,
+ right: viewWidth - gutterWidth
+ }
+ }
+
+
+ function getAllDayRow(index) {
+ return allDayRow;
+ }
+
+
+ function defaultEventEnd(event) {
+ var start = cloneDate(event.start);
+ if (event.allDay) {
+ return start;
+ }
+ return addMinutes(start, opt('defaultEventMinutes'));
+ }
+
+
+
+ /* Selection
+ ---------------------------------------------------------------------------------*/
+
+
+ function defaultSelectionEnd(startDate, allDay) {
+ if (allDay) {
+ return cloneDate(startDate);
+ }
+ return addMinutes(cloneDate(startDate), opt('slotMinutes'));
+ }
+
+
+ function renderSelection(startDate, endDate, allDay) { // only for all-day
+ if (allDay) {
+ if (opt('allDaySlot')) {
+ renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true);
+ }
+ }else{
+ renderSlotSelection(startDate, endDate);
+ }
+ }
+
+
+ function renderSlotSelection(startDate, endDate) {
+ var helperOption = opt('selectHelper');
+ coordinateGrid.build();
+ if (helperOption) {
+ var col = dayDiff(startDate, t.visStart) * dis + dit;
+ if (col >= 0 && col < colCnt) { // only works when times are on same day
+ var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only for horizontal coords
+ var top = timePosition(startDate, startDate);
+ var bottom = timePosition(startDate, endDate);
+ if (bottom > top) { // protect against selections that are entirely before or after visible range
+ rect.top = top;
+ rect.height = bottom - top;
+ rect.left += 2;
+ rect.width -= 5;
+ if ($.isFunction(helperOption)) {
+ var helperRes = helperOption(startDate, endDate);
+ if (helperRes) {
+ rect.position = 'absolute';
+ rect.zIndex = 8;
+ selectionHelper = $(helperRes)
+ .css(rect)
+ .appendTo(slotContent);
+ }
+ }else{
+ rect.isStart = true; // conside rect a "seg" now
+ rect.isEnd = true; //
+ selectionHelper = $(slotSegHtml(
+ {
+ title: '',
+ start: startDate,
+ end: endDate,
+ className: ['fc-select-helper'],
+ editable: false
+ },
+ rect
+ ));
+ selectionHelper.css('opacity', opt('dragOpacity'));
+ }
+ if (selectionHelper) {
+ slotBind(selectionHelper);
+ slotContent.append(selectionHelper);
+ setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
+ setOuterHeight(selectionHelper, rect.height, true);
+ }
+ }
+ }
+ }else{
+ renderSlotOverlay(startDate, endDate);
+ }
+ }
+
+
+ function clearSelection() {
+ clearOverlays();
+ if (selectionHelper) {
+ selectionHelper.remove();
+ selectionHelper = null;
+ }
+ }
+
+
+ function slotSelectionMousedown(ev) {
+ if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button
+ unselect(ev);
+ var dates;
+ hoverListener.start(function(cell, origCell) {
+ clearSelection();
+ if (cell && cell.col == origCell.col && !cellIsAllDay(cell)) {
+ var d1 = cellDate(origCell);
+ var d2 = cellDate(cell);
+ dates = [
+ d1,
+ addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes
+ d2,
+ addMinutes(cloneDate(d2), snapMinutes)
+ ].sort(cmp);
+ renderSlotSelection(dates[0], dates[3]);
+ }else{
+ dates = null;
+ }
+ }, ev);
+ $(document).one('mouseup', function(ev) {
+ hoverListener.stop();
+ if (dates) {
+ if (+dates[0] == +dates[1]) {
+ reportDayClick(dates[0], false, ev);
+ }
+ reportSelection(dates[0], dates[3], false, ev);
+ }
+ });
+ }
+ }
+
+
+ function reportDayClick(date, allDay, ev) {
+ trigger('dayClick', dayBodyCells[dayOfWeekCol(date.getDay())], date, allDay, ev);
+ }
+
+
+
+ /* External Dragging
+ --------------------------------------------------------------------------------*/
+
+
+ function dragStart(_dragElement, ev, ui) {
+ hoverListener.start(function(cell) {
+ clearOverlays();
+ if (cell) {
+ if (cellIsAllDay(cell)) {
+ renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
+ }else{
+ var d1 = cellDate(cell);
+ var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
+ renderSlotOverlay(d1, d2);
+ }
+ }
+ }, ev);
+ }
+
+
+ function dragStop(_dragElement, ev, ui) {
+ var cell = hoverListener.stop();
+ clearOverlays();
+ if (cell) {
+ trigger('drop', _dragElement, cellDate(cell), cellIsAllDay(cell), ev, ui);
+ }
+ }
+
+
+}
+
+;;
+
+function AgendaEventRenderer() {
+ var t = this;
+
+
+ // exports
+ t.renderEvents = renderEvents;
+ t.compileDaySegs = compileDaySegs; // for DayEventRenderer
+ t.clearEvents = clearEvents;
+ t.slotSegHtml = slotSegHtml;
+ t.bindDaySeg = bindDaySeg;
+
+
+ // imports
+ DayEventRenderer.call(t);
+ var opt = t.opt;
+ var trigger = t.trigger;
+ //var setOverflowHidden = t.setOverflowHidden;
+ var isEventDraggable = t.isEventDraggable;
+ var isEventResizable = t.isEventResizable;
+ var eventEnd = t.eventEnd;
+ var reportEvents = t.reportEvents;
+ var reportEventClear = t.reportEventClear;
+ var eventElementHandlers = t.eventElementHandlers;
+ var setHeight = t.setHeight;
+ var getDaySegmentContainer = t.getDaySegmentContainer;
+ var getSlotSegmentContainer = t.getSlotSegmentContainer;
+ var getHoverListener = t.getHoverListener;
+ var getMaxMinute = t.getMaxMinute;
+ var getMinMinute = t.getMinMinute;
+ var timePosition = t.timePosition;
+ var colContentLeft = t.colContentLeft;
+ var colContentRight = t.colContentRight;
+ var renderDaySegs = t.renderDaySegs;
+ var resizableDayEvent = t.resizableDayEvent; // TODO: streamline binding architecture
+ var getColCnt = t.getColCnt;
+ var getColWidth = t.getColWidth;
+ var getSnapHeight = t.getSnapHeight;
+ var getSnapMinutes = t.getSnapMinutes;
+ var getBodyContent = t.getBodyContent;
+ var reportEventElement = t.reportEventElement;
+ var showEvents = t.showEvents;
+ var hideEvents = t.hideEvents;
+ var eventDrop = t.eventDrop;
+ var eventResize = t.eventResize;
+ var renderDayOverlay = t.renderDayOverlay;
+ var clearOverlays = t.clearOverlays;
+ var calendar = t.calendar;
+ var formatDate = calendar.formatDate;
+ var formatDates = calendar.formatDates;
+
+
+
+ /* Rendering
+ ----------------------------------------------------------------------------*/
+
+
+ function renderEvents(events, modifiedEventId) {
+ reportEvents(events);
+ var i, len=events.length,
+ dayEvents=[],
+ slotEvents=[];
+ for (i=0; i" +
+ "" +
+ "
" +
+ htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
+ "
" +
+ "
" +
+ htmlEscape(event.title) +
+ "
" +
+ "
" +
+ "
";
+ if (seg.isEnd && isEventResizable(event)) {
+ html +=
+ "=
";
+ }
+ html +=
+ "" + (url ? "a" : "div") + ">";
+ return html;
+ }
+
+
+ function bindDaySeg(event, eventElement, seg) {
+ if (isEventDraggable(event)) {
+ draggableDayEvent(event, eventElement, seg.isStart);
+ }
+ if (seg.isEnd && isEventResizable(event)) {
+ resizableDayEvent(event, eventElement, seg);
+ }
+ eventElementHandlers(event, eventElement);
+ // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
+ }
+
+
+ function bindSlotSeg(event, eventElement, seg) {
+ var timeElement = eventElement.find('div.fc-event-time');
+ if (isEventDraggable(event)) {
+ draggableSlotEvent(event, eventElement, timeElement);
+ }
+ if (seg.isEnd && isEventResizable(event)) {
+ resizableSlotEvent(event, eventElement, timeElement);
+ }
+ eventElementHandlers(event, eventElement);
+ }
+
+
+
+ /* Dragging
+ -----------------------------------------------------------------------------------*/
+
+
+ // when event starts out FULL-DAY
+
+ function draggableDayEvent(event, eventElement, isStart) {
+ var origWidth;
+ var revert;
+ var allDay=true;
+ var dayDelta;
+ var dis = opt('isRTL') ? -1 : 1;
+ var hoverListener = getHoverListener();
+ var colWidth = getColWidth();
+ var snapHeight = getSnapHeight();
+ var snapMinutes = getSnapMinutes();
+ var minMinute = getMinMinute();
+ eventElement.draggable({
+ zIndex: 9,
+ opacity: opt('dragOpacity', 'month'), // use whatever the month view was using
+ revertDuration: opt('dragRevertDuration'),
+ start: function(ev, ui) {
+ trigger('eventDragStart', eventElement, event, ev, ui);
+ hideEvents(event, eventElement);
+ origWidth = eventElement.width();
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+ clearOverlays();
+ if (cell) {
+ //setOverflowHidden(true);
+ revert = false;
+ dayDelta = colDelta * dis;
+ if (!cell.row) {
+ // on full-days
+ renderDayOverlay(
+ addDays(cloneDate(event.start), dayDelta),
+ addDays(exclEndDay(event), dayDelta)
+ );
+ resetElement();
+ }else{
+ // mouse is over bottom slots
+ if (isStart) {
+ if (allDay) {
+ // convert event to temporary slot-event
+ eventElement.width(colWidth - 10); // don't use entire width
+ setOuterHeight(
+ eventElement,
+ snapHeight * Math.round(
+ (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) /
+ snapMinutes
+ )
+ );
+ eventElement.draggable('option', 'grid', [colWidth, 1]);
+ allDay = false;
+ }
+ }else{
+ revert = true;
+ }
+ }
+ revert = revert || (allDay && !dayDelta);
+ }else{
+ resetElement();
+ //setOverflowHidden(false);
+ revert = true;
+ }
+ eventElement.draggable('option', 'revert', revert);
+ }, ev, 'drag');
+ },
+ stop: function(ev, ui) {
+ hoverListener.stop();
+ clearOverlays();
+ trigger('eventDragStop', eventElement, event, ev, ui);
+ if (revert) {
+ // hasn't moved or is out of bounds (draggable has already reverted)
+ resetElement();
+ eventElement.css('filter', ''); // clear IE opacity side-effects
+ showEvents(event, eventElement);
+ }else{
+ // changed!
+ var minuteDelta = 0;
+ if (!allDay) {
+ minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / snapHeight)
+ * snapMinutes
+ + minMinute
+ - (event.start.getHours() * 60 + event.start.getMinutes());
+ }
+ eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
+ }
+ //setOverflowHidden(false);
+ }
+ });
+ function resetElement() {
+ if (!allDay) {
+ eventElement
+ .width(origWidth)
+ .height('')
+ .draggable('option', 'grid', null);
+ allDay = true;
+ }
+ }
+ }
+
+
+ // when event starts out IN TIMESLOTS
+
+ function draggableSlotEvent(event, eventElement, timeElement) {
+ var origPosition;
+ var allDay=false;
+ var dayDelta;
+ var minuteDelta;
+ var prevMinuteDelta;
+ var dis = opt('isRTL') ? -1 : 1;
+ var hoverListener = getHoverListener();
+ var colCnt = getColCnt();
+ var colWidth = getColWidth();
+ var snapHeight = getSnapHeight();
+ var snapMinutes = getSnapMinutes();
+ eventElement.draggable({
+ zIndex: 9,
+ scroll: false,
+ grid: [colWidth, snapHeight],
+ axis: colCnt==1 ? 'y' : false,
+ opacity: opt('dragOpacity'),
+ revertDuration: opt('dragRevertDuration'),
+ start: function(ev, ui) {
+ trigger('eventDragStart', eventElement, event, ev, ui);
+ hideEvents(event, eventElement);
+ origPosition = eventElement.position();
+ minuteDelta = prevMinuteDelta = 0;
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+ eventElement.draggable('option', 'revert', !cell);
+ clearOverlays();
+ if (cell) {
+ dayDelta = colDelta * dis;
+ if (opt('allDaySlot') && !cell.row) {
+ // over full days
+ if (!allDay) {
+ // convert to temporary all-day event
+ allDay = true;
+ timeElement.hide();
+ eventElement.draggable('option', 'grid', null);
+ }
+ renderDayOverlay(
+ addDays(cloneDate(event.start), dayDelta),
+ addDays(exclEndDay(event), dayDelta)
+ );
+ }else{
+ // on slots
+ resetElement();
+ }
+ }
+ }, ev, 'drag');
+ },
+ drag: function(ev, ui) {
+ minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes;
+ if (minuteDelta != prevMinuteDelta) {
+ if (!allDay) {
+ updateTimeText(minuteDelta);
+ }
+ prevMinuteDelta = minuteDelta;
+ }
+ },
+ stop: function(ev, ui) {
+ var cell = hoverListener.stop();
+ clearOverlays();
+ trigger('eventDragStop', eventElement, event, ev, ui);
+ if (cell && (dayDelta || minuteDelta || allDay)) {
+ // changed!
+ eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui);
+ }else{
+ // either no change or out-of-bounds (draggable has already reverted)
+ resetElement();
+ eventElement.css('filter', ''); // clear IE opacity side-effects
+ eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
+ updateTimeText(0);
+ showEvents(event, eventElement);
+ }
+ }
+ });
+ function updateTimeText(minuteDelta) {
+ var newStart = addMinutes(cloneDate(event.start), minuteDelta);
+ var newEnd;
+ if (event.end) {
+ newEnd = addMinutes(cloneDate(event.end), minuteDelta);
+ }
+ timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
+ }
+ function resetElement() {
+ // convert back to original slot-event
+ if (allDay) {
+ timeElement.css('display', ''); // show() was causing display=inline
+ eventElement.draggable('option', 'grid', [colWidth, snapHeight]);
+ allDay = false;
+ }
+ }
+ }
+
+
+
+ /* Resizing
+ --------------------------------------------------------------------------------------*/
+
+
+ function resizableSlotEvent(event, eventElement, timeElement) {
+ var snapDelta, prevSnapDelta;
+ var snapHeight = getSnapHeight();
+ var snapMinutes = getSnapMinutes();
+ eventElement.resizable({
+ handles: {
+ s: '.ui-resizable-handle'
+ },
+ grid: snapHeight,
+ start: function(ev, ui) {
+ 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
+ snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight);
+ if (snapDelta != prevSnapDelta) {
+ timeElement.text(
+ formatDates(
+ event.start,
+ (!snapDelta && !event.end) ? null : // no change, so don't display time range
+ addMinutes(eventEnd(event), snapMinutes*snapDelta),
+ opt('timeFormat')
+ )
+ );
+ prevSnapDelta = snapDelta;
+ }
+ },
+ stop: function(ev, ui) {
+ trigger('eventResizeStop', this, event, ev, ui);
+ if (snapDelta) {
+ eventResize(this, event, 0, snapMinutes*snapDelta, ev, ui);
+ }else{
+ eventElement.css('z-index', 8);
+ showEvents(event, eventElement);
+ // BUG: if event was really short, need to put title back in span
+ }
+ }
+ });
+ }
+
+
+}
+
+
+function countForwardSegs(levels) {
+ var i, j, k, level, segForward, segBack;
+ for (i=levels.length-1; i>0; i--) {
+ level = levels[i];
+ for (j=0; j");
+ var elements;
+ var segmentContainer = getDaySegmentContainer();
+ var i;
+ var segCnt = segs.length;
+ var element;
+ tempContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
+ elements = tempContainer.children();
+ segmentContainer.append(elements);
+ daySegElementResolve(segs, elements);
+ daySegCalcHSides(segs);
+ daySegSetWidths(segs);
+ daySegCalcHeights(segs);
+ daySegSetTops(segs, getRowTops(getRowDivs()));
+ elements = [];
+ for (i=0; i" +
+ "";
+ if (!event.allDay && seg.isStart) {
+ html +=
+ "" +
+ htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
+ " ";
+ }
+ html +=
+ "" + htmlEscape(event.title) + " " +
+ "
";
+ if (seg.isEnd && isEventResizable(event)) {
+ html +=
+ "" +
+ " " + // makes hit area a lot better for IE6/7
+ "
";
+ }
+ html +=
+ "" + (url ? "a" : "div" ) + ">";
+ seg.left = left;
+ seg.outerWidth = right - left;
+ seg.startCol = leftCol;
+ seg.endCol = rightCol + 1; // needs to be exclusive
+ }
+ return html;
+ }
+
+
+ function daySegElementResolve(segs, elements) { // sets seg.element
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var event;
+ var element;
+ var triggerRes;
+ for (i=0; i div'); // optimal selector?
+ }
+ return rowDivs;
+ }
+
+
+ function getRowTops(rowDivs) {
+ var i;
+ var rowCnt = rowDivs.length;
+ var tops = [];
+ for (i=0; i selection for IE
+ element
+ .mousedown(function(ev) { // prevent native selection for others
+ ev.preventDefault();
+ })
+ .click(function(ev) {
+ if (isResizing) {
+ ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
+ ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
+ // (eventElementHandlers needs to be bound after resizableDayEvent)
+ }
+ });
+
+ handle.mousedown(function(ev) {
+ if (ev.which != 1) {
+ return; // needs to be left mouse button
+ }
+ isResizing = true;
+ var hoverListener = t.getHoverListener();
+ var rowCnt = getRowCnt();
+ var colCnt = getColCnt();
+ var dis = rtl ? -1 : 1;
+ var dit = rtl ? colCnt-1 : 0;
+ var elementTop = element.css('top');
+ var dayDelta;
+ var helpers;
+ var eventCopy = $.extend({}, event);
+ var minCell = dateCell(event.start);
+ clearSelection();
+ $('body')
+ .css('cursor', direction + '-resize')
+ .one('mouseup', mouseup);
+ trigger('eventResizeStart', this, event, ev);
+ hoverListener.start(function(cell, origCell) {
+ if (cell) {
+ var r = Math.max(minCell.row, cell.row);
+ var c = cell.col;
+ if (rowCnt == 1) {
+ r = 0; // hack for all-day area in agenda views
+ }
+ if (r == minCell.row) {
+ if (rtl) {
+ c = Math.min(minCell.col, c);
+ }else{
+ c = Math.max(minCell.col, c);
+ }
+ }
+ dayDelta = (r*7 + c*dis+dit) - (origCell.row*7 + origCell.col*dis+dit);
+ var newEnd = addDays(eventEnd(event), dayDelta, true);
+ if (dayDelta) {
+ eventCopy.end = newEnd;
+ var oldHelpers = helpers;
+ helpers = renderTempDaySegs(compileDaySegs([eventCopy]), seg.row, elementTop);
+ helpers.find('*').css('cursor', direction + '-resize');
+ if (oldHelpers) {
+ oldHelpers.remove();
+ }
+ hideEvents(event);
+ }else{
+ if (helpers) {
+ showEvents(event);
+ helpers.remove();
+ helpers = null;
+ }
+ }
+ clearOverlays();
+ renderDayOverlay(event.start, addDays(cloneDate(newEnd), 1)); // coordinate grid already rebuild at hoverListener.start
+ }
+ }, ev);
+
+ function mouseup(ev) {
+ trigger('eventResizeStop', this, event, ev);
+ $('body').css('cursor', '');
+ hoverListener.stop();
+ clearOverlays();
+ if (dayDelta) {
+ eventResize(this, event, dayDelta, 0, ev);
+ // event redraw will clear helpers
+ }
+ // otherwise, the drag handler already restored the old events
+
+ setTimeout(function() { // make this happen after the element's click event
+ isResizing = false;
+ },0);
+ }
+
+ });
+ }
+
+
+}
+
+;;
+
+//BUG: unselect needs to be triggered when events are dragged+dropped
+
+function SelectionManager() {
+ var t = this;
+
+
+ // exports
+ t.select = select;
+ t.unselect = unselect;
+ t.reportSelection = reportSelection;
+ t.daySelectionMousedown = daySelectionMousedown;
+
+
+ // imports
+ var opt = t.opt;
+ var trigger = t.trigger;
+ var defaultSelectionEnd = t.defaultSelectionEnd;
+ var renderSelection = t.renderSelection;
+ var clearSelection = t.clearSelection;
+
+
+ // locals
+ var selected = false;
+
+
+
+ // unselectAuto
+ if (opt('selectable') && opt('unselectAuto')) {
+ $(document).mousedown(function(ev) {
+ var ignore = opt('unselectCancel');
+ if (ignore) {
+ if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match
+ return;
+ }
+ }
+ unselect(ev);
+ });
+ }
+
+
+ function select(startDate, endDate, allDay) {
+ unselect();
+ if (!endDate) {
+ endDate = defaultSelectionEnd(startDate, allDay);
+ }
+ renderSelection(startDate, endDate, allDay);
+ reportSelection(startDate, endDate, allDay);
+ }
+
+
+ function unselect(ev) {
+ if (selected) {
+ selected = false;
+ clearSelection();
+ trigger('unselect', null, ev);
+ }
+ }
+
+
+ function reportSelection(startDate, endDate, allDay, ev) {
+ selected = true;
+ trigger('select', null, startDate, endDate, allDay, ev);
+ }
+
+
+ function daySelectionMousedown(ev) { // not really a generic manager method, oh well
+ var cellDate = t.cellDate;
+ var cellIsAllDay = t.cellIsAllDay;
+ var hoverListener = t.getHoverListener();
+ var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
+ if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
+ unselect(ev);
+ var _mousedownElement = this;
+ var dates;
+ hoverListener.start(function(cell, origCell) { // TODO: maybe put cellDate/cellIsAllDay info in cell
+ clearSelection();
+ if (cell && cellIsAllDay(cell)) {
+ dates = [ cellDate(origCell), cellDate(cell) ].sort(cmp);
+ renderSelection(dates[0], dates[1], true);
+ }else{
+ dates = null;
+ }
+ }, ev);
+ $(document).one('mouseup', function(ev) {
+ hoverListener.stop();
+ if (dates) {
+ if (+dates[0] == +dates[1]) {
+ reportDayClick(dates[0], true, ev);
+ }
+ reportSelection(dates[0], dates[1], true, ev);
+ }
+ });
+ }
+ }
+
+
+}
+
+;;
+
+function OverlayManager() {
+ var t = this;
+
+
+ // exports
+ t.renderOverlay = renderOverlay;
+ t.clearOverlays = clearOverlays;
+
+
+ // locals
+ var usedOverlays = [];
+ var unusedOverlays = [];
+
+
+ function renderOverlay(rect, parent) {
+ var e = unusedOverlays.shift();
+ if (!e) {
+ e = $("
");
+ }
+ if (e[0].parentNode != parent[0]) {
+ e.appendTo(parent);
+ }
+ usedOverlays.push(e.css(rect).show());
+ return e;
+ }
+
+
+ function clearOverlays() {
+ var e;
+ while (e = usedOverlays.shift()) {
+ unusedOverlays.push(e.hide().unbind());
+ }
+ }
+
+
+}
+
+;;
+
+function CoordinateGrid(buildFunc) {
+
+ var t = this;
+ var rows;
+ var cols;
+
+
+ t.build = function() {
+ rows = [];
+ cols = [];
+ buildFunc(rows, cols);
+ };
+
+
+ t.cell = function(x, y) {
+ var rowCnt = rows.length;
+ var colCnt = cols.length;
+ var i, r=-1, c=-1;
+ for (i=0; i= rows[i][0] && y < rows[i][1]) {
+ r = i;
+ break;
+ }
+ }
+ for (i=0; i= cols[i][0] && x < cols[i][1]) {
+ c = i;
+ break;
+ }
+ }
+ return (r>=0 && c>=0) ? { row:r, col:c } : null;
+ };
+
+
+ t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive
+ var origin = originElement.offset();
+ return {
+ top: rows[row0][0] - origin.top,
+ left: cols[col0][0] - origin.left,
+ width: cols[col1][1] - cols[col0][0],
+ height: rows[row1][1] - rows[row0][0]
+ };
+ };
+
+}
+
+;;
+
+function HoverListener(coordinateGrid) {
+
+
+ var t = this;
+ var bindType;
+ var change;
+ var firstCell;
+ var cell;
+
+
+ t.start = function(_change, ev, _bindType) {
+ change = _change;
+ firstCell = cell = null;
+ coordinateGrid.build();
+ mouse(ev);
+ bindType = _bindType || 'mousemove';
+ $(document).bind(bindType, mouse);
+ };
+
+
+ function mouse(ev) {
+ _fixUIEvent(ev); // see below
+ var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
+ if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
+ if (newCell) {
+ if (!firstCell) {
+ firstCell = newCell;
+ }
+ change(newCell, firstCell, newCell.row-firstCell.row, newCell.col-firstCell.col);
+ }else{
+ change(newCell, firstCell);
+ }
+ cell = newCell;
+ }
+ }
+
+
+ t.stop = function() {
+ $(document).unbind(bindType, mouse);
+ return cell;
+ };
+
+
+}
+
+
+
+// this fix was only necessary for jQuery UI 1.8.16 (and jQuery 1.7 or 1.7.1)
+// upgrading to jQuery UI 1.8.17 (and using either jQuery 1.7 or 1.7.1) fixed the problem
+// but keep this in here for 1.8.16 users
+// and maybe remove it down the line
+
+function _fixUIEvent(event) { // for issue 1168
+ if (event.pageX === undefined) {
+ event.pageX = event.originalEvent.pageX;
+ event.pageY = event.originalEvent.pageY;
+ }
+}
+;;
+
+function HorizontalPositionCache(getElement) {
+
+ var t = this,
+ elements = {},
+ lefts = {},
+ rights = {};
+
+ function e(i) {
+ return elements[i] = elements[i] || getElement(i);
+ }
+
+ t.left = function(i) {
+ return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i];
+ };
+
+ t.right = function(i) {
+ return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i];
+ };
+
+ t.clear = function() {
+ elements = {};
+ lefts = {};
+ rights = {};
+ };
+
+}
+
+;;
+
+})(jQuery);
\ No newline at end of file
diff --git a/3rdparty/fullcalendar/js/fullcalendar.min.js b/3rdparty/fullcalendar/js/fullcalendar.min.js
new file mode 100644
index 00000000..c3e0834a
--- /dev/null
+++ b/3rdparty/fullcalendar/js/fullcalendar.min.js
@@ -0,0 +1,7 @@
+/*!
+ * FullCalendar v1.6.1
+ * Docs & License: http://arshaw.com/fullcalendar/
+ * (c) 2013 Adam Shaw
+ */
+(function(t,e){function n(e){t.extend(!0,ye,e)}function r(n,r,l){function u(t){G?(S(),C(),R(),b(t)):f()}function f(){K=r.theme?"ui":"fc",n.addClass("fc"),r.isRTL?n.addClass("fc-rtl"):n.addClass("fc-ltr"),r.theme&&n.addClass("ui-widget"),G=t("
").prependTo(n),$=new a(Z,r),Q=$.render(),Q&&n.prepend(Q),y(r.defaultView),t(window).resize(x),m()||v()}function v(){setTimeout(function(){!te.start&&m()&&b()},0)}function h(){t(window).unbind("resize",x),$.destroy(),G.remove(),n.removeClass("fc fc-rtl ui-widget")}function g(){return 0!==se.offsetWidth}function m(){return 0!==t("body")[0].offsetWidth}function y(e){if(!te||e!=te.name){ue++,W();var n,r=te;r?((r.beforeHide||I)(),q(G,G.height()),r.element.hide()):q(G,1),G.css("overflow","hidden"),te=ce[e],te?te.element.show():te=ce[e]=new De[e](n=re=t("
").appendTo(G),Z),r&&$.deactivateButton(r.name),$.activateButton(e),b(),G.css("overflow",""),r&&q(G,1),n||(te.afterShow||I)(),ue--}}function b(t){if(g()){ue++,W(),ne===e&&S();var r=!1;!te.start||t||te.start>fe||fe>=te.end?(te.render(fe,t||0),E(!0),r=!0):te.sizeDirty?(te.clearEvents(),E(),r=!0):te.eventsDirty&&(te.clearEvents(),r=!0),te.sizeDirty=!1,te.eventsDirty=!1,T(r),ee=n.outerWidth(),$.updateTitle(te.title);var a=new Date;a>=te.start&&te.end>a?$.disableButton("today"):$.enableButton("today"),ue--,te.trigger("viewDisplay",se)}}function M(){C(),g()&&(S(),E(),W(),te.clearEvents(),te.renderEvents(de),te.sizeDirty=!1)}function C(){t.each(ce,function(t,e){e.sizeDirty=!0})}function S(){ne=r.contentHeight?r.contentHeight:r.height?r.height-(Q?Q.height():0)-L(G):Math.round(G.width()/Math.max(r.aspectRatio,.5))}function E(t){ue++,te.setHeight(ne,t),re&&(re.css("position","relative"),re=null),te.setWidth(G.width(),t),ue--}function x(){if(!ue)if(te.start){var t=++le;setTimeout(function(){t==le&&!ue&&g()&&ee!=(ee=n.outerWidth())&&(ue++,M(),te.trigger("windowResize",se),ue--)},200)}else v()}function T(t){!r.lazyFetching||oe(te.visStart,te.visEnd)?k():t&&F()}function k(){ie(te.visStart,te.visEnd)}function H(t){de=t,F()}function z(t){F(t)}function F(t){R(),g()&&(te.clearEvents(),te.renderEvents(de,t),te.eventsDirty=!1)}function R(){t.each(ce,function(t,e){e.eventsDirty=!0})}function N(t,n,r){te.select(t,n,r===e?!0:r)}function W(){te&&te.unselect()}function A(){b(-1)}function _(){b(1)}function O(){i(fe,-1),b()}function B(){i(fe,1),b()}function Y(){fe=new Date,b()}function j(t,e,n){t instanceof Date?fe=d(t):p(fe,t,e,n),b()}function P(t,n,r){t!==e&&i(fe,t),n!==e&&s(fe,n),r!==e&&c(fe,r),b()}function J(){return d(fe)}function V(){return te}function X(t,n){return n===e?r[t]:(("height"==t||"contentHeight"==t||"aspectRatio"==t)&&(r[t]=n,M()),e)}function U(t,n){return r[t]?r[t].apply(n||se,Array.prototype.slice.call(arguments,2)):e}var Z=this;Z.options=r,Z.render=u,Z.destroy=h,Z.refetchEvents=k,Z.reportEvents=H,Z.reportEventChange=z,Z.rerenderEvents=F,Z.changeView=y,Z.select=N,Z.unselect=W,Z.prev=A,Z.next=_,Z.prevYear=O,Z.nextYear=B,Z.today=Y,Z.gotoDate=j,Z.incrementDate=P,Z.formatDate=function(t,e){return w(t,e,r)},Z.formatDates=function(t,e,n){return D(t,e,n,r)},Z.getDate=J,Z.getView=V,Z.option=X,Z.trigger=U,o.call(Z,r,l);var $,Q,G,K,te,ee,ne,re,ae,oe=Z.isFetchNeeded,ie=Z.fetchEvents,se=n[0],ce={},le=0,ue=0,fe=new Date,de=[];p(fe,r.year,r.month,r.date),r.droppable&&t(document).bind("dragstart",function(e,n){var a=e.target,o=t(a);if(!o.parents(".fc").length){var i=r.dropAccept;(t.isFunction(i)?i.call(a,o):o.is(i))&&(ae=a,te.dragStart(ae,e,n))}}).bind("dragstop",function(t,e){ae&&(te.dragStop(ae,t,e),ae=null)})}function a(n,r){function a(){v=r.theme?"ui":"fc";var n=r.header;return n?h=t("").append(t(" ").append(i("left")).append(i("center")).append(i("right"))):e}function o(){h.remove()}function i(e){var a=t(""),o=r.header[e];return o&&t.each(o.split(" "),function(e){e>0&&a.append("");var o;t.each(this.split(","),function(e,i){if("title"==i)a.append(""),o&&o.addClass(v+"-corner-right"),o=null;else{var s;if(n[i]?s=n[i]:De[i]&&(s=function(){u.removeClass(v+"-state-hover"),n.changeView(i)}),s){var c=r.theme?J(r.buttonIcons,i):null,l=J(r.buttonText,i),u=t(""+(c?" "+" ":l)+" ").click(function(){u.hasClass(v+"-state-disabled")||s()}).mousedown(function(){u.not("."+v+"-state-active").not("."+v+"-state-disabled").addClass(v+"-state-down")}).mouseup(function(){u.removeClass(v+"-state-down")}).hover(function(){u.not("."+v+"-state-active").not("."+v+"-state-disabled").addClass(v+"-state-hover")},function(){u.removeClass(v+"-state-hover").removeClass(v+"-state-down")}).appendTo(a);U(u),o||u.addClass(v+"-corner-left"),o=u}}}),o&&o.addClass(v+"-corner-right")}),a}function s(t){h.find("h2").html(t)}function c(t){h.find("span.fc-button-"+t).addClass(v+"-state-active")}function l(t){h.find("span.fc-button-"+t).removeClass(v+"-state-active")}function u(t){h.find("span.fc-button-"+t).addClass(v+"-state-disabled")}function f(t){h.find("span.fc-button-"+t).removeClass(v+"-state-disabled")}var d=this;d.render=a,d.destroy=o,d.updateTitle=s,d.activateButton=c,d.deactivateButton=l,d.disableButton=u,d.enableButton=f;var v,h=t([])}function o(n,r){function a(t,e){return!S||S>t||e>E}function o(t,e){S=t,E=e,W=[];var n=++F,r=z.length;R=r;for(var a=0;r>a;a++)i(z[a],n)}function i(e,r){s(e,function(a){if(r==F){if(a){n.eventDataTransform&&(a=t.map(a,n.eventDataTransform)),e.eventDataTransform&&(a=t.map(a,e.eventDataTransform));for(var o=0;a.length>o;o++)a[o].source=e,b(a[o]);W=W.concat(a)}R--,R||k(W)}})}function s(r,a){var o,i,c=we.sourceFetchers;for(o=0;c.length>o;o++){if(i=c[o](r,S,E,a),i===!0)return;if("object"==typeof i)return s(i,a),e}var l=r.events;if(l)t.isFunction(l)?(p(),l(d(S),d(E),function(t){a(t),y()})):t.isArray(l)?a(l):a();else{var u=r.url;if(u){var f=r.success,v=r.error,h=r.complete,g=t.extend({},r.data||{}),m=K(r.startParam,n.startParam),b=K(r.endParam,n.endParam);m&&(g[m]=Math.round(+S/1e3)),b&&(g[b]=Math.round(+E/1e3)),p(),t.ajax(t.extend({},Me,r,{data:g,success:function(e){e=e||[];var n=G(f,this,arguments);t.isArray(n)&&(e=n),a(e)},error:function(){G(v,this,arguments),a()},complete:function(){G(h,this,arguments),y()}}))}else a()}}function c(t){t=l(t),t&&(R++,i(t,F))}function l(n){return t.isFunction(n)||t.isArray(n)?n={events:n}:"string"==typeof n&&(n={url:n}),"object"==typeof n?(w(n),z.push(n),n):e}function u(e){z=t.grep(z,function(t){return!D(t,e)}),W=t.grep(W,function(t){return!D(t.source,e)}),k(W)}function f(t){var e,n,r=W.length,a=T().defaultEventEnd,o=t.start-t._start,i=t.end?t.end-(t._end||a(t)):0;for(e=0;r>e;e++)n=W[e],n._id==t._id&&n!=t&&(n.start=new Date(+n.start+o),n.end=t.end?n.end?new Date(+n.end+i):new Date(+a(n)+i):null,n.title=t.title,n.url=t.url,n.allDay=t.allDay,n.className=t.className,n.editable=t.editable,n.color=t.color,n.backgroudColor=t.backgroudColor,n.borderColor=t.borderColor,n.textColor=t.textColor,b(n));b(t),k(W)}function v(t,e){b(t),t.source||(e&&(H.events.push(t),t.source=H),W.push(t)),k(W)}function h(e){if(e){if(!t.isFunction(e)){var n=e+"";e=function(t){return t._id==n}}W=t.grep(W,e,!0);for(var r=0;z.length>r;r++)t.isArray(z[r].events)&&(z[r].events=t.grep(z[r].events,e,!0))}else{W=[];for(var r=0;z.length>r;r++)t.isArray(z[r].events)&&(z[r].events=[])}k(W)}function g(e){return t.isFunction(e)?t.grep(W,e):e?(e+="",t.grep(W,function(t){return t._id==e})):W}function p(){N++||x("loading",null,!0)}function y(){--N||x("loading",null,!1)}function b(t){var r=t.source||{},a=K(r.ignoreTimezone,n.ignoreTimezone);t._id=t._id||(t.id===e?"_fc"+Ce++:t.id+""),t.date&&(t.start||(t.start=t.date),delete t.date),t._start=d(t.start=m(t.start,a)),t.end=m(t.end,a),t.end&&t.end<=t.start&&(t.end=null),t._end=t.end?d(t.end):null,t.allDay===e&&(t.allDay=K(r.allDayDefault,n.allDayDefault)),t.className?"string"==typeof t.className&&(t.className=t.className.split(/\s+/)):t.className=[]}function w(t){t.className?"string"==typeof t.className&&(t.className=t.className.split(/\s+/)):t.className=[];for(var e=we.sourceNormalizers,n=0;e.length>n;n++)e[n](t)}function D(t,e){return t&&e&&M(t)==M(e)}function M(t){return("object"==typeof t?t.events||t.url:"")||t}var C=this;C.isFetchNeeded=a,C.fetchEvents=o,C.addEventSource=c,C.removeEventSource=u,C.updateEvent=f,C.renderEvent=v,C.removeEvents=h,C.clientEvents=g,C.normalizeEvent=b;for(var S,E,x=C.trigger,T=C.getView,k=C.reportEvents,H={events:[]},z=[H],F=0,R=0,N=0,W=[],A=0;r.length>A;A++)l(r[A])}function i(t,e,n){return t.setFullYear(t.getFullYear()+e),n||f(t),t}function s(t,e,n){if(+t){var r=t.getMonth()+e,a=d(t);for(a.setDate(1),a.setMonth(r),t.setMonth(r),n||f(t);t.getMonth()!=a.getMonth();)t.setDate(t.getDate()+(a>t?1:-1))}return t}function c(t,e,n){if(+t){var r=t.getDate()+e,a=d(t);a.setHours(9),a.setDate(r),t.setDate(r),n||f(t),l(t,a)}return t}function l(t,e){if(+t)for(;t.getDate()!=e.getDate();)t.setTime(+t+(e>t?1:-1)*xe)}function u(t,e){return t.setMinutes(t.getMinutes()+e),t}function f(t){return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t}function d(t,e){return e?f(new Date(+t)):new Date(+t)}function v(){var t,e=0;do t=new Date(1970,e++,1);while(t.getHours());return t}function h(t,e,n){for(e=e||1;!t.getDay()||n&&1==t.getDay()||!n&&6==t.getDay();)c(t,e);return t}function g(t,e){return Math.round((d(t,!0)-d(e,!0))/Ee)}function p(t,n,r,a){n!==e&&n!=t.getFullYear()&&(t.setDate(1),t.setMonth(0),t.setFullYear(n)),r!==e&&r!=t.getMonth()&&(t.setDate(1),t.setMonth(r)),a!==e&&t.setDate(a)}function m(t,n){return"object"==typeof t?t:"number"==typeof t?new Date(1e3*t):"string"==typeof t?t.match(/^\d+(\.\d+)?$/)?new Date(1e3*parseFloat(t)):(n===e&&(n=!0),y(t,n)||(t?new Date(t):null)):null}function y(t,e){var n=t.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);if(!n)return null;var r=new Date(n[1],0,1);if(e||!n[13]){var a=new Date(n[1],0,1,9,0);n[3]&&(r.setMonth(n[3]-1),a.setMonth(n[3]-1)),n[5]&&(r.setDate(n[5]),a.setDate(n[5])),l(r,a),n[7]&&r.setHours(n[7]),n[8]&&r.setMinutes(n[8]),n[10]&&r.setSeconds(n[10]),n[12]&&r.setMilliseconds(1e3*Number("0."+n[12])),l(r,a)}else if(r.setUTCFullYear(n[1],n[3]?n[3]-1:0,n[5]||1),r.setUTCHours(n[7]||0,n[8]||0,n[10]||0,n[12]?1e3*Number("0."+n[12]):0),n[14]){var o=60*Number(n[16])+(n[18]?Number(n[18]):0);o*="-"==n[15]?1:-1,r=new Date(+r+1e3*60*o)}return r}function b(t){if("number"==typeof t)return 60*t;if("object"==typeof t)return 60*t.getHours()+t.getMinutes();var e=t.match(/(\d+)(?::(\d+))?\s*(\w+)?/);if(e){var n=parseInt(e[1],10);return e[3]&&(n%=12,"p"==e[3].toLowerCase().charAt(0)&&(n+=12)),60*n+(e[2]?parseInt(e[2],10):0)}}function w(t,e,n){return D(t,null,e,n)}function D(t,e,n,r){r=r||ye;var a,o,i,s,c=t,l=e,u=n.length,f="";for(a=0;u>a;a++)if(o=n.charAt(a),"'"==o){for(i=a+1;u>i;i++)if("'"==n.charAt(i)){c&&(f+=i==a+1?"'":n.substring(a+1,i),a=i);break}}else if("("==o){for(i=a+1;u>i;i++)if(")"==n.charAt(i)){var d=w(c,n.substring(a+1,i),r);parseInt(d.replace(/\D/,""),10)&&(f+=d),a=i;break}}else if("["==o){for(i=a+1;u>i;i++)if("]"==n.charAt(i)){var v=n.substring(a+1,i),d=w(c,v,r);d!=w(l,v,r)&&(f+=d),a=i;break}}else if("{"==o)c=e,l=t;else if("}"==o)c=t,l=e;else{for(i=u;i>a;i--)if(s=ke[n.substring(a,i)]){c&&(f+=s(c,r)),a=i-1;break}i==a&&c&&(f+=o)}return f}function M(t){var e,n=new Date(t.getTime());return n.setDate(n.getDate()+4-(n.getDay()||7)),e=n.getTime(),n.setMonth(0),n.setDate(1),Math.floor(Math.round((e-n)/864e5)/7)+1}function C(t){return t.end?S(t.end,t.allDay):c(d(t.start),1)}function S(t,e){return t=d(t),e||t.getHours()||t.getMinutes()?c(t,1):f(t)}function E(t,e){return 100*(e.msLength-t.msLength)+(t.event.start-e.event.start)}function x(t,e){return t.end>e.start&&t.starta;a++)o=t[a],i=o.start,s=e[a],s>n&&r>i&&(n>i?(c=d(n),u=!1):(c=i,u=!0),s>r?(l=d(r),f=!1):(l=s,f=!0),v.push({event:o,start:c,end:l,isStart:u,isEnd:f,msLength:l-c}));return v.sort(E)}function k(t){var e,n,r,a,o,i=[],s=t.length;for(e=0;s>e;e++){for(n=t[e],r=0;;){if(a=!1,i[r])for(o=0;i[r].length>o;o++)if(x(i[r][o],n)){a=!0;break}if(!a)break;r++}i[r]?i[r].push(n):i[r]=[n]}return i}function H(n,r,a){n.unbind("mouseover").mouseover(function(n){for(var o,i,s,c=n.target;c!=this;)o=c,c=c.parentNode;(i=o._fci)!==e&&(o._fci=e,s=r[i],a(s.event,s.element,s),t(n.target).trigger(n)),n.stopPropagation()})}function z(e,n,r){for(var a,o=0;e.length>o;o++)a=t(e[o]),a.width(Math.max(0,n-R(a,r)))}function F(e,n,r){for(var a,o=0;e.length>o;o++)a=t(e[o]),a.height(Math.max(0,n-L(a,r)))}function R(t,e){return N(t)+A(t)+(e?W(t):0)}function N(e){return(parseFloat(t.css(e[0],"paddingLeft",!0))||0)+(parseFloat(t.css(e[0],"paddingRight",!0))||0)}function W(e){return(parseFloat(t.css(e[0],"marginLeft",!0))||0)+(parseFloat(t.css(e[0],"marginRight",!0))||0)}function A(e){return(parseFloat(t.css(e[0],"borderLeftWidth",!0))||0)+(parseFloat(t.css(e[0],"borderRightWidth",!0))||0)}function L(t,e){return _(t)+B(t)+(e?O(t):0)}function _(e){return(parseFloat(t.css(e[0],"paddingTop",!0))||0)+(parseFloat(t.css(e[0],"paddingBottom",!0))||0)}function O(e){return(parseFloat(t.css(e[0],"marginTop",!0))||0)+(parseFloat(t.css(e[0],"marginBottom",!0))||0)}function B(e){return(parseFloat(t.css(e[0],"borderTopWidth",!0))||0)+(parseFloat(t.css(e[0],"borderBottomWidth",!0))||0)}function q(t,e){e="number"==typeof e?e+"px":e,t.each(function(t,n){n.style.cssText+=";min-height:"+e+";_height:"+e})}function I(){}function Y(t,e){return t-e}function j(t){return Math.max.apply(Math,t)}function P(t){return(10>t?"0":"")+t}function J(t,n){if(t[n]!==e)return t[n];for(var r,a=n.split(/(?=[A-Z])/),o=a.length-1;o>=0;o--)if(r=t[a[o].toLowerCase()],r!==e)return r;return t[""]}function V(t){return t.replace(/&/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g," ")}function X(t){return t.id+"/"+t.className+"/"+t.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/gi,"")}function U(t){t.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return!1})}function Z(t){t.children().removeClass("fc-first fc-last").filter(":first-child").addClass("fc-first").end().filter(":last-child").addClass("fc-last")}function $(t,e){t.each(function(t,n){n.className=n.className.replace(/^fc-\w*/,"fc-"+Se[e.getDay()])})}function Q(t,e){var n=t.source||{},r=t.color,a=n.color,o=e("eventColor"),i=t.backgroundColor||r||n.backgroundColor||a||e("eventBackgroundColor")||o,s=t.borderColor||r||n.borderColor||a||e("eventBorderColor")||o,c=t.textColor||n.textColor||e("eventTextColor"),l=[];return i&&l.push("background-color:"+i),s&&l.push("border-color:"+s),c&&l.push("color:"+c),l.join(";")}function G(e,n,r){if(t.isFunction(e)&&(e=[e]),e){var a,o;for(a=0;e.length>a;a++)o=e[a].apply(n,r)||o;return o}}function K(){for(var t=0;arguments.length>t;t++)if(arguments[t]!==e)return arguments[t]}function te(t,e){function n(t,e){e&&(s(t,e),t.setDate(1));var n=d(t,!0);n.setDate(1);var l=s(d(n),1),u=d(n),f=d(l),v=a("firstDay"),g=a("weekends")?0:1;g&&(h(u),h(f,-1,!0)),c(u,-((u.getDay()-Math.max(v,g)+7)%7)),c(f,(7-f.getDay()+Math.max(v,g))%7);var p=Math.round((f-u)/(7*Ee));"fixed"==a("weekMode")&&(c(f,7*(6-p)),p=6),r.title=i(n,a("titleFormat")),r.start=n,r.end=l,r.visStart=u,r.visEnd=f,o(p,g?5:7,!0)}var r=this;r.render=n,re.call(r,t,e,"month");var a=r.opt,o=r.renderBasic,i=e.formatDate}function ee(t,e){function n(t,e){e&&c(t,7*e);var n=c(d(t),-((t.getDay()-a("firstDay")+7)%7)),s=c(d(n),7),l=d(n),u=d(s),f=a("weekends");f||(h(l),h(u,-1,!0)),r.title=i(l,c(d(u),-1),a("titleFormat")),r.start=n,r.end=s,r.visStart=l,r.visEnd=u,o(1,f?7:5,!1)}var r=this;r.render=n,re.call(r,t,e,"basicWeek");var a=r.opt,o=r.renderBasic,i=e.formatDates}function ne(t,e){function n(t,e){e&&(c(t,e),a("weekends")||h(t,0>e?-1:1)),r.title=i(t,a("titleFormat")),r.start=r.visStart=d(t,!0),r.end=r.visEnd=c(d(r.start),1),o(1,1,!1)}var r=this;r.render=n,re.call(r,t,e,"basicDay");var a=r.opt,o=r.renderBasic,i=e.formatDate}function re(e,n,r){function a(t,e,n){ne=t,re=e,o();var r=!P;r?i():Te(),s(n)}function o(){ce=Ee("isRTL"),ce?(le=-1,fe=re-1):(le=1,fe=0),pe=Ee("firstDay"),ye=Ee("weekends")?0:1,be=Ee("theme")?"ui":"fc",we=Ee("columnFormat"),De=Ee("weekNumbers"),Me=Ee("weekNumberTitle"),Ce="iso"!=Ee("weekNumberCalculation")?"w":"W"}function i(){Q=t("
").appendTo(e)}function s(n){var r,a,o,i,s="",c=be+"-widget-header",l=be+"-widget-content",u=B.start.getMonth(),d=f(new Date);for(s+="",De&&(s+=" "),r=0;re>r;r++)o=F(0,r),s+="";for(s+=" ",r=0;ne>r;r++){for(s+="",De&&(s+=""+"
"+" "),a=0;re>a;a++)o=F(r,a),i=["fc-day","fc-"+Se[o.getDay()],l],o.getMonth()!=u&&i.push("fc-other-month"),+o==+d&&(i.push("fc-today"),i.push(be+"-state-highlight")),s+=""+"",n&&(s+="
"+o.getDate()+"
"),s+="
";s+=" "}s+="
",_(),I&&I.remove(),I=t(s).appendTo(e),Y=I.find("thead"),j=Y.find(".fc-day-header"),P=I.find("tbody"),J=P.find("tr"),V=P.find(".fc-day"),X=J.find("td:first-child"),$=J.eq(0).find(".fc-day-content > div"),Z(Y.add(Y.find("tr"))),Z(J),J.eq(0).addClass("fc-first"),J.filter(":last").addClass("fc-last"),De&&Y.find(".fc-week-number").text(Me),j.each(function(e,n){var r=R(e);t(n).text(Fe(r,we))}),De&&P.find(".fc-week-number > div").each(function(e,n){var r=F(e,0);t(n).text(Fe(r,Ce))}),V.each(function(e,n){var r=R(e);xe("dayRender",B,r,t(n))}),v(V)}function l(e){K=e;var n,r,a,o=K-Y.height();"variable"==Ee("weekMode")?n=r=Math.floor(o/(1==ne?2:6)):(n=Math.floor(o/ne),r=o-n*(ne-1)),X.each(function(e,o){ne>e&&(a=t(o),q(a.find("> div"),(e==ne-1?r:n)-L(a)))}),O()}function u(t){G=t,se.clear(),ee=0,De&&(ee=Y.find("th.fc-week-number").outerWidth()),te=Math.floor((G-ee)/re),z(j.slice(0,-1),te)}function v(t){t.click(h).mousedown(ze)}function h(e){if(!Ee("selectable")){var n=y(t(this).data("date"));xe("dayClick",this,n,!0,e)}}function p(t,e,n){n&&oe.build();for(var r=d(B.visStart),a=c(d(r),re),o=0;ne>o;o++){var i=new Date(Math.max(r,t)),s=new Date(Math.min(a,e));if(s>i){var l,u;ce?(l=g(s,r)*le+fe+1,u=g(i,r)*le+fe+1):(l=g(i,r),u=g(s,r)),v(m(o,l,o,u-1))}c(r,7),c(a,7)}}function m(t,n,r,a){var o=oe.rect(t,n,r,a,e);return ke(o,e)}function b(t){return d(t)}function w(t,e){p(t,c(d(e),1),!0)}function D(){He()}function M(t,e,n){var r=k(t),a=V[r.row*re+r.col];xe("dayClick",a,t,e,n)}function C(t,e){ie.start(function(t){He(),t&&m(t.row,t.col,t.row,t.col)},e)}function S(t,e,n){var r=ie.stop();if(He(),r){var a=H(r);xe("drop",t,a,!0,e,n)}}function E(t){return d(t.start)}function x(t){return se.left(t)}function T(t){return se.right(t)}function k(t){return{row:Math.floor(g(t,B.visStart)/7),col:N(t.getDay())}}function H(t){return F(t.row,t.col)}function F(t,e){return c(d(B.visStart),7*t+e*le+fe)}function R(t){return F(Math.floor(t/re),t%re)}function N(t){return(t-Math.max(pe,ye)+re)%re*le+fe}function W(t){return J.eq(t)}function A(){var t=0;return De&&(t+=ee),{left:t,right:G}}function _(){q(e,e.height())}function O(){q(e,1)}var B=this;B.renderBasic=a,B.setHeight=l,B.setWidth=u,B.renderDayOverlay=p,B.defaultSelectionEnd=b,B.renderSelection=w,B.clearSelection=D,B.reportDayClick=M,B.dragStart=C,B.dragStop=S,B.defaultEventEnd=E,B.getHoverListener=function(){return ie},B.colContentLeft=x,B.colContentRight=T,B.dayOfWeekCol=N,B.dateCell=k,B.cellDate=H,B.cellIsAllDay=function(){return!0},B.allDayRow=W,B.allDayBounds=A,B.getRowCnt=function(){return ne},B.getColCnt=function(){return re},B.getColWidth=function(){return te},B.getDaySegmentContainer=function(){return Q},ue.call(B,e,n,r),ve.call(B),de.call(B),ae.call(B);var I,Y,j,P,J,V,X,$,Q,G,K,te,ee,ne,re,oe,ie,se,ce,le,fe,pe,ye,be,we,De,Me,Ce,Ee=B.opt,xe=B.trigger,Te=B.clearEvents,ke=B.renderOverlay,He=B.clearOverlays,ze=B.daySelectionMousedown,Fe=n.formatDate;U(e.addClass("fc-grid")),oe=new he(function(e,n){var r,a,o;j.each(function(e,i){r=t(i),a=r.offset().left,e&&(o[1]=a),o=[a],n[e]=o}),o[1]=a+r.outerWidth(),J.each(function(n,i){ne>n&&(r=t(i),a=r.offset().top,n&&(o[1]=a),o=[a],e[n]=o)}),o[1]=a+r.outerHeight()}),ie=new ge(oe),se=new me(function(t){return $.eq(t)})}function ae(){function e(t,e){v(t),x(r(t),e),l("eventAfterAllRender")}function n(){h(),b().empty()}function r(e){var n,r,a,o,s,l,u=S(),f=E(),v=d(i.visStart),h=c(d(v),f),g=t.map(e,C),p=[];for(n=0;u>n;n++){for(r=k(T(e,g,v,h)),a=0;r.length>a;a++)for(o=r[a],s=0;o.length>s;s++)l=o[s],l.row=n,l.level=a,p.push(l);c(v,7),c(h,7)}return p}function a(t,e,n){u(t)&&o(t,e),n.isEnd&&f(t)&&H(t,e,n),g(t,e)}function o(t,e){var n,r=w();e.draggable({zIndex:9,delay:50,opacity:s("dragOpacity"),revertDuration:s("dragRevertDuration"),start:function(a,o){l("eventDragStart",e,t,a,o),m(t,e),r.start(function(r,a,o,i){e.draggable("option","revert",!r||!o&&!i),M(),r?(n=7*o+i*(s("isRTL")?-1:1),D(c(d(t.start),n),c(C(t),n))):n=0},a,"drag")},stop:function(a,o){r.stop(),M(),l("eventDragStop",e,t,a,o),n?y(this,t,n,0,t.allDay,a,o):(e.css("filter",""),p(t,e))}})}var i=this;i.renderEvents=e,i.compileDaySegs=r,i.clearEvents=n,i.bindDaySeg=a,fe.call(i);var s=i.opt,l=i.trigger,u=i.isEventDraggable,f=i.isEventResizable,v=i.reportEvents,h=i.reportEventClear,g=i.eventElementHandlers,p=i.showEvents,m=i.hideEvents,y=i.eventDrop,b=i.getDaySegmentContainer,w=i.getHoverListener,D=i.renderDayOverlay,M=i.clearOverlays,S=i.getRowCnt,E=i.getColCnt,x=i.renderDaySegs,H=i.resizableDayEvent}function oe(t,e){function n(t,e){e&&c(t,7*e);var n=c(d(t),-((t.getDay()-a("firstDay")+7)%7)),s=c(d(n),7),l=d(n),u=d(s),f=a("weekends");f||(h(l),h(u,-1,!0)),r.title=i(l,c(d(u),-1),a("titleFormat")),r.start=n,r.end=s,r.visStart=l,r.visEnd=u,o(f?7:5)}var r=this;r.render=n,se.call(r,t,e,"agendaWeek");var a=r.opt,o=r.renderAgenda,i=e.formatDates}function ie(t,e){function n(t,e){e&&(c(t,e),a("weekends")||h(t,0>e?-1:1));var n=d(t,!0),s=c(d(n),1);r.title=i(t,a("titleFormat")),r.start=r.visStart=n,r.end=r.visEnd=s,o(1)}var r=this;r.render=n,se.call(r,t,e,"agendaDay");var a=r.opt,o=r.renderAgenda,i=e.formatDate}function se(n,r,a){function o(t){Le=t,i(),te?nn():s(),l()}function i(){Ye=tn("theme")?"ui":"fc",Pe=tn("weekends")?0:1,je=tn("firstDay"),(Je=tn("isRTL"))?(Ve=-1,Xe=Le-1):(Ve=1,Xe=0),Ue=b(tn("minTime")),Ze=b(tn("maxTime")),$e=tn("columnFormat"),Qe=tn("weekNumbers"),Ge=tn("weekNumberTitle"),Ke="iso"!=tn("weekNumberCalculation")?"w":"W",Ne=tn("snapMinutes")||tn("slotMinutes")}function s(){var e,r,a,o,i,s=Ye+"-widget-header",c=Ye+"-widget-content",l=0==tn("slotMinutes")%15;for(e="",e+=Qe?" ":" ",r=0;Le>r;r++)e+=" ";for(e+=" "+" "+" "+""+""+" ",r=0;Le>r;r++)e+=""+""+" ";for(e+=" "+" "+" "+"
",te=t(e).appendTo(n),ee=te.find("thead"),ne=ee.find("th").slice(1,-1),re=te.find("tbody"),ae=re.find("td").slice(0,-1),oe=ae.find("div.fc-day-content div"),ie=ae.eq(0),se=ie.find("> div"),Z(ee.add(ee.find("tr"))),Z(re.add(re.find("tr"))),Se=ee.find("th:first"),Ee=te.find(".fc-agenda-gutter"),le=t("
").appendTo(n),tn("allDaySlot")?(fe=t("
").appendTo(le),e=""+tn("allDayText")+" "+""+""+" "+" "+" "+"
",pe=t(e).appendTo(le),ye=pe.find("tr"),D(ye.find("td")),Se=Se.add(pe.find("th:first")),Ee=Ee.add(pe.find("th.fc-agenda-gutter")),le.append("")):fe=t([]),be=t("
").appendTo(le),we=t("
").appendTo(be),De=t("
").appendTo(we),e="",a=v(),o=u(d(a),Ze),u(a,Ue),_e=0,r=0;o>a;r++)i=a.getMinutes(),e+=""+""+(l&&i?" ":un(a,tn("axisFormat")))+" "+""+"
"+" "+" ",u(a,tn("slotMinutes")),_e++;e+="
",Me=t(e).appendTo(we),Ce=Me.find("div:first"),M(Me.find("td")),Se=Se.add(Me.find("th:first"))}function l(){var t,e,n,r,a=f(new Date);if(Qe){var o=un(N(0),Ke);Je?o+=Ge:o=Ge+o,ee.find(".fc-week-number").text(o)}for(t=0;Le>t;t++)r=N(t),e=ne.eq(t),e.html(un(r,$e)),n=ae.eq(t),+r==+a?n.addClass(Ye+"-state-highlight fc-today"):n.removeClass(Ye+"-state-highlight fc-today"),$(e.add(n),r)}function h(t,n){t===e&&(t=ke),ke=t,fn={};var r=re.position().top,a=be.position().top,o=Math.min(t-r,Me.height()+a+1);se.height(o-L(ie)),le.css("top",r),be.height(o-a-1),Re=Ce.height()+1,We=tn("slotMinutes")/Ne,Ae=Re/We,n&&m()}function p(e){Te=e,qe.clear(),He=0,z(Se.width("").each(function(e,n){He=Math.max(He,t(n).outerWidth())}),He);var n=be[0].clientWidth;Fe=be.width()-n,Fe?(z(Ee,Fe),Ee.show().prev().removeClass("fc-last")):Ee.hide().prev().addClass("fc-last"),ze=Math.floor((n-He)/Le),z(ne.slice(0,-1),ze)}function m(){function t(){be.scrollTop(r)}var e=v(),n=d(e);n.setHours(tn("firstHour"));var r=_(e,n)+1;t(),setTimeout(t,0)}function y(){Ie=be.scrollTop()}function w(){be.scrollTop(Ie)}function D(t){t.click(C).mousedown(cn)}function M(t){t.click(C).mousedown(V)}function C(t){if(!tn("selectable")){var e=Math.min(Le-1,Math.floor((t.pageX-te.offset().left-He)/ze)),n=N(e),r=this.parentNode.className.match(/fc-slot(\d+)/);if(r){var a=parseInt(r[1])*tn("slotMinutes"),o=Math.floor(a/60);n.setHours(o),n.setMinutes(a%60+Ue),en("dayClick",ae[e],n,!1,t)}else en("dayClick",ae[e],n,!0,t)}}function S(t,e,n){n&&Oe.build();var r,a,o=d(K.visStart);Je?(r=g(e,o)*Ve+Xe+1,a=g(t,o)*Ve+Xe+1):(r=g(t,o),a=g(e,o)),r=Math.max(0,r),a=Math.min(Le,a),a>r&&D(E(0,r,0,a-1))}function E(t,e,n,r){var a=Oe.rect(t,e,n,r,le);return rn(a,le)}function x(t,e){for(var n=d(K.visStart),r=c(d(n),1),a=0;Le>a;a++){var o=new Date(Math.max(n,t)),i=new Date(Math.min(r,e));if(i>o){var s=a*Ve+Xe,l=Oe.rect(0,s,0,s,we),u=_(n,o),f=_(n,i);l.top=u,l.height=f-u,M(rn(l,we))}c(n,1),c(r,1)}}function T(t){return qe.left(t)}function k(t){return qe.right(t)}function H(t){return{row:Math.floor(g(t,K.visStart)/7),col:A(t.getDay())}}function R(t){var e=N(t.col),n=t.row;return tn("allDaySlot")&&n--,n>=0&&u(e,Ue+n*Ne),e}function N(t){return c(d(K.visStart),t*Ve+Xe)}function W(t){return tn("allDaySlot")&&!t.row}function A(t){return(t-Math.max(je,Pe)+Le)%Le*Ve+Xe}function _(t,n){if(t=d(t,!0),u(d(t),Ue)>n)return 0;if(n>=u(d(t),Ze))return Me.height();var r=tn("slotMinutes"),a=60*n.getHours()+n.getMinutes()-Ue,o=Math.floor(a/r),i=fn[o];return i===e&&(i=fn[o]=Me.find("tr:eq("+o+") td div")[0].offsetTop),Math.max(0,Math.round(i-1+Re*(a%r/r)))}function O(){return{left:He,right:Te-Fe}}function B(){return ye}function q(t){var e=d(t.start);return t.allDay?e:u(e,tn("defaultEventMinutes"))}function I(t,e){return e?d(t):u(d(t),tn("slotMinutes"))}function j(t,e,n){n?tn("allDaySlot")&&S(t,c(d(e),1),!0):P(t,e)}function P(e,n){var r=tn("selectHelper");if(Oe.build(),r){var a=g(e,K.visStart)*Ve+Xe;if(a>=0&&Le>a){var o=Oe.rect(0,a,0,a,we),i=_(e,e),s=_(e,n);if(s>i){if(o.top=i,o.height=s-i,o.left+=2,o.width-=5,t.isFunction(r)){var c=r(e,n);c&&(o.position="absolute",o.zIndex=8,xe=t(c).css(o).appendTo(we))}else o.isStart=!0,o.isEnd=!0,xe=t(ln({title:"",start:e,end:n,className:["fc-select-helper"],editable:!1},o)),xe.css("opacity",tn("dragOpacity"));xe&&(M(xe),we.append(xe),z(xe,o.width,!0),F(xe,o.height,!0))}}}else x(e,n)}function J(){an(),xe&&(xe.remove(),xe=null)}function V(e){if(1==e.which&&tn("selectable")){sn(e);var n;Be.start(function(t,e){if(J(),t&&t.col==e.col&&!W(t)){var r=R(e),a=R(t);n=[r,u(d(r),Ne),a,u(d(a),Ne)].sort(Y),P(n[0],n[3])}else n=null},e),t(document).one("mouseup",function(t){Be.stop(),n&&(+n[0]==+n[1]&&X(n[0],!1,t),on(n[0],n[3],!1,t))})}}function X(t,e,n){en("dayClick",ae[A(t.getDay())],t,e,n)}function Q(t,e){Be.start(function(t){if(an(),t)if(W(t))E(t.row,t.col,t.row,t.col);else{var e=R(t),n=u(d(e),tn("defaultEventMinutes"));x(e,n)}},e)}function G(t,e,n){var r=Be.stop();an(),r&&en("drop",t,R(r),W(r),e,n)}var K=this;K.renderAgenda=o,K.setWidth=p,K.setHeight=h,K.beforeHide=y,K.afterShow=w,K.defaultEventEnd=q,K.timePosition=_,K.dayOfWeekCol=A,K.dateCell=H,K.cellDate=R,K.cellIsAllDay=W,K.allDayRow=B,K.allDayBounds=O,K.getHoverListener=function(){return Be},K.colContentLeft=T,K.colContentRight=k,K.getDaySegmentContainer=function(){return fe},K.getSlotSegmentContainer=function(){return De},K.getMinMinute=function(){return Ue},K.getMaxMinute=function(){return Ze},K.getBodyContent=function(){return we},K.getRowCnt=function(){return 1},K.getColCnt=function(){return Le},K.getColWidth=function(){return ze},K.getSnapHeight=function(){return Ae},K.getSnapMinutes=function(){return Ne},K.defaultSelectionEnd=I,K.renderDayOverlay=S,K.renderSelection=j,K.clearSelection=J,K.reportDayClick=X,K.dragStart=Q,K.dragStop=G,ue.call(K,n,r,a),ve.call(K),de.call(K),ce.call(K);var te,ee,ne,re,ae,oe,ie,se,le,fe,pe,ye,be,we,De,Me,Ce,Se,Ee,xe,Te,ke,He,ze,Fe,Re,Ne,We,Ae,Le,_e,Oe,Be,qe,Ie,Ye,je,Pe,Je,Ve,Xe,Ue,Ze,$e,Qe,Ge,Ke,tn=K.opt,en=K.trigger,nn=K.clearEvents,rn=K.renderOverlay,an=K.clearOverlays,on=K.reportSelection,sn=K.unselect,cn=K.daySelectionMousedown,ln=K.slotSegHtml,un=r.formatDate,fn={};U(n.addClass("fc-agenda")),Oe=new he(function(e,n){function r(t){return Math.max(c,Math.min(l,t))}var a,o,i;ne.each(function(e,r){a=t(r),o=a.offset().left,e&&(i[1]=o),i=[o],n[e]=i}),i[1]=o+a.outerWidth(),tn("allDaySlot")&&(a=ye,o=a.offset().top,e[0]=[o,o+a.outerHeight()]);for(var s=we.offset().top,c=be.offset().top,l=c+be.outerHeight(),u=0;_e*We>u;u++)e.push([r(s+Ae*u),r(s+Ae*(u+1))])}),Be=new ge(Oe),qe=new me(function(t){return oe.eq(t)})}function ce(){function n(t,e){S(t);var n,r=t.length,i=[],c=[];for(n=0;r>n;n++)t[n].allDay?i.push(t[n]):c.push(t[n]);y("allDaySlot")&&(Y(a(i),e),z()),s(o(c),e),b("eventAfterAllRender")}function r(){E(),N().empty(),W().empty()}function a(e){var n,r,a,o,i=k(T(e,t.map(e,C),m.visStart,m.visEnd)),s=i.length,c=[];for(n=0;s>n;n++)for(r=i[n],a=0;r.length>a;a++)o=r[a],o.row=0,o.level=n,c.push(o);return c}function o(e){var n,r,a,o,s,l,f=P(),v=O(),h=_(),g=u(d(m.visStart),v),p=t.map(e,i),y=[];for(n=0;f>n;n++){for(r=k(T(e,p,g,u(d(g),h-v))),le(r),a=0;r.length>a;a++)for(o=r[a],s=0;o.length>s;s++)l=o[s],l.col=n,l.level=a,y.push(l);c(g,1,!0)}return y}function i(t){return t.end?d(t.end):u(d(t.start),y("defaultEventMinutes"))}function s(n,r){var a,o,i,s,c,u,f,d,h,g,p,m,w,D,M,C,S,E,x,T,k,z,F=n.length,N="",A={},_={},O=W(),Y=P();for((T=y("isRTL"))?(k=-1,z=Y-1):(k=1,z=0),a=0;F>a;a++)o=n[a],i=o.event,s=B(o.start,o.start),c=B(o.start,o.end),u=o.col,f=o.level,d=o.forward||0,h=q(u*k+z),g=I(u*k+z)-h,g=Math.min(g-6,.95*g),p=f?g/(f+d+1):d?2*(g/(d+1)-6):g,m=h+g/(f+d+1)*f*k+(T?g-p:0),o.top=s,o.left=m,o.outerWidth=p,o.outerHeight=c-s,N+=l(i,o);
+for(O[0].innerHTML=N,w=O.children(),a=0;F>a;a++)o=n[a],i=o.event,D=t(w[a]),M=b("eventRender",i,i,D),M===!1?D.remove():(M&&M!==!0&&(D.remove(),D=t(M).css({position:"absolute",top:o.top,left:o.left}).appendTo(O)),o.element=D,i._id===r?v(i,D,o):D[0]._fci=a,G(i,D));for(H(O,n,v),a=0;F>a;a++)o=n[a],(D=o.element)&&(S=A[C=o.key=X(D[0])],o.vsides=S===e?A[C]=L(D,!0):S,S=_[C],o.hsides=S===e?_[C]=R(D,!0):S,E=D.find(".fc-event-title"),E.length&&(o.contentTop=E[0].offsetTop));for(a=0;F>a;a++)o=n[a],(D=o.element)&&(D[0].style.width=Math.max(0,o.outerWidth-o.hsides)+"px",x=Math.max(0,o.outerHeight-o.vsides),D[0].style.height=x+"px",i=o.event,o.contentTop!==e&&10>x-o.contentTop&&(D.find("div.fc-event-time").text(ie(i.start,y("timeFormat"))+" - "+i.title),D.find("div.fc-event-title").remove()),b("eventAfterRender",i,i,D))}function l(t,e){var n="<",r=t.url,a=Q(t,y),o=["fc-event","fc-event-vert"];return w(t)&&o.push("fc-event-draggable"),e.isStart&&o.push("fc-event-start"),e.isEnd&&o.push("fc-event-end"),o=o.concat(t.className),t.source&&(o=o.concat(t.source.className||[])),n+=r?"a href='"+V(t.url)+"'":"div",n+=" class='"+o.join(" ")+"'"+" style='position:absolute;z-index:8;top:"+e.top+"px;left:"+e.left+"px;"+a+"'"+">"+""+"
"+V(se(t.start,t.end,y("timeFormat")))+"
"+"
"+V(t.title)+"
"+"
"+"
",e.isEnd&&D(t)&&(n+="=
"),n+=""+(r?"a":"div")+">"}function f(t,e,n){w(t)&&h(t,e,n.isStart),n.isEnd&&D(t)&&j(t,e,n),x(t,e)}function v(t,e,n){var r=e.find("div.fc-event-time");w(t)&&g(t,e,r),n.isEnd&&D(t)&&p(t,e,r),x(t,e)}function h(t,e,n){function r(){s||(e.width(a).height("").draggable("option","grid",null),s=!0)}var a,o,i,s=!0,l=y("isRTL")?-1:1,u=A(),f=J(),v=U(),h=Z(),g=O();e.draggable({zIndex:9,opacity:y("dragOpacity","month"),revertDuration:y("dragRevertDuration"),start:function(g,p){b("eventDragStart",e,t,g,p),te(t,e),a=e.width(),u.start(function(a,u,g,p){ae(),a?(o=!1,i=p*l,a.row?n?s&&(e.width(f-10),F(e,v*Math.round((t.end?(t.end-t.start)/Te:y("defaultEventMinutes"))/h)),e.draggable("option","grid",[f,1]),s=!1):o=!0:(re(c(d(t.start),i),c(C(t),i)),r()),o=o||s&&!i):(r(),o=!0),e.draggable("option","revert",o)},g,"drag")},stop:function(n,a){if(u.stop(),ae(),b("eventDragStop",e,t,n,a),o)r(),e.css("filter",""),K(t,e);else{var c=0;s||(c=Math.round((e.offset().top-$().offset().top)/v)*h+g-(60*t.start.getHours()+t.start.getMinutes())),ee(this,t,i,c,s,n,a)}}})}function g(t,e,n){function r(e){var r,a=u(d(t.start),e);t.end&&(r=u(d(t.end),e)),n.text(se(a,r,y("timeFormat")))}function a(){f&&(n.css("display",""),e.draggable("option","grid",[p,m]),f=!1)}var o,i,s,l,f=!1,v=y("isRTL")?-1:1,h=A(),g=P(),p=J(),m=U(),w=Z();e.draggable({zIndex:9,scroll:!1,grid:[p,m],axis:1==g?"y":!1,opacity:y("dragOpacity"),revertDuration:y("dragRevertDuration"),start:function(r,u){b("eventDragStart",e,t,r,u),te(t,e),o=e.position(),s=l=0,h.start(function(r,o,s,l){e.draggable("option","revert",!r),ae(),r&&(i=l*v,y("allDaySlot")&&!r.row?(f||(f=!0,n.hide(),e.draggable("option","grid",null)),re(c(d(t.start),i),c(C(t),i))):a())},r,"drag")},drag:function(t,e){s=Math.round((e.position.top-o.top)/m)*w,s!=l&&(f||r(s),l=s)},stop:function(n,c){var l=h.stop();ae(),b("eventDragStop",e,t,n,c),l&&(i||s||f)?ee(this,t,i,f?0:s,f,n,c):(a(),e.css("filter",""),e.css(o),r(0),K(t,e))}})}function p(t,e,n){var r,a,o=U(),i=Z();e.resizable({handles:{s:".ui-resizable-handle"},grid:o,start:function(n,o){r=a=0,te(t,e),e.css("z-index",9),b("eventResizeStart",this,t,n,o)},resize:function(s,c){r=Math.round((Math.max(o,e.height())-c.originalSize.height)/o),r!=a&&(n.text(se(t.start,r||t.end?u(M(t),i*r):null,y("timeFormat"))),a=r)},stop:function(n,a){b("eventResizeStop",this,t,n,a),r?ne(this,t,0,i*r,n,a):(e.css("z-index",8),K(t,e))}})}var m=this;m.renderEvents=n,m.compileDaySegs=a,m.clearEvents=r,m.slotSegHtml=l,m.bindDaySeg=f,fe.call(m);var y=m.opt,b=m.trigger,w=m.isEventDraggable,D=m.isEventResizable,M=m.eventEnd,S=m.reportEvents,E=m.reportEventClear,x=m.eventElementHandlers,z=m.setHeight,N=m.getDaySegmentContainer,W=m.getSlotSegmentContainer,A=m.getHoverListener,_=m.getMaxMinute,O=m.getMinMinute,B=m.timePosition,q=m.colContentLeft,I=m.colContentRight,Y=m.renderDaySegs,j=m.resizableDayEvent,P=m.getColCnt,J=m.getColWidth,U=m.getSnapHeight,Z=m.getSnapMinutes,$=m.getBodyContent,G=m.reportEventElement,K=m.showEvents,te=m.hideEvents,ee=m.eventDrop,ne=m.eventResize,re=m.renderDayOverlay,ae=m.clearOverlays,oe=m.calendar,ie=oe.formatDate,se=oe.formatDates}function le(t){var e,n,r,a,o,i;for(e=t.length-1;e>0;e--)for(a=t[e],n=0;a.length>n;n++)for(o=a[n],r=0;t[e-1].length>r;r++)i=t[e-1][r],x(o,i)&&(i.forward=Math.max(i.forward||0,(o.forward||0)+1))}function ue(t,n,r){function a(t,e){var n=F[t];return"object"==typeof n?J(n,e||r):n}function o(t,e){return n.trigger.apply(n,[t,e||S].concat(Array.prototype.slice.call(arguments,2),[S]))}function i(t){return l(t)&&!a("disableDragging")}function s(t){return l(t)&&!a("disableResizing")}function l(t){return K(t.editable,(t.source||{}).editable,a("editable"))}function f(t){k={};var e,n,r=t.length;for(e=0;r>e;e++)n=t[e],k[n._id]?k[n._id].push(n):k[n._id]=[n]}function v(t){return t.end?d(t.end):E(t)}function h(t,e){H.push(e),z[t._id]?z[t._id].push(e):z[t._id]=[e]}function g(){H=[],z={}}function p(t,n){n.click(function(r){return n.hasClass("ui-draggable-dragging")||n.hasClass("ui-resizable-resizing")?e:o("eventClick",this,t,r)}).hover(function(e){o("eventMouseover",this,t,e)},function(e){o("eventMouseout",this,t,e)})}function m(t,e){b(t,e,"show")}function y(t,e){b(t,e,"hide")}function b(t,e,n){var r,a=z[t._id],o=a.length;for(r=0;o>r;r++)e&&a[r][0]==e[0]||a[r][n]()}function w(t,e,n,r,a,i,s){var c=e.allDay,l=e._id;M(k[l],n,r,a),o("eventDrop",t,e,n,r,a,function(){M(k[l],-n,-r,c),T(l)},i,s),T(l)}function D(t,e,n,r,a,i){var s=e._id;C(k[s],n,r),o("eventResize",t,e,n,r,function(){C(k[s],-n,-r),T(s)},a,i),T(s)}function M(t,n,r,a){r=r||0;for(var o,i=t.length,s=0;i>s;s++)o=t[s],a!==e&&(o.allDay=a),u(c(o.start,n,!0),r),o.end&&(o.end=u(c(o.end,n,!0),r)),x(o,F)}function C(t,e,n){n=n||0;for(var r,a=t.length,o=0;a>o;o++)r=t[o],r.end=u(c(v(r),e,!0),n),x(r,F)}var S=this;S.element=t,S.calendar=n,S.name=r,S.opt=a,S.trigger=o,S.isEventDraggable=i,S.isEventResizable=s,S.reportEvents=f,S.eventEnd=v,S.reportEventElement=h,S.reportEventClear=g,S.eventElementHandlers=p,S.showEvents=m,S.hideEvents=y,S.eventDrop=w,S.eventResize=D;var E=S.defaultEventEnd,x=n.normalizeEvent,T=n.reportEventChange,k={},H=[],z={},F=n.options}function fe(){function n(t,e){var n,r,c,d,p,m,y,b,w=B(),D=T(),M=k(),C=0,S=t.length;for(w[0].innerHTML=a(t),o(t,w.children()),i(t),s(t,w,e),l(t),u(t),f(t),n=v(),r=0;D>r;r++){for(c=0,d=[],p=0;M>p;p++)d[p]=0;for(;S>C&&(m=t[C]).row==r;){for(y=j(d.slice(m.startCol,m.endCol)),m.top=y,y+=m.outerHeight,b=m.startCol;m.endCol>b;b++)d[b]=y;C++}n[r].height(j(d))}g(t,h(n))}function r(e,n,r){var i,s,c,d=t("
"),p=B(),m=e.length;for(d[0].innerHTML=a(e),i=d.children(),p.append(i),o(e,i),l(e),u(e),f(e),g(e,h(v())),i=[],s=0;m>s;s++)c=e[s].element,c&&(e[s].row===n&&c.css("top",r),i.push(c[0]));return t(i)}function a(t){var e,n,r,a,o,i,s,c,l,u,f=y("isRTL"),d=t.length,v=F(),h=v.left,g=v.right,p="";for(e=0;d>e;e++)n=t[e],r=n.event,o=["fc-event","fc-event-hori"],w(r)&&o.push("fc-event-draggable"),n.isStart&&o.push("fc-event-start"),n.isEnd&&o.push("fc-event-end"),f?(i=A(n.end.getDay()-1),s=A(n.start.getDay()),c=n.isEnd?N(i):h,l=n.isStart?W(s):g):(i=A(n.start.getDay()),s=A(n.end.getDay()-1),c=n.isStart?N(i):h,l=n.isEnd?W(s):g),o=o.concat(r.className),r.source&&(o=o.concat(r.source.className||[])),a=r.url,u=Q(r,y),p+=a?""+"",!r.allDay&&n.isStart&&(p+=""+V(I(r.start,r.end,y("timeFormat")))+" "),p+=""+V(r.title)+" "+"
",n.isEnd&&D(r)&&(p+=""+" "+"
"),p+=""+(a?"a":"div")+">",n.left=c,n.outerWidth=l-c,n.startCol=i,n.endCol=s+1;return p}function o(e,n){var r,a,o,i,s,c=e.length;for(r=0;c>r;r++)a=e[r],o=a.event,i=t(n[r]),s=b("eventRender",o,o,i),s===!1?i.remove():(s&&s!==!0&&(s=t(s).css({position:"absolute",left:a.left}),i.replaceWith(s),i=s),a.element=i)}function i(t){var e,n,r,a=t.length;for(e=0;a>e;e++)n=t[e],r=n.element,r&&C(n.event,r)}function s(t,e,n){var r,a,o,i,s=t.length;for(r=0;s>r;r++)a=t[r],o=a.element,o&&(i=a.event,i._id===n?q(i,o,a):o[0]._fci=r);H(e,t,q)}function l(t){var n,r,a,o,i,s=t.length,c={};for(n=0;s>n;n++)r=t[n],a=r.element,a&&(o=r.key=X(a[0]),i=c[o],i===e&&(i=c[o]=R(a,!0)),r.hsides=i)}function u(t){var e,n,r,a=t.length;for(e=0;a>e;e++)n=t[e],r=n.element,r&&(r[0].style.width=Math.max(0,n.outerWidth-n.hsides)+"px")}function f(t){var n,r,a,o,i,s=t.length,c={};for(n=0;s>n;n++)r=t[n],a=r.element,a&&(o=r.key,i=c[o],i===e&&(i=c[o]=O(a)),r.outerHeight=a[0].offsetHeight+i)}function v(){var t,e=T(),n=[];for(t=0;e>t;t++)n[t]=z(t).find("div.fc-day-content > div");return n}function h(t){var e,n=t.length,r=[];for(e=0;n>e;e++)r[e]=t[e][0].offsetTop;return r}function g(t,e){var n,r,a,o,i=t.length;for(n=0;i>n;n++)r=t[n],a=r.element,a&&(a[0].style.top=e[r.row]+(r.top||0)+"px",o=r.event,b("eventAfterRender",o,o,a))}function p(e,n,a){var o=y("isRTL"),i=o?"w":"e",s=n.find(".ui-resizable-"+i),l=!1;U(n),n.mousedown(function(t){t.preventDefault()}).click(function(t){l&&(t.preventDefault(),t.stopImmediatePropagation())}),s.mousedown(function(s){function u(n){b("eventResizeStop",this,e,n),t("body").css("cursor",""),h.stop(),P(),f&&x(this,e,f,0,n),setTimeout(function(){l=!1},0)}if(1==s.which){l=!0;var f,v,h=m.getHoverListener(),g=T(),p=k(),y=o?-1:1,w=o?p-1:0,D=n.css("top"),C=t.extend({},e),H=L(e.start);J(),t("body").css("cursor",i+"-resize").one("mouseup",u),b("eventResizeStart",this,e,s),h.start(function(t,n){if(t){var s=Math.max(H.row,t.row),l=t.col;1==g&&(s=0),s==H.row&&(l=o?Math.min(H.col,l):Math.max(H.col,l)),f=7*s+l*y+w-(7*n.row+n.col*y+w);var u=c(M(e),f,!0);if(f){C.end=u;var h=v;v=r(_([C]),a.row,D),v.find("*").css("cursor",i+"-resize"),h&&h.remove(),E(e)}else v&&(S(e),v.remove(),v=null);P(),Y(e.start,c(d(u),1))}},s)}})}var m=this;m.renderDaySegs=n,m.resizableDayEvent=p;var y=m.opt,b=m.trigger,w=m.isEventDraggable,D=m.isEventResizable,M=m.eventEnd,C=m.reportEventElement,S=m.showEvents,E=m.hideEvents,x=m.eventResize,T=m.getRowCnt,k=m.getColCnt;m.getColWidth;var z=m.allDayRow,F=m.allDayBounds,N=m.colContentLeft,W=m.colContentRight,A=m.dayOfWeekCol,L=m.dateCell,_=m.compileDaySegs,B=m.getDaySegmentContainer,q=m.bindDaySeg,I=m.calendar.formatDates,Y=m.renderDayOverlay,P=m.clearOverlays,J=m.clearSelection}function de(){function e(t,e,a){n(),e||(e=c(t,a)),l(t,e,a),r(t,e,a)}function n(t){f&&(f=!1,u(),s("unselect",null,t))}function r(t,e,n,r){f=!0,s("select",null,t,e,n,r)}function a(e){var a=o.cellDate,s=o.cellIsAllDay,c=o.getHoverListener(),f=o.reportDayClick;if(1==e.which&&i("selectable")){n(e);var d;c.start(function(t,e){u(),t&&s(t)?(d=[a(e),a(t)].sort(Y),l(d[0],d[1],!0)):d=null},e),t(document).one("mouseup",function(t){c.stop(),d&&(+d[0]==+d[1]&&f(d[0],!0,t),r(d[0],d[1],!0,t))})}}var o=this;o.select=e,o.unselect=n,o.reportSelection=r,o.daySelectionMousedown=a;var i=o.opt,s=o.trigger,c=o.defaultSelectionEnd,l=o.renderSelection,u=o.clearSelection,f=!1;i("selectable")&&i("unselectAuto")&&t(document).mousedown(function(e){var r=i("unselectCancel");r&&t(e.target).parents(r).length||n(e)})}function ve(){function e(e,n){var r=o.shift();return r||(r=t("
")),r[0].parentNode!=n[0]&&r.appendTo(n),a.push(r.css(e).show()),r}function n(){for(var t;t=a.shift();)o.push(t.hide().unbind())}var r=this;r.renderOverlay=e,r.clearOverlays=n;var a=[],o=[]}function he(t){var e,n,r=this;r.build=function(){e=[],n=[],t(e,n)},r.cell=function(t,r){var a,o=e.length,i=n.length,s=-1,c=-1;for(a=0;o>a;a++)if(r>=e[a][0]&&e[a][1]>r){s=a;break}for(a=0;i>a;a++)if(t>=n[a][0]&&n[a][1]>t){c=a;break}return s>=0&&c>=0?{row:s,col:c}:null},r.rect=function(t,r,a,o,i){var s=i.offset();return{top:e[t][0]-s.top,left:n[r][0]-s.left,width:n[o][1]-n[r][0],height:e[a][1]-e[t][0]}}}function ge(e){function n(t){pe(t);var n=e.cell(t.pageX,t.pageY);(!n!=!i||n&&(n.row!=i.row||n.col!=i.col))&&(n?(o||(o=n),a(n,o,n.row-o.row,n.col-o.col)):a(n,o),i=n)}var r,a,o,i,s=this;s.start=function(s,c,l){a=s,o=i=null,e.build(),n(c),r=l||"mousemove",t(document).bind(r,n)},s.stop=function(){return t(document).unbind(r,n),i}}function pe(t){t.pageX===e&&(t.pageX=t.originalEvent.pageX,t.pageY=t.originalEvent.pageY)}function me(t){function n(e){return a[e]=a[e]||t(e)}var r=this,a={},o={},i={};r.left=function(t){return o[t]=o[t]===e?n(t).position().left:o[t]},r.right=function(t){return i[t]=i[t]===e?r.left(t)+n(t).width():i[t]},r.clear=function(){a={},o={},i={}}}var ye={defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberCalculation:"iso",weekNumberTitle:"W",allDayDefault:!0,ignoreTimezone:!0,lazyFetching:!0,startParam:"start",endParam:"end",titleFormat:{month:"MMMM yyyy",week:"MMM d[ yyyy]{ '—'[ MMM] d yyyy}",day:"dddd, MMM d, yyyy"},columnFormat:{month:"ddd",week:"ddd M/d",day:"dddd M/d"},timeFormat:{"":"h(:mm)t"},isRTL:!1,firstDay:0,monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],buttonText:{prev:"‹ ",next:"› ",prevYear:"« ",nextYear:"» ",today:"today",month:"month",week:"week",day:"day"},theme:!1,buttonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e"},unselectAuto:!0,dropAccept:"*"},be={header:{left:"next,prev today",center:"",right:"title"},buttonText:{prev:"› ",next:"‹ ",prevYear:"» ",nextYear:"« "},buttonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w"}},we=t.fullCalendar={version:"1.6.1"},De=we.views={};t.fn.fullCalendar=function(n){if("string"==typeof n){var a,o=Array.prototype.slice.call(arguments,1);return this.each(function(){var r=t.data(this,"fullCalendar");if(r&&t.isFunction(r[n])){var i=r[n].apply(r,o);a===e&&(a=i),"destroy"==n&&t.removeData(this,"fullCalendar")}}),a!==e?a:this}var i=n.eventSources||[];return delete n.eventSources,n.events&&(i.push(n.events),delete n.events),n=t.extend(!0,{},ye,n.isRTL||n.isRTL===e&&ye.isRTL?be:{},n),this.each(function(e,a){var o=t(a),s=new r(o,n,i);o.data("fullCalendar",s),s.render()}),this},we.sourceNormalizers=[],we.sourceFetchers=[];var Me={dataType:"json",cache:!1},Ce=1;we.addDays=c,we.cloneDate=d,we.parseDate=m,we.parseISO8601=y,we.parseTime=b,we.formatDate=w,we.formatDates=D;var Se=["sun","mon","tue","wed","thu","fri","sat"],Ee=864e5,xe=36e5,Te=6e4,ke={s:function(t){return t.getSeconds()},ss:function(t){return P(t.getSeconds())},m:function(t){return t.getMinutes()},mm:function(t){return P(t.getMinutes())},h:function(t){return t.getHours()%12||12},hh:function(t){return P(t.getHours()%12||12)},H:function(t){return t.getHours()},HH:function(t){return P(t.getHours())},d:function(t){return t.getDate()},dd:function(t){return P(t.getDate())},ddd:function(t,e){return e.dayNamesShort[t.getDay()]},dddd:function(t,e){return e.dayNames[t.getDay()]},M:function(t){return t.getMonth()+1},MM:function(t){return P(t.getMonth()+1)},MMM:function(t,e){return e.monthNamesShort[t.getMonth()]},MMMM:function(t,e){return e.monthNames[t.getMonth()]},yy:function(t){return(t.getFullYear()+"").substring(2)},yyyy:function(t){return t.getFullYear()},t:function(t){return 12>t.getHours()?"a":"p"},tt:function(t){return 12>t.getHours()?"am":"pm"},T:function(t){return 12>t.getHours()?"A":"P"},TT:function(t){return 12>t.getHours()?"AM":"PM"},u:function(t){return w(t,"yyyy-MM-dd'T'HH:mm:ss'Z'")},S:function(t){var e=t.getDate();return e>10&&20>e?"th":["st","nd","rd"][e%10-1]||"th"},w:function(t,e){return e.weekNumberCalculation(t)},W:function(t){return M(t)}};we.dateFormatters=ke,we.applyAll=G,De.month=te,De.basicWeek=ee,De.basicDay=ne,n({weekMode:"fixed"}),De.agendaWeek=oe,De.agendaDay=ie,n({allDaySlot:!0,allDayText:"all-day",firstHour:6,slotMinutes:30,defaultEventMinutes:120,axisFormat:"h(:mm)tt",timeFormat:{agenda:"h:mm{ - h:mm}"},dragOpacity:{agenda:.5},minTime:0,maxTime:24})})(jQuery);
\ No newline at end of file
diff --git a/3rdparty/fullcalendar/js/gcal.js b/3rdparty/fullcalendar/js/gcal.js
new file mode 100644
index 00000000..e3008a93
--- /dev/null
+++ b/3rdparty/fullcalendar/js/gcal.js
@@ -0,0 +1,107 @@
+/*!
+ * FullCalendar v1.6.1 Google Calendar Plugin
+ * Docs & License: http://arshaw.com/fullcalendar/
+ * (c) 2013 Adam Shaw
+ */
+
+(function($) {
+
+
+var fc = $.fullCalendar;
+var formatDate = fc.formatDate;
+var parseISO8601 = fc.parseISO8601;
+var addDays = fc.addDays;
+var applyAll = fc.applyAll;
+
+
+fc.sourceNormalizers.push(function(sourceOptions) {
+ if (sourceOptions.dataType == 'gcal' ||
+ sourceOptions.dataType === undefined &&
+ (sourceOptions.url || '').match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//)) {
+ sourceOptions.dataType = 'gcal';
+ if (sourceOptions.editable === undefined) {
+ sourceOptions.editable = false;
+ }
+ }
+});
+
+
+fc.sourceFetchers.push(function(sourceOptions, start, end) {
+ if (sourceOptions.dataType == 'gcal') {
+ return transformOptions(sourceOptions, start, end);
+ }
+});
+
+
+function transformOptions(sourceOptions, start, end) {
+
+ var success = sourceOptions.success;
+ var data = $.extend({}, sourceOptions.data || {}, {
+ 'start-min': formatDate(start, 'u'),
+ 'start-max': formatDate(end, 'u'),
+ 'singleevents': true,
+ 'max-results': 9999
+ });
+
+ var ctz = sourceOptions.currentTimezone;
+ if (ctz) {
+ data.ctz = ctz = ctz.replace(' ', '_');
+ }
+
+ return $.extend({}, sourceOptions, {
+ url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?',
+ dataType: 'jsonp',
+ data: data,
+ startParam: false,
+ endParam: false,
+ success: function(data) {
+ var events = [];
+ if (data.feed.entry) {
+ $.each(data.feed.entry, function(i, entry) {
+ var startStr = entry['gd$when'][0]['startTime'];
+ var start = parseISO8601(startStr, true);
+ var end = parseISO8601(entry['gd$when'][0]['endTime'], true);
+ var allDay = startStr.indexOf('T') == -1;
+ var url;
+ $.each(entry.link, function(i, link) {
+ if (link.type == 'text/html') {
+ url = link.href;
+ if (ctz) {
+ url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + ctz;
+ }
+ }
+ });
+ if (allDay) {
+ addDays(end, -1); // make inclusive
+ }
+ events.push({
+ id: entry['gCal$uid']['value'],
+ title: entry['title']['$t'],
+ url: url,
+ start: start,
+ end: end,
+ allDay: allDay,
+ location: entry['gd$where'][0]['valueString'],
+ description: entry['content']['$t']
+ });
+ });
+ }
+ var args = [events].concat(Array.prototype.slice.call(arguments, 1));
+ var res = applyAll(success, this, args);
+ if ($.isArray(res)) {
+ return res;
+ }
+ return events;
+ }
+ });
+
+}
+
+
+// legacy
+fc.gcalFeed = function(url, sourceOptions) {
+ return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' });
+};
+
+
+})(jQuery);
diff --git a/index.php b/index.php
index b33f8bc2..f7213648 100644
--- a/index.php
+++ b/index.php
@@ -26,8 +26,8 @@ if(OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'currentview', 'mon
OCP\Config::setUserValue(OCP\USER::getUser(), "calendar", "currentview", "list");
}
-OCP\Util::addscript('3rdparty/fullcalendar', 'fullcalendar');
-OCP\Util::addStyle('3rdparty/fullcalendar', 'fullcalendar');
+OCP\Util::addscript('calendar/3rdparty/fullcalendar', 'fullcalendar');
+OCP\Util::addStyle('calendar/3rdparty/fullcalendar', 'fullcalendar');
OCP\Util::addscript('3rdparty/timepicker', 'jquery.ui.timepicker');
OCP\Util::addStyle('3rdparty/timepicker', 'jquery.ui.timepicker');
if(OCP\Config::getUserValue(OCP\USER::getUser(), "calendar", "timezone") == null || OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezonedetection') == 'true') {