Selaa lähdekoodia

Merge remote-tracking branch 'remotes/upstream/jquery-removal' into HEAD

acerix 7 vuotta sitten
vanhempi
sitoutus
fbf30abde0
53 muutettua tiedostoa jossa 1484 lisäystä ja 1334 poistoa
  1. 4 1
      bin/publish-release.sh
  2. 0 1
      bower.json
  3. 0 1
      composer.json
  4. 16 16
      demos/agenda-views.html
  5. 14 14
      demos/background-events.html
  6. 16 16
      demos/basic-views.html
  7. 16 16
      demos/default.html
  8. 139 0
      demos/external-dragging-dragula.html
  9. 1 0
      demos/external-dragging-jquery-ui.html
  10. 1 1
      demos/json.html
  11. 15 15
      demos/json/events.json
  12. 16 16
      demos/list-views.html
  13. 16 16
      demos/locales.html
  14. 16 16
      demos/selectable.html
  15. 16 16
      demos/themes.html
  16. 1 1
      demos/timezones.html
  17. 16 16
      demos/week-numbers.html
  18. 1 0
      karma.config.js
  19. 256 301
      package-lock.json
  20. 8 8
      package.json
  21. 48 0
      plugins/dragula/main.ts
  22. 1 1
      plugins/gcal/GcalEventSource.ts
  23. 50 0
      plugins/jquery-ui-draggable/main.ts
  24. 42 1
      src/Calendar.ts
  25. 3 4
      src/View.ts
  26. 4 17
      src/common/DragListener.ts
  27. 8 2
      src/common/Mixin.ts
  28. 20 0
      src/component/DateComponent.ts
  29. 31 18
      src/component/InteractiveDateComponent.ts
  30. 78 104
      src/component/interactions/ExternalDropping.ts
  31. 0 1
      src/main.ts
  32. 17 4
      src/types/input-types.ts
  33. 0 51
      src/types/jquery-hooks.ts
  34. 4 1
      tasks/archive.js
  35. 9 5
      tasks/example-repos.js
  36. 2 1
      tests/automated/legacy/external-dnd-advanced.js
  37. 33 37
      tests/automated/legacy/forceEventDuration.js
  38. 46 51
      tests/automated/legacy/header-rendering.js
  39. 18 24
      tests/automated/legacy/locale.js
  40. 12 34
      tests/automated/legacy/maxTime.js
  41. 36 36
      tests/automated/legacy/monthNames.js
  42. 32 33
      tests/automated/legacy/monthNamesShort.js
  43. 13 14
      tests/automated/legacy/navLinks.js
  44. 3 8
      tests/automated/legacy/nextDayThreshold.js
  45. 7 8
      tests/automated/legacy/nowIndicator.js
  46. 43 51
      tests/automated/legacy/refetchEvents.js
  47. 37 40
      tests/automated/legacy/removeEvents.js
  48. 47 44
      tests/automated/legacy/scroll-state.js
  49. 13 30
      tests/automated/legacy/slotDuration.js
  50. 194 174
      tests/automated/legacy/updateEvent.js
  51. 26 38
      tests/automated/legacy/viewDestroy.js
  52. 26 31
      tests/automated/legacy/weekNumberCalculation.js
  53. 13 0
      webpack.config.js

+ 4 - 1
bin/publish-release.sh

@@ -25,8 +25,11 @@ success=0
 current_branch=$(git symbolic-ref --quiet --short HEAD)
 
 # temporarily checkout the tag's commit, publish to NPM
+echo
+echo 'NOTE!!! publishing with "alpha" tag (whereas default is "latest")'
+echo
 git checkout --quiet "v$version"
-if npm publish
+if npm publish --tag alpha
 then
   success=1
 fi

+ 0 - 1
bower.json

@@ -22,7 +22,6 @@
   },
   "copyright": "2018 Adam Shaw",
   "dependencies": {
-    "jquery": "2 - 3",
     "moment": "^2.20.1"
   },
   "main": [

+ 0 - 1
composer.json

@@ -20,7 +20,6 @@
   "minimum-stability": "stable",
   "require": {
     "robloach/component-installer": "*",
-    "components/jquery": "2 - 3",
     "moment/moment": "^2.20.1"
   },
   "extra": {

+ 16 - 16
demos/agenda-views.html

@@ -17,64 +17,64 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay,listWeek'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
       editable: true,
       eventLimit: true, // allow "more" link when too many events
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01',
+          start: '2018-04-01',
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 14 - 14
demos/background-events.html

@@ -17,57 +17,57 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay,listMonth'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
       businessHours: true, // display business hours
       editable: true,
       events: [
         {
           title: 'Business Lunch',
-          start: '2018-03-03T13:00:00',
+          start: '2018-04-03T13:00:00',
           constraint: 'businessHours'
         },
         {
           title: 'Meeting',
-          start: '2018-03-13T11:00:00',
+          start: '2018-04-13T11:00:00',
           constraint: 'availableForMeeting', // defined below
           color: '#257e4a'
         },
         {
           title: 'Conference',
-          start: '2018-03-18',
-          end: '2018-03-20'
+          start: '2018-04-18',
+          end: '2018-04-20'
         },
         {
           title: 'Party',
-          start: '2018-03-29T20:00:00'
+          start: '2018-04-29T20:00:00'
         },
 
         // areas where "Meeting" must be dropped
         {
           id: 'availableForMeeting',
-          start: '2018-03-11T10:00:00',
-          end: '2018-03-11T16:00:00',
+          start: '2018-04-11T10:00:00',
+          end: '2018-04-11T16:00:00',
           rendering: 'background'
         },
         {
           id: 'availableForMeeting',
-          start: '2018-03-13T10:00:00',
-          end: '2018-03-13T16:00:00',
+          start: '2018-04-13T10:00:00',
+          end: '2018-04-13T16:00:00',
           rendering: 'background'
         },
 
         // red areas where no events can be dropped
         {
-          start: '2018-03-24',
-          end: '2018-03-28',
+          start: '2018-04-24',
+          end: '2018-04-28',
           overlap: false,
           rendering: 'background',
           color: '#ff9f89'
         },
         {
-          start: '2018-03-06',
-          end: '2018-03-08',
+          start: '2018-04-06',
+          end: '2018-04-08',
           overlap: false,
           rendering: 'background',
           color: '#ff9f89'

+ 16 - 16
demos/basic-views.html

@@ -17,64 +17,64 @@
         center: 'title',
         right: 'month,basicWeek,basicDay'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
       editable: true,
       eventLimit: true, // allow "more" link when too many events
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01'
+          start: '2018-04-01'
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 16 - 16
demos/default.html

@@ -12,63 +12,63 @@
     var calendarEl = document.getElementById('calendar');
 
     var calendar = new FullCalendar.Calendar(calendarEl, {
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       editable: true,
       eventLimit: true, // allow "more" link when too many events
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01'
+          start: '2018-04-01'
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 139 - 0
demos/external-dragging-dragula.html

@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset='utf-8' />
+<link href='../node_modules/dragula/dist/dragula.css' rel='stylesheet' />
+<link href='../dist/fullcalendar.css' rel='stylesheet' />
+<link href='../dist/fullcalendar.print.css' rel='stylesheet' media='print' />
+<script src='../node_modules/moment/moment.js'></script>
+<script src='../node_modules/dragula/dist/dragula.js'></script>
+<script src='../dist/fullcalendar.js'></script>
+<script src='../dist/dragula.js'></script>
+<script>
+
+  document.addEventListener('DOMContentLoaded', function() {
+
+    /* initialize the external events
+    -----------------------------------------------------------------*/
+
+    var containerEl = document.getElementById('external-events-list');
+    var eventEls = Array.prototype.slice.call(
+      containerEl.querySelectorAll('.fc-event')
+    );
+
+    eventEls.forEach(function(eventEl) {
+      eventEl.setAttribute('data-event', JSON.stringify({
+        title: eventEl.innerText.trim(),
+        stick: true
+      }));
+    });
+
+    FullCalendar.dragula({
+      containers: [ containerEl ],
+      copy: true
+    });
+
+    /* initialize the calendar
+    -----------------------------------------------------------------*/
+
+    var calendarEl = document.getElementById('calendar');
+    var calendar = new FullCalendar.Calendar(calendarEl, {
+      header: {
+        left: 'prev,next today',
+        center: 'title',
+        right: 'month,agendaWeek,agendaDay'
+      },
+      editable: true,
+      droppable: true, // this allows things to be dropped onto the calendar
+      drop: function() {
+        // is the "remove after drop" checkbox checked?
+        if (document.getElementById('drop-remove').checked) {
+          // if so, remove the element from the "Draggable Events" list
+          this.parentNode.removeChild(this);
+        }
+      }
+    });
+    calendar.render();
+
+  });
+
+</script>
+<style>
+
+  body {
+    margin-top: 40px;
+    text-align: center;
+    font-size: 14px;
+    font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
+  }
+
+  #wrap {
+    width: 1100px;
+    margin: 0 auto;
+  }
+
+  #external-events {
+    float: left;
+    width: 150px;
+    padding: 0 10px;
+    border: 1px solid #ccc;
+    background: #eee;
+    text-align: left;
+  }
+
+  #external-events h4 {
+    font-size: 16px;
+    margin-top: 0;
+    padding-top: 1em;
+  }
+
+  #external-events .fc-event {
+    margin: 10px 0;
+    cursor: pointer;
+  }
+
+  #external-events p {
+    margin: 1.5em 0;
+    font-size: 11px;
+    color: #666;
+  }
+
+  #external-events p input {
+    margin: 0;
+    vertical-align: middle;
+  }
+
+  #calendar {
+    float: right;
+    width: 900px;
+  }
+
+</style>
+</head>
+<body>
+  <div id='wrap'>
+
+    <div id='external-events'>
+      <h4>Draggable Events</h4>
+
+      <div id='external-events-list'>
+        <div class='fc-event'>My Event 1</div>
+        <div class='fc-event'>My Event 2</div>
+        <div class='fc-event'>My Event 3</div>
+        <div class='fc-event'>My Event 4</div>
+        <div class='fc-event'>My Event 5</div>
+      </div>
+
+      <p>
+        <input type='checkbox' id='drop-remove' />
+        <label for='drop-remove'>remove after drop</label>
+      </p>
+    </div>
+
+    <div id='calendar'></div>
+
+    <div style='clear:both'></div>
+
+  </div>
+</body>
+</html>

+ 1 - 0
demos/external-dragging.html → demos/external-dragging-jquery-ui.html

@@ -8,6 +8,7 @@
 <script src='../node_modules/jquery/dist/jquery.js'></script>
 <script src='../node_modules/components-jqueryui/jquery-ui.js'></script>
 <script src='../dist/fullcalendar.js'></script>
+<script src='../dist/jquery-ui-draggable.js'></script>
 <script>
 
   document.addEventListener('DOMContentLoaded', function() {

+ 1 - 1
demos/json.html

@@ -18,7 +18,7 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay,listWeek'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       editable: true,
       navLinks: true, // can click day/week names to navigate views
       eventLimit: true, // allow "more" link when too many events

+ 15 - 15
demos/json/events.json

@@ -1,56 +1,56 @@
 [
   {
     "title": "All Day Event",
-    "start": "2018-03-01"
+    "start": "2018-04-01"
   },
   {
     "title": "Long Event",
-    "start": "2018-03-07",
-    "end": "2018-03-10"
+    "start": "2018-04-07",
+    "end": "2018-04-10"
   },
   {
     "id": "999",
     "title": "Repeating Event",
-    "start": "2018-03-09T16:00:00-05:00"
+    "start": "2018-04-09T16:00:00-05:00"
   },
   {
     "id": "999",
     "title": "Repeating Event",
-    "start": "2018-03-16T16:00:00-05:00"
+    "start": "2018-04-16T16:00:00-05:00"
   },
   {
     "title": "Conference",
-    "start": "2018-03-11",
-    "end": "2018-03-13"
+    "start": "2018-04-11",
+    "end": "2018-04-13"
   },
   {
     "title": "Meeting",
-    "start": "2018-03-12T10:30:00-05:00",
-    "end": "2018-03-12T12:30:00-05:00"
+    "start": "2018-04-12T10:30:00-05:00",
+    "end": "2018-04-12T12:30:00-05:00"
   },
   {
     "title": "Lunch",
-    "start": "2018-03-12T12:00:00-05:00"
+    "start": "2018-04-12T12:00:00-05:00"
   },
   {
     "title": "Meeting",
-    "start": "2018-03-12T14:30:00-05:00"
+    "start": "2018-04-12T14:30:00-05:00"
   },
   {
     "title": "Happy Hour",
-    "start": "2018-03-12T17:30:00-05:00"
+    "start": "2018-04-12T17:30:00-05:00"
   },
   {
     "title": "Dinner",
-    "start": "2018-03-12T20:00:00"
+    "start": "2018-04-12T20:00:00"
   },
   {
     "title": "Birthday Party",
-    "start": "2018-03-13T07:00:00-05:00"
+    "start": "2018-04-13T07:00:00-05:00"
   },
   {
     "title": "Click for Google",
     "url": "http://google.com/",
-    "start": "2018-03-28"
+    "start": "2018-04-28"
   }
 ]

+ 16 - 16
demos/list-views.html

@@ -26,64 +26,64 @@
       },
 
       defaultView: 'listWeek',
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
       editable: true,
       eventLimit: true, // allow "more" link when too many events
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01'
+          start: '2018-04-01'
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 16 - 16
demos/locales.html

@@ -20,7 +20,7 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay,listMonth'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       locale: initialLocaleCode,
       buttonIcons: false, // show the prev/next text
       weekNumbers: true,
@@ -30,57 +30,57 @@
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01'
+          start: '2018-04-01'
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 16 - 16
demos/selectable.html

@@ -17,7 +17,7 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
       selectable: true,
       selectHelper: true,
@@ -39,57 +39,57 @@
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01'
+          start: '2018-04-01'
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 16 - 16
demos/themes.html

@@ -24,7 +24,7 @@
             center: 'title',
             right: 'month,agendaWeek,agendaDay,listMonth'
           },
-          defaultDate: '2018-03-12',
+          defaultDate: '2018-04-12',
           weekNumbers: true,
           navLinks: true, // can click day/week names to navigate views
           editable: true,
@@ -32,57 +32,57 @@
           events: [
             {
               title: 'All Day Event',
-              start: '2018-03-01'
+              start: '2018-04-01'
             },
             {
               title: 'Long Event',
-              start: '2018-03-07',
-              end: '2018-03-10'
+              start: '2018-04-07',
+              end: '2018-04-10'
             },
             {
               id: 999,
               title: 'Repeating Event',
-              start: '2018-03-09T16:00:00'
+              start: '2018-04-09T16:00:00'
             },
             {
               id: 999,
               title: 'Repeating Event',
-              start: '2018-03-16T16:00:00'
+              start: '2018-04-16T16:00:00'
             },
             {
               title: 'Conference',
-              start: '2018-03-11',
-              end: '2018-03-13'
+              start: '2018-04-11',
+              end: '2018-04-13'
             },
             {
               title: 'Meeting',
-              start: '2018-03-12T10:30:00',
-              end: '2018-03-12T12:30:00'
+              start: '2018-04-12T10:30:00',
+              end: '2018-04-12T12:30:00'
             },
             {
               title: 'Lunch',
-              start: '2018-03-12T12:00:00'
+              start: '2018-04-12T12:00:00'
             },
             {
               title: 'Meeting',
-              start: '2018-03-12T14:30:00'
+              start: '2018-04-12T14:30:00'
             },
             {
               title: 'Happy Hour',
-              start: '2018-03-12T17:30:00'
+              start: '2018-04-12T17:30:00'
             },
             {
               title: 'Dinner',
-              start: '2018-03-12T20:00:00'
+              start: '2018-04-12T20:00:00'
             },
             {
               title: 'Birthday Party',
-              start: '2018-03-13T07:00:00'
+              start: '2018-04-13T07:00:00'
             },
             {
               title: 'Click for Google',
               url: 'http://google.com/',
-              start: '2018-03-28'
+              start: '2018-04-28'
             }
           ]
         });

+ 1 - 1
demos/timezones.html

@@ -18,7 +18,7 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay,listWeek'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
       editable: true,
       selectable: true,

+ 16 - 16
demos/week-numbers.html

@@ -17,7 +17,7 @@
         center: 'title',
         right: 'month,agendaWeek,agendaDay,listWeek'
       },
-      defaultDate: '2018-03-12',
+      defaultDate: '2018-04-12',
       navLinks: true, // can click day/week names to navigate views
 
       weekNumbers: true,
@@ -29,57 +29,57 @@
       events: [
         {
           title: 'All Day Event',
-          start: '2018-03-01'
+          start: '2018-04-01'
         },
         {
           title: 'Long Event',
-          start: '2018-03-07',
-          end: '2018-03-10'
+          start: '2018-04-07',
+          end: '2018-04-10'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-09T16:00:00'
+          start: '2018-04-09T16:00:00'
         },
         {
           id: 999,
           title: 'Repeating Event',
-          start: '2018-03-16T16:00:00'
+          start: '2018-04-16T16:00:00'
         },
         {
           title: 'Conference',
-          start: '2018-03-11',
-          end: '2018-03-13'
+          start: '2018-04-11',
+          end: '2018-04-13'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T10:30:00',
-          end: '2018-03-12T12:30:00'
+          start: '2018-04-12T10:30:00',
+          end: '2018-04-12T12:30:00'
         },
         {
           title: 'Lunch',
-          start: '2018-03-12T12:00:00'
+          start: '2018-04-12T12:00:00'
         },
         {
           title: 'Meeting',
-          start: '2018-03-12T14:30:00'
+          start: '2018-04-12T14:30:00'
         },
         {
           title: 'Happy Hour',
-          start: '2018-03-12T17:30:00'
+          start: '2018-04-12T17:30:00'
         },
         {
           title: 'Dinner',
-          start: '2018-03-12T20:00:00'
+          start: '2018-04-12T20:00:00'
         },
         {
           title: 'Birthday Party',
-          start: '2018-03-13T07:00:00'
+          start: '2018-04-13T07:00:00'
         },
         {
           title: 'Click for Google',
           url: 'http://google.com/',
-          start: '2018-03-28'
+          start: '2018-04-28'
         }
       ]
     });

+ 1 - 0
karma.config.js

@@ -26,6 +26,7 @@ module.exports = function(config) {
       'dist/fullcalendar.js',
       'dist/fullcalendar.css',
       'dist/gcal.js',
+      'dist/jquery-ui-draggable.js',
       'dist/locale-all.js',
 
       // temporary. needs to go first.

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 256 - 301
package-lock.json


+ 8 - 8
package.json

@@ -28,18 +28,18 @@
   "version": "0.0.0",
   "releaseDate": "1970-01-01",
   "devDependencies": {
-    "@types/jquery": "2.0.47",
     "awesome-typescript-loader": "^3.5.0",
     "bootstrap": "^3.3.7",
     "components-jqueryui": "github:components/jqueryui",
-    "css-loader": "^0.28.10",
+    "css-loader": "^0.28.11",
     "del": "^2.2.1",
+    "dragula": "^3.7.2",
     "dts-generator": "^2.1.0",
-    "eslint": "^4.18.2",
+    "eslint": "^4.19.1",
     "eslint-config-standard": "^11.0.0",
-    "eslint-plugin-import": "^2.9.0",
+    "eslint-plugin-import": "^2.11.0",
     "eslint-plugin-node": "^5.2.1",
-    "eslint-plugin-promise": "^3.6.0",
+    "eslint-plugin-promise": "^3.7.0",
     "eslint-plugin-standard": "^3.0.1",
     "extract-text-webpack-plugin": "^3.0.2",
     "glob": "^7.1.2",
@@ -67,14 +67,14 @@
     "karma-verbose-reporter": "0.0.6",
     "moment-timezone": "^0.5.5",
     "native-promise-only": "^0.8.1",
-    "node-sass": "^4.7.2",
+    "node-sass": "^4.8.3",
     "sass-loader": "^6.0.7",
     "tslib": "^1.8.0",
     "tslint": "^5.8.0",
     "tslint-config-standard": "^7.0.0",
-    "typescript": "^2.7.2",
+    "typescript": "^2.8.1",
     "webpack": "^3.11.0",
-    "webpack-stream": "^4.0.2",
+    "webpack-stream": "^4.0.3",
     "xhr-mock": "^2.3.1",
     "yargs": "^4.8.1"
   },

+ 48 - 0
plugins/dragula/main.ts

@@ -0,0 +1,48 @@
+import * as dragula from 'dragula'
+import * as FullCalendar from 'fullcalendar'
+
+
+let visibleCalendars = []
+
+FullCalendar.Calendar.on('initialRender', function(calendar) {
+  visibleCalendars.push(calendar)
+
+  calendar.one('destroy', function() {
+    FullCalendar.removeExact(visibleCalendars, calendar)
+  })
+})
+
+
+let recentEvent
+
+[
+  'mousedown',
+  'touchstart',
+  'pointerdown'
+].forEach(function(eventName) {
+  document.addEventListener(eventName, function(ev) {
+    recentEvent = ev
+  })
+})
+
+
+function constructDragula(...args) {
+  let drake = dragula.apply(window, args)
+
+  drake.on('drag', function(draggingEl) { // dragging started
+    for (let calendar of visibleCalendars) {
+      calendar.handlExternalDragStart(
+        recentEvent,
+        draggingEl,
+        false // have FullCalendar watch for mouse/touch events
+          // because dragula doesn't expose a 'move' event
+      )
+    }
+  })
+
+  return drake
+}
+
+
+(FullCalendar as any).dragula = constructDragula
+export default constructDragula

+ 1 - 1
plugins/gcal/GcalEventSource.ts

@@ -49,7 +49,7 @@ export default class GcalEventSource extends EventSource {
 
         this.calendar.popLoading()
 
-        if (res.body && res.body.error) {
+        if (res && res.body && res.body.error) {
           this.reportError('Google Calendar API: ' + res.body.error.message, res.body.error.errors)
         } else if (error) {
           this.reportError('Google Calendar API', error)

+ 50 - 0
plugins/jquery-ui-draggable/main.ts

@@ -0,0 +1,50 @@
+import * as $ from 'jquery'
+import { Calendar, ExternalDropping } from 'fullcalendar'
+
+let $document = $(document)
+
+Calendar.on('initialRender', function(calendar) {
+
+  const handleDragStart = function(ev, ui) {
+
+    const handleDragMove = (ev, ui) => {
+      calendar.handleExternalDragMove(ev)
+    }
+
+    const handleDragStop = (ev, ui) => {
+      calendar.handleExternalDragStop(ev)
+      $document
+        .off('drag', handleDragMove)
+        .off('dragstop', handleDragStop)
+    }
+
+    $document
+      .on('drag', handleDragMove)
+      .on('dragstop', handleDragStop)
+
+    calendar.handlExternalDragStart(
+      ev.originalEvent,
+      ((ui && ui.item) ? ui.item[0] : null) || ev.target,
+      ev.name === 'dragstart' // don't watch mouse/touch movements if doing jqui drag (not sort)
+    )
+  }
+
+  $document.on('dragstart sortstart', handleDragStart)
+
+  calendar.one('destroy', function(calendar) {
+    $document.off('dragstart sortstart', handleDragStart)
+  })
+})
+
+
+const origGetEmbeddedElData = ExternalDropping.getEmbeddedElData
+
+ExternalDropping.getEmbeddedElData = function(el, name, shouldParseJson = false) {
+  let val = $(el).data(name) // will automatically parse JSON
+
+  if (val != null) {
+    return val
+  }
+
+  return origGetEmbeddedElData.apply(ExternalDropping, arguments)
+}

+ 42 - 1
src/Calendar.ts

@@ -37,6 +37,11 @@ export default class Calendar {
   static englishDefaults: any = englishDefaults
   static rtlDefaults: any = rtlDefaults
 
+  // global handler registry
+  static on: EmitterInterface['on']
+  static off: EmitterInterface['off']
+  static trigger: EmitterInterface['trigger']
+
   on: EmitterInterface['on']
   one: EmitterInterface['one']
   off: EmitterInterface['off']
@@ -405,10 +410,15 @@ export default class Calendar {
         )
       )
     }
+
+    this.trigger('initialRender')
+    Calendar.trigger('initialRender', this)
   }
 
 
   destroy() {
+    let wasRendered = Boolean(this.contentEl && this.contentEl.parentNode)
+
     if (this.view) {
       this.clearView()
     }
@@ -433,7 +443,12 @@ export default class Calendar {
       this.windowResizeProxy = null
     }
 
-    GlobalEmitter.unneeded()
+    if (wasRendered) {
+      GlobalEmitter.unneeded()
+
+      this.trigger('destroy')
+      Calendar.trigger('destroy', this)
+    }
   }
 
 
@@ -821,6 +836,31 @@ export default class Calendar {
   }
 
 
+  // External Dragging
+  // -----------------------------------------------------------------------------------------------------------------
+
+
+  handlExternalDragStart(ev, el, skipBinding) {
+    if (this.view) {
+      this.view.handlExternalDragStart(ev, el, skipBinding)
+    }
+  }
+
+
+  handleExternalDragMove(ev) {
+    if (this.view) {
+      this.view.handleExternalDragMove(ev)
+    }
+  }
+
+
+  handleExternalDragStop(ev) {
+    if (this.view) {
+      this.view.handleExternalDragStop(ev)
+    }
+  }
+
+
   // Date Utils
   // -----------------------------------------------------------------------------------------------------------------
 
@@ -1314,6 +1354,7 @@ export default class Calendar {
 
 }
 
+EmitterMixin.mixIntoObj(Calendar) // for global registry
 EmitterMixin.mixInto(Calendar)
 ListenerMixin.mixInto(Calendar)
 

+ 3 - 4
src/View.ts

@@ -608,18 +608,18 @@ export default abstract class View extends InteractiveDateComponent {
   // Must be called when an external element, via jQuery UI, has been dropped onto the calendar.
   // `meta` is the parsed data that has been embedded into the dragging event.
   // `dropLocation` is an object that contains the new zoned start/end/allDay values for the event.
-  reportExternalDrop(singleEventDef, isEvent, isSticky, el, ev, ui) {
+  reportExternalDrop(singleEventDef, isEvent, isSticky, el, ev) {
 
     if (isEvent) {
       this.calendar.eventManager.addEventDef(singleEventDef, isSticky)
     }
 
-    this.triggerExternalDrop(singleEventDef, isEvent, el, ev, ui)
+    this.triggerExternalDrop(singleEventDef, isEvent, el, ev)
   }
 
 
   // Triggers external-drop handlers that have subscribed via the API
-  triggerExternalDrop(singleEventDef, isEvent, el, ev, ui) {
+  triggerExternalDrop(singleEventDef, isEvent, el, ev) {
 
     // trigger 'drop' regardless of whether element represents an event
     this.publiclyTrigger('drop', {
@@ -627,7 +627,6 @@ export default abstract class View extends InteractiveDateComponent {
       args: [
         singleEventDef.dateProfile.start.clone(),
         ev,
-        ui,
         this
       ]
     })

+ 4 - 17
src/common/DragListener.ts

@@ -33,7 +33,6 @@ export default class DragListener {
   listenTo: ListenerInterface['listenTo']
   stopListeningTo: ListenerInterface['stopListeningTo']
 
-  $document: any // jQuery object
   options: DragListenerOptions
   subjectEl: HTMLElement
 
@@ -50,7 +49,7 @@ export default class DragListener {
   isDelayEnded: boolean = false
   isDragging: boolean = false
   isTouch: boolean = false
-  isGeneric: boolean = false // initiated by 'dragstart' (jqui)
+  skipBinding: boolean = false // if true, don't watch mouse/touch events
 
   delay: any
   delayTimeoutId: any
@@ -81,7 +80,7 @@ export default class DragListener {
   // -----------------------------------------------------------------------------------------------------------------
 
 
-  startInteraction(ev, extraOptions: any= {}) {
+  startInteraction(ev, extraOptions: any = {}) {
 
     if (ev.type === 'mousedown') {
       if (GlobalEmitter.get().shouldIgnoreMouse()) {
@@ -104,7 +103,6 @@ export default class DragListener {
 
       this.isInteracting = true
       this.isTouch = getEvIsTouch(ev)
-      this.isGeneric = ev.type === 'dragstart'
       this.isDelayEnded = false
       this.isDistanceSurpassed = false
 
@@ -163,16 +161,8 @@ export default class DragListener {
     // so listen to the GlobalEmitter singleton, which is always bound, instead of the document directly.
     let globalEmitter = GlobalEmitter.get()
 
-    if (this.isGeneric) {
-      if (!this.$document && window['jQuery']) {
-        this.$document = window['jQuery'](document)
-      }
-      if (this.$document) { // we can only attach jQueryUI drag handlers to a jQuery element reference
-        this.listenTo(this.$document, { // might only work on iOS because of GlobalEmitter's bind :(
-          drag: this.handleMove,
-          dragstop: this.endInteraction
-        })
-      }
+    if (this.skipBinding) {
+      //
     } else if (this.isTouch) {
       this.listenTo(globalEmitter, {
         touchmove: this.handleTouchMove,
@@ -195,9 +185,6 @@ export default class DragListener {
 
   unbindHandlers() {
     this.stopListeningTo(GlobalEmitter.get())
-    if (this.$document) {
-      this.stopListeningTo(this.$document) // for isGeneric
-    }
   }
 
 

+ 8 - 2
src/common/Mixin.ts

@@ -1,10 +1,16 @@
 
 export default class Mixin {
 
+  // mix into a CLASS
   static mixInto(destClass) {
+    this.mixIntoObj(destClass.prototype)
+  }
+
+  // mix into ANY object
+  static mixIntoObj(destObj) {
     Object.getOwnPropertyNames(this.prototype).forEach((name) => { // copy methods
-      if (!destClass.prototype[name]) { // if destination class doesn't already define it
-        destClass.prototype[name] = this.prototype[name]
+      if (!destObj[name]) { // if destination doesn't already define it
+        destObj[name] = this.prototype[name]
       }
     })
   }

+ 20 - 0
src/component/DateComponent.ts

@@ -391,6 +391,26 @@ export default abstract class DateComponent extends Component {
   }
 
 
+  // EXTERNAL Drag-n-Drop
+  // ---------------------------------------------------------------------------------------------------------------
+  // Doesn't need to implement a response, but must pass to children
+
+
+  handlExternalDragStart(ev, el, skipBinding) {
+    this.callChildren('handlExternalDragStart', arguments)
+  }
+
+
+  handleExternalDragMove(ev) {
+    this.callChildren('handleExternalDragMove', arguments)
+  }
+
+
+  handleExternalDragStop(ev) {
+    this.callChildren('handleExternalDragStop', arguments)
+  }
+
+
   // Event Resizing
   // ---------------------------------------------------------------------------------------------------------------
 

+ 31 - 18
src/component/InteractiveDateComponent.ts

@@ -91,24 +91,6 @@ export default abstract class InteractiveDateComponent extends DateComponent {
   }
 
 
-  bindGlobalHandlers() {
-    super.bindGlobalHandlers()
-
-    if (this.externalDropping) {
-      this.externalDropping.bindToDocument()
-    }
-  }
-
-
-  unbindGlobalHandlers() {
-    super.unbindGlobalHandlers()
-
-    if (this.externalDropping) {
-      this.externalDropping.unbindFromDocument()
-    }
-  }
-
-
   bindDateHandlerToEl(el, name, handler) {
     el.addEventListener(name, (ev) => {
       if (
@@ -273,6 +255,37 @@ export default abstract class InteractiveDateComponent extends DateComponent {
   }
 
 
+  // EXTERNAL Drag-n-Drop
+  // ---------------------------------------------------------------------------------------------------------------
+
+
+  handlExternalDragStart(ev, el, skipBinding) {
+    if (this.externalDropping) {
+      this.externalDropping.handleDragStart(ev, el, skipBinding)
+    }
+
+    super.handlExternalDragStart(ev, el, skipBinding)
+  }
+
+
+  handleExternalDragMove(ev) {
+    if (this.externalDropping) {
+      this.externalDropping.handleDragMove(ev)
+    }
+
+    super.handleExternalDragMove(ev)
+  }
+
+
+  handleExternalDragStop(ev) {
+    if (this.externalDropping) {
+      this.externalDropping.handleDragStop(ev)
+    }
+
+    super.handleExternalDragStop(ev)
+  }
+
+
   // Event Resizing
   // ---------------------------------------------------------------------------------------------------------------
 

+ 78 - 104
src/component/interactions/ExternalDropping.ts

@@ -1,10 +1,8 @@
 import * as moment from 'moment'
-import * as exportHooks from '../../exports'
 import { assignTo } from '../../util/object'
 import { elementMatches } from '../../util/dom-manip'
 import { disableCursor, enableCursor } from '../../util/misc'
 import momentExt from '../../moment-ext'
-import { default as ListenerMixin, ListenerInterface } from '../../common/ListenerMixin'
 import HitDragListener from '../../common/HitDragListener'
 import SingleEventDef from '../../models/event/SingleEventDef'
 import EventInstanceGroup from '../../models/event/EventInstanceGroup'
@@ -14,14 +12,68 @@ import Interaction from './Interaction'
 
 export default class ExternalDropping extends Interaction {
 
-  listenTo: ListenerInterface['listenTo']
-  stopListeningTo: ListenerInterface['stopListeningTo']
+  static dataAttrPrefix: string = ''
 
-  $document: any
   dragListener: any
   isDragging: boolean = false // jqui-dragging an external element? boolean
 
 
+  // Given a jQuery element that might represent a dragged FullCalendar event, returns an intermediate data structure
+  // to be used for Event Object creation.
+  // A defined `.eventProps`, even when empty, indicates that an event should be created.
+  static getDraggedElMeta(el) {
+    let eventProps // properties for creating the event, not related to date/time
+    let startTime // a Duration
+    let duration
+    let stick
+
+    eventProps = ExternalDropping.getEmbeddedElData(el, 'event', true)
+
+    if (eventProps) {
+
+      // something like 1 or true. still signal event creation
+      if (typeof eventProps !== 'object') {
+        eventProps = {}
+      }
+
+      // pluck special-cased date/time properties
+      startTime = eventProps.start
+      if (startTime == null) { startTime = eventProps.time } // accept 'time' as well
+      duration = eventProps.duration
+      stick = eventProps.stick
+      delete eventProps.start
+      delete eventProps.time
+      delete eventProps.duration
+      delete eventProps.stick
+    }
+
+    // fallback to standalone attribute values for each of the date/time properties
+    if (startTime == null) { startTime = ExternalDropping.getEmbeddedElData(el, 'start') }
+    if (startTime == null) { startTime = ExternalDropping.getEmbeddedElData(el, 'time') } // accept 'time' as well
+    if (duration == null) { duration = ExternalDropping.getEmbeddedElData(el, 'duration') }
+    if (stick == null) { stick = ExternalDropping.getEmbeddedElData(el, 'stick', true) }
+
+    // massage into correct data types
+    startTime = startTime != null ? moment.duration(startTime) : null
+    duration = duration != null ? moment.duration(duration) : null
+    stick = Boolean(stick)
+
+    return { eventProps: eventProps, startTime: startTime, duration: duration, stick: stick }
+  }
+
+  static getEmbeddedElData(el, name, shouldParseJson = false) {
+    let prefix = ExternalDropping.dataAttrPrefix
+    let prefixedName = (prefix ? prefix + '-' : '') + name
+
+    let data = el.getAttribute('data-' + prefixedName) || null
+    if (data && shouldParseJson) {
+      data = JSON.parse(data)
+    }
+
+    return data
+  }
+
+
   /*
   component impements:
     - eventRangesToEventFootprints
@@ -39,51 +91,43 @@ export default class ExternalDropping extends Interaction {
   }
 
 
-  bindToDocument() {
-    if (!this.$document && window['jQuery']) {
-      this.$document = window['jQuery'](document)
-    }
-    if (this.$document) { // need jquery for attaching jqui handlers
-      this.listenTo(this.$document, {
-        dragstart: this.handleDragStart, // jqui
-        sortstart: this.handleDragStart // jqui
-      })
-    }
-  }
-
-
-  unbindFromDocument() {
-    if (this.$document) {
-      this.stopListeningTo(this.$document)
-    }
-  }
-
-
   // Called when a jQuery UI drag is initiated anywhere in the DOM
-  handleDragStart(ev, ui) {
-    let el
+  handleDragStart(ev, el, skipBinding) {
     let accept
 
     if (this.opt('droppable')) { // only listen if this setting is on
-      el = ((ui && ui.item) ? ui.item[0] : null) || ev.target
 
       // Test that the dragged element passes the dropAccept selector or filter function.
       // FYI, the default is "*" (matches all)
       accept = this.opt('dropAccept')
       if (typeof accept === 'function' ? accept.call(el, el) : elementMatches(el, accept)) {
         if (!this.isDragging) { // prevent double-listening if fired twice
-          this.listenToExternalDrag(el, ev, ui)
+          this.listenToExternalDrag(ev, el, skipBinding)
         }
       }
     }
   }
 
 
-  // Called when a jQuery UI drag starts and it needs to be monitored for dropping
-  listenToExternalDrag(el, ev, ui) {
+  handleDragMove(ev) {
+    if (this.dragListener) {
+      this.dragListener.handleMove(ev)
+    }
+  }
+
+
+  handleDragStop(ev) {
+    if (this.dragListener) {
+      this.dragListener.endInteraction(ev)
+    }
+  }
+
+
+  // Called when a 3rd-party draggable starts and it needs to be monitored for dropping
+  listenToExternalDrag(ev, el, skipBinding) {
     let component = this.component
     let view = this.view
-    let meta = getDraggedElMeta(el) // extra data about event drop, including possible event to create
+    let meta = ExternalDropping.getDraggedElMeta(el) // extra data about event drop, including possible event to create
     let singleEventDef // a null value signals an unsuccessful drag
 
     // listener that tracks mouse movement over date-associated pixel regions
@@ -140,7 +184,7 @@ export default class ExternalDropping extends Interaction {
             singleEventDef,
             Boolean(meta.eventProps), // isEvent
             Boolean(meta.stick), // isSticky
-            el, ev, ui
+            el, ev
           )
         }
 
@@ -149,6 +193,7 @@ export default class ExternalDropping extends Interaction {
       }
     })
 
+    dragListener.skipBinding = skipBinding
     dragListener.startDrag(ev) // start listening immediately
   }
 
@@ -195,74 +240,3 @@ export default class ExternalDropping extends Interaction {
   }
 
 }
-
-ListenerMixin.mixInto(ExternalDropping);
-
-
-/* External-Dragging-Element Data
-----------------------------------------------------------------------------------------------------------------------*/
-
-// Require all HTML5 data-* attributes used by FullCalendar to have this prefix.
-// A value of '' will query attributes like data-event. A value of 'fc' will query attributes like data-fc-event.
-(exportHooks as any).dataAttrPrefix = ''
-
-// Given a jQuery element that might represent a dragged FullCalendar event, returns an intermediate data structure
-// to be used for Event Object creation.
-// A defined `.eventProps`, even when empty, indicates that an event should be created.
-function getDraggedElMeta(el) {
-  let eventProps // properties for creating the event, not related to date/time
-  let startTime // a Duration
-  let duration
-  let stick
-
-  eventProps = getEmbeddedElData(el, 'event', true)
-
-  if (eventProps) {
-
-    // something like 1 or true. still signal event creation
-    if (typeof eventProps !== 'object') {
-      eventProps = {}
-    }
-
-    // pluck special-cased date/time properties
-    startTime = eventProps.start
-    if (startTime == null) { startTime = eventProps.time } // accept 'time' as well
-    duration = eventProps.duration
-    stick = eventProps.stick
-    delete eventProps.start
-    delete eventProps.time
-    delete eventProps.duration
-    delete eventProps.stick
-  }
-
-  // fallback to standalone attribute values for each of the date/time properties
-  if (startTime == null) { startTime = getEmbeddedElData(el, 'start') }
-  if (startTime == null) { startTime = getEmbeddedElData(el, 'time') } // accept 'time' as well
-  if (duration == null) { duration = getEmbeddedElData(el, 'duration') }
-  if (stick == null) { stick = getEmbeddedElData(el, 'stick') }
-
-  // massage into correct data types
-  startTime = startTime != null ? moment.duration(startTime) : null
-  duration = duration != null ? moment.duration(duration) : null
-  stick = Boolean(stick)
-
-  return { eventProps: eventProps, startTime: startTime, duration: duration, stick: stick }
-}
-
-
-function getEmbeddedElData(el, name, shouldParseJson = false) {
-  let prefix = (exportHooks as any).dataAttrPrefix
-  let prefixedName = (prefix ? prefix + '-' : '') + name
-
-  let data = el.getAttribute('data-' + prefixedName) || null
-  if (data && shouldParseJson) {
-    data = JSON.parse(data)
-  }
-
-  if (data === null && window['jQuery']) {
-    // jQuery will automatically parse JSON
-    data = window['jQuery'](el).data(prefixedName)
-  }
-
-  return data
-}

+ 0 - 1
src/main.ts

@@ -8,6 +8,5 @@ import './theme/config'
 import './basic/config'
 import './agenda/config'
 import './list/config'
-import './types/jquery-hooks'
 
 export = exportHooks

+ 17 - 4
src/types/input-types.ts

@@ -31,7 +31,9 @@ export interface EventOptionsBase {
   textColor?: string
 }
 
-export interface EventObjectInput extends EventOptionsBase, RangeInput { // used for input and toLegacy output
+// used for input and toLegacy output
+// when we expose the real EventObject, will need lots of changes
+export interface EventObjectInput extends EventOptionsBase, RangeInput {
   _id?: string
   id?: string | number
   title: string
@@ -44,12 +46,23 @@ export interface EventObjectInput extends EventOptionsBase, RangeInput { // used
 export type EventSourceFunction = (start: moment.Moment, end: moment.Moment, timezone: string, callback: ((events: EventObjectInput[]) => void)) => void
 export type EventSourceSimpleInput = EventObjectInput[] | EventSourceFunction | string
 
-export interface EventSourceExtendedInput extends EventOptionsBase, JQueryAjaxSettings {
-  url?: string
+export interface EventSourceExtendedInput extends EventOptionsBase {
+
+  // array
   events?: EventSourceSimpleInput
-  allDayDefault?: boolean
+
+  // json feed
+  url?: string
+  method?: string
+  data?: object | (() => object)
   startParam?: string
   endParam?: string
+  timezoneParam?: string
+  success?: (eventDefs: EventObjectInput[], ajaxRes: any) => void
+  error?: (error: any, ajaxRes: any) => void
+
+  // general
+  allDayDefault?: boolean
   eventDataTransform?(eventData: any): EventObjectInput
 }
 

+ 0 - 51
src/types/jquery-hooks.ts

@@ -1,51 +0,0 @@
-import * as moment from 'moment'
-import Calendar from '../Calendar'
-import View from '../View'
-import EventSource from '../models/event-source/EventSource'
-import { RangeInput, MomentInput, OptionsInput, EventObjectInput, EventSourceInput } from './input-types'
-
-declare global {
-
-  interface JQueryStatic {
-    fullCalendar: object // TODO: more specificity
-  }
-
-  interface JQuery {
-    fullCalendar(options?: OptionsInput): JQuery // initialization
-    fullCalendar(method: 'getCalendar'): Calendar
-    fullCalendar(method: 'getView'): View
-    fullCalendar(method: 'destroy'): JQuery
-    fullCalendar(method: 'option', name: string | object, value?: any): any
-    fullCalendar(method: 'isValidViewType', viewType: string): boolean
-    fullCalendar(method: 'changeView', viewName: string, dateOrRange?: RangeInput | MomentInput): JQuery
-    fullCalendar(method: 'zoomTo', newDate: moment.Moment, viewType?: string): JQuery
-    fullCalendar(method: 'prev'): JQuery
-    fullCalendar(method: 'next'): JQuery
-    fullCalendar(method: 'prevYear'): JQuery
-    fullCalendar(method: 'nextYear'): JQuery
-    fullCalendar(method: 'today'): JQuery
-    fullCalendar(method: 'gotoDate', zonedDateInput: any): JQuery
-    fullCalendar(method: 'incrementDate', delta: any): JQuery
-    fullCalendar(method: 'getDate'): moment.Moment
-    fullCalendar(method: 'render'): JQuery
-    fullCalendar(method: 'select', zonedStartInput: MomentInput, zonedEndInput?: MomentInput, resourceId?: string): JQuery
-    fullCalendar(method: 'unselect'): JQuery
-    fullCalendar(method: 'moment', ...args: any[]): moment.Moment
-    fullCalendar(method: 'getNow'): moment.Moment
-    fullCalendar(method: 'rerenderEvents'): JQuery
-    fullCalendar(method: 'refetchEvents'): JQuery
-    fullCalendar(method: 'renderEvents', eventInputs: EventObjectInput[], isSticky?: boolean): JQuery
-    fullCalendar(method: 'renderEvent', eventInput: EventObjectInput, isSticky?: boolean): JQuery
-    fullCalendar(method: 'removeEvents', legacyQuery?: any): JQuery
-    fullCalendar(method: 'clientEvents', legacyQuery: any): any
-    fullCalendar(method: 'updateEvents', eventPropsArray: EventObjectInput[]): JQuery
-    fullCalendar(method: 'updateEvent', eventProps: EventObjectInput): JQuery
-    fullCalendar(method: 'getEventSources'): EventSource
-    fullCalendar(method: 'getEventSourceById', id: any): EventSource
-    fullCalendar(method: 'addEventSource', sourceInput: EventSourceInput): JQuery
-    fullCalendar(method: 'removeEventSources', sourceMultiQuery: any): JQuery
-    fullCalendar(method: 'removeEventSource', sourceQuery: any): JQuery
-    fullCalendar(method: 'refetchEventSources', sourceMultiQuery: any): JQuery
-  }
-
-}

+ 4 - 1
tasks/archive.js

@@ -46,7 +46,9 @@ gulp.task('archive:deps', function() {
     'node_modules/moment/min/moment.min.js',
     'node_modules/superagent/superagent.js',
     'node_modules/jquery/dist/jquery.min.js', // only for draggable example
-    'node_modules/components-jqueryui/jquery-ui.min.js' // "
+    'node_modules/components-jqueryui/jquery-ui.min.js', // "
+    'node_modules/dragula/dist/dragula.min.js', // "
+    'node_modules/dragula/dist/dragula.min.css' // "
   ])
     .pipe(gulp.dest('tmp/' + packageId + '/lib/'))
 })
@@ -76,6 +78,7 @@ function transformDemoPath(path) {
   path = path.replace('../node_modules/superagent/', '../lib/')
   path = path.replace('../node_modules/jquery/dist/', '../lib/')
   path = path.replace('../node_modules/components-jqueryui/', '../lib/')
+  path = path.replace('../node_modules/dragula/dist/', '../lib/')
 
   // reroot dist files to archive root
   path = path.replace('../dist/', '../')

+ 9 - 5
tasks/example-repos.js

@@ -1,15 +1,19 @@
 const gulp = require('gulp')
 const gutil = require('gulp-util')
-const shell = require('gulp-shell')
 const modify = require('gulp-modify-file')
 
 // parsed command line arguments
 const { argv } = require('yargs')
 
-// try to build all example repos
-gulp.task('example-repos:build', [ 'webpack', 'ts-types' ], shell.task(
-  './bin/build-example-repos.sh'
-))
+// const shell = require('gulp-shell')
+// // try to build all example repos
+// gulp.task('example-repos:build', [ 'webpack', 'ts-types' ], shell.task(
+//   './bin/build-example-repos.sh'
+// ))
+
+gulp.task('example-repos:build', [ 'webpack', 'ts-types' ], function() {
+  gutil.log('\n\nTODO!!! fix example-repos for jquery removal\n\n')
+})
 
 // does a SINGLE example repo
 gulp.task('example-repo:bump', function(done) {

+ 2 - 1
tests/automated/legacy/external-dnd-advanced.js

@@ -123,7 +123,8 @@ describe('advanced external dnd', function() {
         })
         describe('through the `start` data attribute', function() {
           beforeEach(function() {
-            $('.drag').data('event', true)
+            $('.drag')
+              .data('event', true)
               .data('start', '05:00')
           })
           defineTests()

+ 33 - 37
tests/automated/legacy/forceEventDuration.js

@@ -1,55 +1,51 @@
 describe('forceEventDuration', function() {
 
-  var options
-
-  beforeEach(function() {
-    affix('#cal')
-
-    options = {
-      defaultDate: '2014-05-01',
-      defaultView: 'month'
-    }
+  pushOptions({
+    defaultDate: '2014-05-01',
+    defaultView: 'month'
   })
 
   describe('when turned off', function() {
-    beforeEach(function() {
-      options.forceEventDuration = false
+    pushOptions({
+      forceEventDuration: false
     })
     it('allows a null end date for all-day and timed events', function() {
-      options.events = [
-        {
-          id: '1',
-          start: '2014-05-10'
-        },
-        {
-          id: '2',
-          start: '2014-05-10T14:00:00'
-        }
-      ]
-      $('#cal').fullCalendar(options)
-      var events = $('#cal').fullCalendar('clientEvents')
+      initCalendar({
+        events: [
+          {
+            id: '1',
+            start: '2014-05-10'
+          },
+          {
+            id: '2',
+            start: '2014-05-10T14:00:00'
+          }
+        ]
+      })
+      var events = currentCalendar.clientEvents()
       expect(events[0].end).toBeNull()
       expect(events[1].end).toBeNull()
     })
   })
 
   describe('when turned on', function() {
-    beforeEach(function() {
-      options.forceEventDuration = true
+    pushOptions({
+      forceEventDuration: true
     })
     it('allows a null end date for all-day and timed events', function() {
-      options.events = [
-        {
-          id: '1',
-          start: '2014-05-10'
-        },
-        {
-          id: '2',
-          start: '2014-05-10T14:00:00'
-        }
-      ]
-      $('#cal').fullCalendar(options)
-      var events = $('#cal').fullCalendar('clientEvents')
+      initCalendar({
+        events: [
+          {
+            id: '1',
+            start: '2014-05-10'
+          },
+          {
+            id: '2',
+            start: '2014-05-10T14:00:00'
+          }
+        ]
+      })
+      var events = currentCalendar.clientEvents()
       expect(events[0].id).toEqual('1')
       expect(moment.isMoment(events[0].end)).toEqual(true)
       expect(events[1].id).toEqual('2')

+ 46 - 51
tests/automated/legacy/header-rendering.js

@@ -1,23 +1,18 @@
-
 describe('header rendering', function() {
 
-  beforeEach(function() {
-    affix('#calendar')
-  })
-
   describe('when using default header options', function() {
     it('should have title as default on left', function() {
-      $('#calendar').fullCalendar()
-      expect($('#calendar > .fc-toolbar > .fc-left > *')).toBeMatchedBy('h2')
+      initCalendar()
+      expect($('.fc-toolbar > .fc-left > *', currentCalendar.el)).toBeMatchedBy('h2')
     })
     it('should have empty center', function() {
-      $('#calendar').fullCalendar()
-      var center = $('#calendar > .fc-toolbar > .fc-center')
+      initCalendar()
+      var center = $('.fc-toolbar > .fc-center', currentCalendar.el)
       expect(center).toBeEmpty()
     })
     it('should have right with today|space|left|right', function() {
-      $('#calendar').fullCalendar()
-      var rightChildren = $('#calendar > .fc-toolbar > .fc-right > *')
+      initCalendar()
+      var rightChildren = $('.fc-toolbar > .fc-right > *', currentCalendar.el)
       var todayButton = rightChildren.eq(0)
       var buttonGroup = rightChildren.eq(1)
       var prevNextButtons = buttonGroup.children()
@@ -29,27 +24,27 @@ describe('header rendering', function() {
   })
 
   describe('when supplying header options', function() {
-    beforeEach(function() {
-      var options = {
-        header: {
-          left: 'next,prev',
-          center: 'prevYear today nextYear agendaView,dayView',
-          right: 'title'
-        }
+    pushOptions({
+      header: {
+        left: 'next,prev',
+        center: 'prevYear today nextYear agendaView,dayView',
+        right: 'title'
       }
-      $('#calendar').fullCalendar(options)
     })
     it('should have title on the right', function() {
-      expect($('#calendar > .fc-toolbar > .fc-right > *')).toBeMatchedBy('h2')
+      initCalendar()
+      expect($('.fc-toolbar > .fc-right > *', currentCalendar.el)).toBeMatchedBy('h2')
     })
     it('should have next|prev on left', function() {
-      var buttonGroup = $('#calendar > .fc-toolbar > .fc-left > *')
+      initCalendar()
+      var buttonGroup = $('.fc-toolbar > .fc-left > *', currentCalendar.el)
       var prevNextButtons = buttonGroup.children()
       expect(prevNextButtons.eq(0)).toHaveClass('fc-next-button')
       expect(prevNextButtons.eq(1)).toHaveClass('fc-prev-button')
     })
     it('should have prevYear|space|today|space|nextYear in center', function() {
-      var items = $('#calendar > .fc-toolbar > .fc-center > *')
+      initCalendar()
+      var items = $('.fc-toolbar > .fc-center > *', currentCalendar.el)
       expect(items.eq(0)).toHaveClass('fc-prevYear-button')
       expect(items.eq(1)).toHaveClass('fc-today-button')
       expect(items.eq(2)).toHaveClass('fc-nextYear-button')
@@ -57,47 +52,46 @@ describe('header rendering', function() {
   })
 
   describe('when setting header to false', function() {
-    beforeEach(function() {
-      var options = {
-        header: false
-      }
-      $('#calendar').fullCalendar(options)
+    pushOptions({
+      header: false
     })
     it('should not have header table', function() {
+      initCalendar()
       expect($('.fc-toolbar')).not.toBeInDOM()
     })
   })
 
   it('allow for dynamically changing', function() {
-    $('#calendar').fullCalendar()
+    initCalendar()
     expect($('.fc-toolbar')).toBeInDOM()
-    $('#calendar').fullCalendar('option', 'header', false)
+    currentCalendar.option('header', false)
     expect($('.fc-toolbar')).not.toBeInDOM()
   })
 
   describe('renders left and right literally', function() {
     [ true, false ].forEach(function(isRTL) {
       describe('when isRTL is ' + isRTL, function() {
-        beforeEach(function() {
-          $('#calendar').fullCalendar({
-            header: {
-              left: 'prev',
-              center: 'today',
-              right: 'next'
-            },
-            isRTL: isRTL
-          })
+        pushOptions({
+          header: {
+            left: 'prev',
+            center: 'today',
+            right: 'next'
+          },
+          isRTL: isRTL
         })
         it('should have prev in left', function() {
-          var fcHeaderLeft = $('#calendar .fc-toolbar > .fc-left')
+          initCalendar()
+          var fcHeaderLeft = $('.fc-toolbar > .fc-left', currentCalendar.el)
           expect(fcHeaderLeft).toContainElement('.fc-prev-button')
         })
         it('should have today in center', function() {
-          var fcHeaderCenter = $('#calendar .fc-toolbar > .fc-center')
+          initCalendar()
+          var fcHeaderCenter = $('.fc-toolbar > .fc-center', currentCalendar.el)
           expect(fcHeaderCenter).toContainElement('.fc-today-button')
         })
         it('should have next in right', function() {
-          var fcHeaderRight = $('#calendar .fc-toolbar > .fc-right')
+          initCalendar()
+          var fcHeaderRight = $('.fc-toolbar > .fc-right', currentCalendar.el)
           expect(fcHeaderRight).toContainElement('.fc-next-button')
         })
       })
@@ -105,17 +99,12 @@ describe('header rendering', function() {
   })
 
   describe('when calendar is within a form', function() {
-    beforeEach(function() {
-      $('#calendar').wrap('<form action="http://google.com/"></form>')
-    })
+
     it('should not submit the form when clicking the button', function(done) {
-      var options = {
-        header: {
-          left: 'prev,next',
-          right: 'title'
-        }
-      }
       var unloadCalled = false
+      var el = $('<div id="calendar"/>')
+        .wrap('<form action="https://google.com/"></form>')
+        .appendTo('body')
 
       function beforeUnloadHandler() {
         console.log('when calendar is within a form, it submits!!!')
@@ -126,10 +115,16 @@ describe('header rendering', function() {
       $(window).on('beforeunload', beforeUnloadHandler)
 
       function cleanup() {
+        el.remove()
         $(window).off('beforeunload', beforeUnloadHandler)
       }
 
-      $('#calendar').fullCalendar(options)
+      initCalendar({
+        header: {
+          left: 'prev,next',
+          right: 'title'
+        }
+      }, el)
       $('.fc-next-button').simulate('click')
 
       setTimeout(function() { // wait to see if handler was called

+ 18 - 24
tests/automated/legacy/locale.js

@@ -1,4 +1,3 @@
-
 describe('locale', function() {
 
   afterEach(function() {
@@ -7,30 +6,25 @@ describe('locale', function() {
 
   it('is not affected by global moment locale when unset', function() {
     moment.locale('fr')
-    affix('#cal')
-    $('#cal').fullCalendar()
-    var calendar = $('#cal').fullCalendar('getCalendar')
-    var mom = calendar.moment('2014-05-01')
+    initCalendar()
+    var mom = currentCalendar.moment('2014-05-01')
     var s = mom.format('dddd MMMM Do YYYY')
     expect(s).toEqual('Thursday May 1st 2014')
   })
 
   it('is not affected by global moment locale when unset', function() {
     moment.locale('fr')
-    affix('#cal')
-    $('#cal').fullCalendar({
+    initCalendar({
       locale: 'es'
     })
-    var calendar = $('#cal').fullCalendar('getCalendar')
-    var mom = calendar.moment('2014-05-01')
+    var mom = currentCalendar.moment('2014-05-01')
     var s = mom.format('dddd MMMM Do YYYY')
     expect(s).toEqual('jueves mayo 1º 2014')
   })
 
   it('doesn\'t side-effect the global moment locale when customized', function() {
     moment.locale('fr')
-    affix('#cal')
-    $('#cal').fullCalendar({
+    initCalendar({
       locale: 'es'
     })
     var mom = moment.utc('2014-05-01')
@@ -44,11 +38,10 @@ describe('locale', function() {
   // needs to be fixed to the developer.
   /*
   xit('defaults to English when configured to locale that isn\'t loaded', function() {
-    affix('#cal');
-    $('#cal').fullCalendar({
+    pushOptions({
       locale: 'zz'
     });
-    var calendar = $('#cal').fullCalendar('getCalendar');
+    var calendar = initCalendar();
     var mom = calendar.moment('2014-05-01');
     var s = mom.format('dddd MMMM Do YYYY');
     expect(s).toEqual('Thursday May 1st 2014');
@@ -56,8 +49,7 @@ describe('locale', function() {
   */
 
   it('works when certain locale has no FC settings defined', function() {
-    affix('#cal')
-    $('#cal').fullCalendar({
+    initCalendar({
       locale: 'en-ca',
       defaultView: 'agendaWeek',
       defaultDate: '2014-12-25',
@@ -70,20 +62,22 @@ describe('locale', function() {
   })
 
   it('allows dynamic setting', function() {
-    affix('#cal')
-    $('#cal').fullCalendar({
+    initCalendar({
       locale: 'es',
       defaultDate: '2016-07-10',
       defaultView: 'month'
     })
-    expect($('.fc h2')).toHaveText('julio 2016')
-    expect($('.fc')).not.toHaveClass('fc-rtl')
 
-    $('#cal').fullCalendar('option', 'locale', 'fr')
-    expect($('.fc h2')).toHaveText('juillet 2016')
+    var calendarEl = currentCalendar.el
+
+    expect($('h2', calendarEl)).toHaveText('julio 2016')
+    expect($(calendarEl)).not.toHaveClass('fc-rtl')
+
+    currentCalendar.option('locale', 'fr')
+    expect($('h2', calendarEl)).toHaveText('juillet 2016')
 
-    $('#cal').fullCalendar('option', 'locale', 'ar') // NOTE: we had problems testing for RTL title text
-    expect($('.fc')).toHaveClass('fc-rtl')
+    currentCalendar.option('locale', 'ar') // NOTE: we had problems testing for RTL title text
+    expect($(calendarEl)).toHaveClass('fc-rtl')
   })
 
 })

+ 12 - 34
tests/automated/legacy/maxTime.js

@@ -1,9 +1,5 @@
 describe('maxTime', function() {
 
-  beforeEach(function() {
-    affix('#cal')
-  })
-
   var numToStringConverter = function(timeIn) {
     var time = (timeIn % 12) || 12
     var amPm = 'am'
@@ -17,10 +13,9 @@ describe('maxTime', function() {
 
     describe('in agendaWeek', function() {
       it('should start at 12am', function() {
-        var options = {
+        initCalendar({
           defaultView: 'agendaWeek'
-        }
-        $('#cal').fullCalendar(options)
+        })
         var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text()
         expect(lastSlotText).toEqual('11pm')
       })
@@ -28,10 +23,9 @@ describe('maxTime', function() {
 
     describe('in agendaDay', function() {
       it('should start at 12am', function() {
-        var options = {
+        initCalendar({
           defaultView: 'agendaDay'
-        }
-        $('#cal').fullCalendar(options)
+        })
         var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text()
         expect(lastSlotText).toEqual('11pm')
       })
@@ -43,16 +37,12 @@ describe('maxTime', function() {
     var hourNumbers = [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 ]
 
     describe('in agendaWeek', function() {
-      beforeEach(function() {
-        affix('#cal2')
-      })
       hourNumbers.forEach(function(hourNumber) {
         it('should end at ' + hourNumber, function() {
-          var options = {
+          initCalendar({
             defaultView: 'agendaWeek',
             maxTime: { hours: hourNumber }
-          }
-          $('#cal2').fullCalendar(options)
+          })
           var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text()
           var expected = numToStringConverter(hourNumber - 1)
           expect(lastSlotText).toEqual(expected)
@@ -61,16 +51,12 @@ describe('maxTime', function() {
     })
 
     describe('in agendaDay', function() {
-      beforeEach(function() {
-        affix('#cal2')
-      })
       hourNumbers.forEach(function(hourNumber) {
         it('should end at ' + hourNumber, function() {
-          var options = {
+          initCalendar({
             defaultView: 'agendaDay',
             maxTime: hourNumber + ':00' // in addition, test string duration input
-          }
-          $('#cal2').fullCalendar(options)
+          })
           var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text()
           var expected = numToStringConverter(hourNumber - 1)
           expect(lastSlotText).toEqual(expected)
@@ -84,16 +70,12 @@ describe('maxTime', function() {
     var hourNumbers = [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 ]
 
     describe('in agendaWeek', function() {
-      beforeEach(function() {
-        affix('#cal2')
-      })
       hourNumbers.forEach(function(hourNumber) {
         it('should end at ' + hourNumber + ':20', function() {
-          var options = {
+          initCalendar({
             defaultView: 'agendaWeek',
             maxTime: { hours: hourNumber, minutes: 20 }
-          }
-          $('#cal2').fullCalendar(options)
+          })
           var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text()
           // since exclusive end is :20, last slot will be on the current hour's 00:00
           var expected = numToStringConverter(hourNumber)
@@ -103,16 +85,12 @@ describe('maxTime', function() {
     })
 
     describe('in agendaDay', function() {
-      beforeEach(function() {
-        affix('#cal2')
-      })
       hourNumbers.forEach(function(hourNumber) {
         it('should end at ' + hourNumber + ':20', function() {
-          var options = {
+          initCalendar({
             defaultView: 'agendaDay',
             maxTime: { hours: hourNumber, minutes: 20 }
-          }
-          $('#cal2').fullCalendar(options)
+          })
           var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text()
           // since exclusive end is :20, last slot will be on the current hour's 00:00
           var expected = numToStringConverter(hourNumber)

+ 36 - 36
tests/automated/legacy/monthNames.js

@@ -1,13 +1,9 @@
 describe('month name', function() {
-  var settings = {}
   var referenceDate = '2014-01-01' // The day the world is hung-over
   var locales = [ 'es', 'fr', 'de', 'zh-cn', 'nl' ]
 
-  beforeEach(function() {
-    affix('#cal')
-    settings = {
-      defaultDate: referenceDate
-    }
+  pushOptions({
+    defaultDate: referenceDate
   })
 
   afterEach(function() {
@@ -16,25 +12,27 @@ describe('month name', function() {
 
   [ 'month', 'agendaDay', 'basicDay' ].forEach(function(viewClass, index, viewClasses) {
     describe('when view is ' + viewClass, function() {
-      beforeEach(function() {
-        settings.defaultView = viewClass
+      pushOptions({
+        defaultView: viewClass
       })
 
       describe('when locale is default', function() {
         beforeEach(function() {
-          settings.locale = 'en'
           moment.locale('en')
         })
+        pushOptions({
+          locale: 'en'
+        })
 
         moment.months().forEach(function(month, index, months) {
           it('should be ' + month, function(done) {
-            settings.defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
-            settings.eventAfterAllRender = function() {
-              expect($('.fc-toolbar h2')).toContainText(month)
-              done()
-            }
-
-            $('#cal').fullCalendar(settings)
+            initCalendar({
+              defaultDate: FullCalendar.moment(referenceDate).add(index, 'months'),
+              eventAfterAllRender: function() {
+                expect($('.fc-toolbar h2')).toContainText(month)
+                done()
+              }
+            })
           })
         })
       })
@@ -42,26 +40,28 @@ describe('month name', function() {
       locales.forEach(function(locale, index, locales) {
         describe('when locale is ' + locale, function() {
           beforeEach(function() {
-            settings.locale = locale
             moment.locale(locale)
           })
+          pushOptions({
+            locale: locale
+          })
 
           moment.months().forEach(function(month, index, months) { // `month` will always be English
             it('should be the translated name for ' + month, function(done) {
               var localeMonths = moment.months()
               var localeMonth = localeMonths[index]
-
-              settings.defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
-              settings.eventAfterAllRender = function() {
-                if (viewClass === 'month') { // with month view check for occurence of the monthname in the title
-                  expect($('.fc-toolbar h2')).toContainText(localeMonth)
-                } else { // with day views ensure that title contains the properly formatted phrase
-                  expect($('.fc-toolbar h2')).toHaveText(settings.defaultDate.format('LL'))
+              var defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
+              initCalendar({
+                defaultDate: defaultDate,
+                eventAfterAllRender: function() {
+                  if (viewClass === 'month') { // with month view check for occurence of the monthname in the title
+                    expect($('.fc-toolbar h2')).toContainText(localeMonth)
+                  } else { // with day views ensure that title contains the properly formatted phrase
+                    expect($('.fc-toolbar h2')).toHaveText(defaultDate.format('LL'))
+                  }
+                  done()
                 }
-                done()
-              }
-
-              $('#cal').fullCalendar(settings)
+              })
             })
           })
         })
@@ -85,14 +85,14 @@ describe('month name', function() {
 
         months.forEach(function(month, index, months) { // `month` is our custom month name
           it('should be the translated name for ' + month, function(done) {
-            settings.defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
-            settings.monthNames = months
-            settings.eventAfterAllRender = function() {
-              expect($('.fc-toolbar h2')).toContainText(month)
-              done()
-            }
-
-            $('#cal').fullCalendar(settings)
+            initCalendar({
+              defaultDate: FullCalendar.moment(referenceDate).add(index, 'months'),
+              monthNames: months,
+              eventAfterAllRender: function() {
+                expect($('.fc-toolbar h2')).toContainText(month)
+                done()
+              }
+            })
           })
         })
       })

+ 32 - 33
tests/automated/legacy/monthNamesShort.js

@@ -1,13 +1,9 @@
 describe('short month name', function() {
-  var settings = {}
   var referenceDate = '2014-01-01' // The day the world is hung-over
   var locales = [ 'es', 'fr', 'de', 'zh-cn', 'nl' ]
 
-  beforeEach(function() {
-    affix('#cal')
-    settings = {
-      defaultDate: referenceDate
-    }
+  pushOptions({
+    defaultDate: referenceDate
   })
 
   afterEach(function() {
@@ -16,25 +12,27 @@ describe('short month name', function() {
 
   [ 'agendaWeek', 'basicWeek' ].forEach(function(viewClass, index, viewClasses) {
     describe('when view is ' + viewClass, function() {
-      beforeEach(function() {
-        settings.defaultView = viewClass
+      pushOptions({
+        defaultView: viewClass
       })
 
       describe('when locale is default', function() {
         beforeEach(function() {
-          settings.locale = 'en'
           moment.locale('en')
         })
+        pushOptions({
+          locale: 'en'
+        })
 
         moment.monthsShort().forEach(function(monthShort, index) {
           it('should be ' + monthShort, function(done) {
-            settings.defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
-            settings.eventAfterAllRender = function() {
-              expect($('.fc-toolbar h2')).toContainText(monthShort)
-              done()
-            }
-
-            $('#cal').fullCalendar(settings)
+            initCalendar({
+              defaultDate: FullCalendar.moment(referenceDate).add(index, 'months'),
+              eventAfterAllRender: function() {
+                expect($('.fc-toolbar h2')).toContainText(monthShort)
+                done()
+              }
+            })
           })
         })
       })
@@ -42,22 +40,23 @@ describe('short month name', function() {
       locales.forEach(function(locale, index, locales) {
         describe('when locale is ' + locale, function() {
           beforeEach(function() {
-            settings.locale = locale
             moment.locale(locale)
           })
+          pushOptions({
+            locale: locale
+          })
 
           moment.monthsShort().forEach(function(monthShort, index) { // `monthShort` will always be English
             it('should be the translated name for ' + monthShort, function(done) {
               var localeMonthsShort = moment.monthsShort()
               var localeMonthShort = localeMonthsShort[index]
-
-              settings.defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
-              settings.eventAfterAllRender = function() {
-                expect($('.fc-toolbar h2')).toContainText(localeMonthShort)
-                done()
-              }
-
-              $('#cal').fullCalendar(settings)
+              initCalendar({
+                defaultDate: FullCalendar.moment(referenceDate).add(index, 'months'),
+                eventAfterAllRender: function() {
+                  expect($('.fc-toolbar h2')).toContainText(localeMonthShort)
+                  done()
+                }
+              })
             })
           })
         })
@@ -81,14 +80,14 @@ describe('short month name', function() {
 
         monthsShort.forEach(function(monthShort, index) { // `monthShort` will be our custom month name
           it('should be the translated name for ' + monthShort, function(done) {
-            settings.defaultDate = FullCalendar.moment(referenceDate).add(index, 'months')
-            settings.monthNamesShort = monthsShort
-            settings.eventAfterAllRender = function() {
-              expect($('.fc-toolbar h2')).toContainText(monthShort)
-              done()
-            }
-
-            $('#cal').fullCalendar(settings)
+            initCalendar({
+              defaultDate: FullCalendar.moment(referenceDate).add(index, 'months'),
+              monthNamesShort: monthsShort,
+              eventAfterAllRender: function() {
+                expect($('.fc-toolbar h2')).toContainText(monthShort)
+                done()
+              }
+            })
           })
         })
       })

+ 13 - 14
tests/automated/legacy/navLinks.js

@@ -2,7 +2,6 @@ describe('navLinks', function() {
   var options
 
   beforeEach(function() {
-    affix('#cal')
     options = {
       now: '2016-08-20',
       navLinks: true,
@@ -22,7 +21,7 @@ describe('navLinks', function() {
     })
 
     it('moves to day', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
       expectDayView('agendaDay', '2016-08-09')
       expect(options.dayClick).not.toHaveBeenCalled()
@@ -30,18 +29,18 @@ describe('navLinks', function() {
 
     // https://github.com/fullcalendar/fullcalendar/issues/3869
     it('moves to two different days', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
       expectDayView('agendaDay', '2016-08-09')
       expect(options.dayClick).not.toHaveBeenCalled()
-      $('#cal').fullCalendar('changeView', 'month')
+      currentCalendar.changeView('month')
       $.simulateMouseClick(getDayGridNumberEl('2016-08-10'))
       expectDayView('agendaDay', '2016-08-10')
     })
 
     it('moves to agendaDay specifically', function() {
       options.navLinkDayClick = 'agendaDay'
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
       expectDayView('agendaDay', '2016-08-09')
       expect(options.dayClick).not.toHaveBeenCalled()
@@ -49,7 +48,7 @@ describe('navLinks', function() {
 
     it('moves to basicDay specifically', function() {
       options.navLinkDayClick = 'basicDay'
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
       expectDayView('basicDay', '2016-08-09')
       expect(options.dayClick).not.toHaveBeenCalled()
@@ -61,7 +60,7 @@ describe('navLinks', function() {
         expect(typeof ev).toBe('object')
       }
       spyOn(options, 'navLinkDayClick').and.callThrough()
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
       expect(options.navLinkDayClick).toHaveBeenCalled()
       expect(options.dayClick).not.toHaveBeenCalled()
@@ -73,7 +72,7 @@ describe('navLinks', function() {
       })
 
       it('moves to week', function() {
-        $('#cal').fullCalendar(options)
+        initCalendar(options)
         $.simulateMouseClick(getDayGridClassicWeekLinks().eq(1))
         expectWeekView('agendaWeek', '2016-08-07')
         expect(options.dayClick).not.toHaveBeenCalled()
@@ -81,7 +80,7 @@ describe('navLinks', function() {
 
       it('moves to week with within-day rendering', function() {
         options.weekNumbersWithinDays = true
-        $('#cal').fullCalendar(options)
+        initCalendar(options)
         $.simulateMouseClick(getDayGridEmbeddedWeekLinks().eq(1))
         expectWeekView('agendaWeek', '2016-08-07')
         expect(options.dayClick).not.toHaveBeenCalled()
@@ -89,7 +88,7 @@ describe('navLinks', function() {
     })
 
     it('does not have clickable day header', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       expect(getDayHeaderLinks().length).toBe(0)
     })
   })
@@ -100,7 +99,7 @@ describe('navLinks', function() {
     })
 
     it('moves to day view', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getDayHeaderLink('2016-08-15'))
       expectDayView('agendaDay', '2016-08-15')
       expect(options.dayClick).not.toHaveBeenCalled()
@@ -119,7 +118,7 @@ describe('navLinks', function() {
     })
 
     it('moves to day view', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getListDayHeaderLink('2016-08-20'))
       expectDayView('agendaDay', '2016-08-20')
       expect(options.dayClick).not.toHaveBeenCalled()
@@ -133,14 +132,14 @@ describe('navLinks', function() {
 
     it('moves to week view', function() {
       options.weekNumbers = true
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       $.simulateMouseClick(getAgendaWeekNumberLink())
       expectWeekView('agendaWeek', '2016-08-14')
       expect(options.dayClick).not.toHaveBeenCalled()
     })
 
     it('does not have a clickable day header', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       expect(getDayHeaderLinks().length).toBe(0)
     })
   })

+ 3 - 8
tests/automated/legacy/nextDayThreshold.js

@@ -1,16 +1,11 @@
-
 describe('nextDayThreshold', function() {
 
   // when a view object exposes its nextDayThreshold value (after some refactoring)...
   //   TODO: detect the default of 9am
   //   TODO: detect 2 or more different types of Duration-ish parsing
 
-  beforeEach(function() {
-    affix('#cal')
-  })
-
   it('renders an event before the threshold', function() {
-    $('#cal').fullCalendar({
+    initCalendar({
       nextDayThreshold: '10:00:00',
       defaultDate: '2014-06',
       defaultView: 'month',
@@ -26,7 +21,7 @@ describe('nextDayThreshold', function() {
   })
 
   it('renders an event equal to the threshold', function() {
-    $('#cal').fullCalendar({
+    initCalendar({
       nextDayThreshold: '10:00:00',
       defaultDate: '2014-06',
       defaultView: 'month',
@@ -42,7 +37,7 @@ describe('nextDayThreshold', function() {
   })
 
   it('renders an event after the threshold', function() {
-    $('#cal').fullCalendar({
+    initCalendar({
       nextDayThreshold: '10:00:00',
       defaultDate: '2014-06',
       defaultView: 'month',

+ 7 - 8
tests/automated/legacy/nowIndicator.js

@@ -6,7 +6,6 @@ describe('now indicator', function() {
   var options
 
   beforeEach(function() {
-    affix('#cal')
     options = {
       now: '2015-12-26T06:00:00',
       scrollTime: '00:00'
@@ -19,7 +18,7 @@ describe('now indicator', function() {
     })
 
     it('doesn\'t render even when activated', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       expect(isNowIndicatorRendered()).toBe(false)
     })
   })
@@ -30,7 +29,7 @@ describe('now indicator', function() {
     })
 
     it('doesn\'t render by default', function() {
-      $('#cal').fullCalendar(options)
+      initCalendar(options)
       expect(isNowIndicatorRendered()).toBe(false)
     })
 
@@ -48,18 +47,18 @@ describe('now indicator', function() {
 
           it('doesn\'t render when out of view', function() {
             options.defaultDate = '2015-12-27' // sun of next week
-            $('#cal').fullCalendar(options)
+            initCalendar(options)
             expect(isNowIndicatorRendered()).toBe(false)
           })
 
           it('renders on correct time', function() {
-            $('#cal').fullCalendar(options)
+            initCalendar(options)
             isNowIndicatorRenderedAt('2015-12-26T06:00:00')
           })
 
           it('renders on correct time2', function() {
             options.now = '2015-12-20T02:30:00'
-            $('#cal').fullCalendar(options)
+            initCalendar(options)
             isNowIndicatorRenderedAt('2015-12-20T02:30:00')
           })
         })
@@ -75,8 +74,8 @@ describe('now indicator', function() {
 
       options.defaultDate = '2016-01-01' // does NOT have "now" in view
       options.nowIndicator = true
-      $('#cal').fullCalendar(options)
-      $('#cal').fullCalendar('today') // the bug only happens after navigate
+      initCalendar(options)
+      currentCalendar.today() // the bug only happens after navigate
 
       setTimeout(function() {
         expect($('.fc-now-indicator-arrow').length).toBe(1)

+ 43 - 51
tests/automated/legacy/refetchEvents.js

@@ -2,14 +2,11 @@ describe('refetchEvents', function() {
 
   // there IS a similar test in automated-better, but does month view
   describe('when agenda events are rerendered', function() {
-    beforeEach(function() {
-      affix('#cal')
-    })
 
     it('keeps scroll after refetchEvents', function(done) {
       var renderCalls = 0
 
-      $('#cal').fullCalendar({
+      initCalendar({
         now: '2015-08-07',
         scrollTime: '00:00',
         height: 400, // makes this test more consistent across viewports
@@ -32,7 +29,7 @@ describe('refetchEvents', function() {
             setTimeout(function() {
               scrollEl.scrollTop(100)
               setTimeout(function() {
-                $('#cal').fullCalendar('refetchEvents')
+                currentCalendar.refetchEvents()
               }, 100)
             }, 100)
           } else if (renderCalls === 2) {
@@ -45,42 +42,40 @@ describe('refetchEvents', function() {
   })
 
   describe('when there are multiple event sources', function() {
-    var options
     var fetchCount // affects events created in createEventGenerator
-    var calendarEl
+    var eventSources
+
+    pushOptions({
+      now: '2015-08-07',
+      defaultView: 'agendaWeek'
+    })
 
     beforeEach(function() {
-      affix('#cal')
       fetchCount = 0
-      calendarEl = $('#cal')
-      options = {
-        now: '2015-08-07',
-        defaultView: 'agendaWeek',
-        eventSources: [
-          {
-            events: createEventGenerator(),
-            color: 'green',
-            id: 'source1'
-          },
-          {
-            events: createEventGenerator(),
-            color: 'blue',
-            id: 'source2'
-          },
-          {
-            events: createEventGenerator(),
-            color: 'red',
-            id: 'source3'
-          }
-        ]
-      }
+      eventSources = [
+        {
+          events: createEventGenerator(),
+          color: 'green',
+          id: 'source1'
+        },
+        {
+          events: createEventGenerator(),
+          color: 'blue',
+          id: 'source2'
+        },
+        {
+          events: createEventGenerator(),
+          color: 'red',
+          id: 'source3'
+        }
+      ]
     })
 
     describe('and all events are fetched synchronously', function() {
       it('all events are immediately updated', function(done) {
-        calendarEl.fullCalendar(options)
+        initCalendar({ eventSources })
         fetchCount++
-        calendarEl.fullCalendar('refetchEvents')
+        currentCalendar.refetchEvents()
         expect($('.fetch0').length).toEqual(0)
         expect($('.fetch1').length).toEqual(3)
         done()
@@ -90,7 +85,7 @@ describe('refetchEvents', function() {
     describe('and one event source is asynchronous', function() {
       it('original events remain on the calendar until all events have been refetched', function(done) {
         // set a 100ms timeout on this event source
-        options.eventSources[0].events = function(start, end, timezone, callback) {
+        eventSources[0].events = function(start, end, timezone, callback) {
           var events = [
             { id: '1',
               start: '2015-08-07T02:00:00',
@@ -99,28 +94,26 @@ describe('refetchEvents', function() {
               className: 'fetch' + fetchCount
             }
           ]
-
           setTimeout(function() {
             callback(events)
           }, 100)
         }
-        options.eventAfterAllRender = function() {
-          fetchCount++
-          if (fetchCount === 1) {
-            // after the initial rendering of events, call refetchEvents
-            calendarEl.fullCalendar('refetchEvents')
-
-            expect($('.fetch0').length).toEqual(3) // original events still on the calendar
-            expect($('.fetch1').length).toEqual(0) // new events not yet refetched
-
-          } else if (fetchCount === 2) { // after refetch+rerender is over
-            expect($('.fetch0').length).toEqual(0)
-            expect($('.fetch1').length).toEqual(3)
-            done()
+        initCalendar({
+          eventSources: eventSources,
+          eventAfterAllRender: function() {
+            fetchCount++
+            if (fetchCount === 1) {
+              // after the initial rendering of events, call refetchEvents
+              currentCalendar.refetchEvents()
+              expect($('.fetch0').length).toEqual(3) // original events still on the calendar
+              expect($('.fetch1').length).toEqual(0) // new events not yet refetched
+            } else if (fetchCount === 2) { // after refetch+rerender is over
+              expect($('.fetch0').length).toEqual(0)
+              expect($('.fetch1').length).toEqual(3)
+              done()
+            }
           }
-        }
-
-        calendarEl.fullCalendar(options)
+        })
       })
     })
 
@@ -136,7 +129,6 @@ describe('refetchEvents', function() {
             className: 'fetch' + fetchCount
           }
         ]
-
         callback(events)
       }
     }

+ 37 - 40
tests/automated/legacy/removeEvents.js

@@ -1,12 +1,8 @@
 describe('removeEvents', function() {
-  var options
 
-  beforeEach(function() {
-    affix('#cal')
-    options = {
-      defaultDate: '2014-06-24',
-      defaultView: 'month'
-    }
+  pushOptions({
+    defaultDate: '2014-06-24',
+    defaultView: 'month'
   })
 
   function buildEventsWithoutIds() {
@@ -38,10 +34,10 @@ describe('removeEvents', function() {
         go(
           eventGenerator(),
           function() {
-            $('#cal').fullCalendar('removeEvents')
+            currentCalendar.removeEvents()
           },
           function() {
-            expect($('#cal').fullCalendar('clientEvents').length).toEqual(0)
+            expect(currentCalendar.clientEvents().length).toEqual(0)
             expect($('.fc-event').length).toEqual(0)
           },
           done
@@ -52,12 +48,12 @@ describe('removeEvents', function() {
         go(
           eventGenerator(),
           function() {
-            $('#cal').fullCalendar('removeEvents', function(event) {
+            currentCalendar.removeEvents(function(event) {
               return $.inArray('event-one', event.className) !== -1
             })
           },
           function() {
-            expect($('#cal').fullCalendar('clientEvents').length).toEqual(2)
+            expect(currentCalendar.clientEvents().length).toEqual(2)
             expect($('.fc-event').length).toEqual(2)
             expect($('.event-zero').length).toEqual(1)
             expect($('.event-two').length).toEqual(1)
@@ -73,10 +69,10 @@ describe('removeEvents', function() {
     go(
       buildEventsWithIds(),
       function() {
-        $('#cal').fullCalendar('removeEvents', 1)
+        currentCalendar.removeEvents(1)
       },
       function() {
-        expect($('#cal').fullCalendar('clientEvents').length).toEqual(2)
+        expect(currentCalendar.clientEvents().length).toEqual(2)
         expect($('.fc-event').length).toEqual(2)
         expect($('.event-zero').length).toEqual(1)
         expect($('.event-two').length).toEqual(1)
@@ -89,10 +85,10 @@ describe('removeEvents', function() {
     go(
       buildEventsWithIds(),
       function() {
-        $('#cal').fullCalendar('removeEvents', '1')
+        currentCalendar.removeEvents('1')
       },
       function() {
-        expect($('#cal').fullCalendar('clientEvents').length).toEqual(2)
+        expect(currentCalendar.clientEvents().length).toEqual(2)
         expect($('.fc-event').length).toEqual(2)
         expect($('.event-zero').length).toEqual(1)
         expect($('.event-two').length).toEqual(1)
@@ -105,10 +101,10 @@ describe('removeEvents', function() {
     go(
       buildEventsWithIds(),
       function() {
-        $('#cal').fullCalendar('removeEvents', 0)
+        currentCalendar.removeEvents(0)
       },
       function() {
-        expect($('#cal').fullCalendar('clientEvents').length).toEqual(2)
+        expect(currentCalendar.clientEvents().length).toEqual(2)
         expect($('.fc-event').length).toEqual(2)
         expect($('.event-zero').length).toEqual(0)
         expect($('.event-non-zero').length).toEqual(2)
@@ -137,42 +133,43 @@ describe('removeEvents', function() {
   // Verifies the actions in removeFunc executed correctly by calling checkFunc.
   function go(events, removeFunc, checkFunc, doneFunc) {
     var called = false
-    options.events = events
-    options.eventAfterAllRender = function() {
-      if (!called) { // don't execute on subsequent removeEvents/next/prev
-        called = true
+    initCalendar({
+      events: events,
+      eventAfterAllRender: function() {
+        if (!called) { // don't execute on subsequent removeEvents/next/prev
+          called = true
 
-        checkAllEvents() // make sure all events initially rendered correctly
+          checkAllEvents() // make sure all events initially rendered correctly
 
-        removeFunc() // remove the events
-        setTimeout(function() { // because the event rerender will be queued because we're a level deep
+          removeFunc() // remove the events
+          setTimeout(function() { // because the event rerender will be queued because we're a level deep
 
-          checkFunc() // check correctness
+            checkFunc() // check correctness
 
-          // move the calendar back out of view, then back in
-          $('#cal').fullCalendar('next')
-          $('#cal').fullCalendar('prev')
+            // move the calendar back out of view, then back in
+            currentCalendar.next()
+            currentCalendar.prev()
 
-          // array event sources should maintain the same state
-          // whereas "dynamic" event sources should refetch and reset the state
-          if ($.isArray(events)) {
-            checkFunc() // for issue 2187
-          } else {
-            checkAllEvents()
-          }
+            // array event sources should maintain the same state
+            // whereas "dynamic" event sources should refetch and reset the state
+            if ($.isArray(events)) {
+              checkFunc() // for issue 2187
+            } else {
+              checkAllEvents()
+            }
 
-          doneFunc()
-        }, 0)
+            doneFunc()
+          }, 0)
+        }
       }
-    }
-    $('#cal').fullCalendar(options)
+    })
   }
 
 
   // Checks to make sure all events have been rendered and that the calendar
   // has internal info on all the events.
   function checkAllEvents() {
-    expect($('#cal').fullCalendar('clientEvents').length).toEqual(3)
+    expect(currentCalendar.clientEvents().length).toEqual(3)
     expect($('.fc-event').length).toEqual(3)
   }
 

+ 47 - 44
tests/automated/legacy/scroll-state.js

@@ -1,27 +1,30 @@
-
 describe('scroll state', function() {
-  var options
+  var calendarEl
 
   beforeEach(function() {
-    options = {
-      defaultDate: '2015-02-20',
-      contentHeight: 200
-    }
-    affix('#cal')
-    $('#cal').width(800)
+    calendarEl = $('<div id="calendar">').width(800).appendTo('body')
+  })
+  afterEach(function() {
+    calendarEl.remove()
+    calendarEl = null
+  })
+
+  pushOptions({
+    defaultDate: '2015-02-20',
+    contentHeight: 200
   })
 
   describe('when in month view', function() {
-    beforeEach(function() {
-      options.defaultView = 'month'
+    pushOptions({
+      defaultView: 'month'
     })
     defineTests()
   })
 
   describe('when in agendaWeek view', function() {
-    beforeEach(function() {
-      options.defaultView = 'agendaWeek'
-      options.scrollTime = '00:00'
+    pushOptions({
+      defaultView: 'agendaWeek',
+      scrollTime: '00:00'
     })
     defineTests()
   })
@@ -32,15 +35,15 @@ describe('scroll state', function() {
       var scrollEl
       var scroll0
 
-      options.windowResize = function() {
-        setTimeout(function() { // wait until all other tasks are finished
-          expect(scrollEl.scrollTop()).toBe(scroll0)
-          done()
-        }, 0)
-      }
-
-      $('#cal').fullCalendar(options)
-      scrollEl = $('#cal .fc-scroller')
+      initCalendar({
+        windowResize: function() {
+          setTimeout(function() { // wait until all other tasks are finished
+            expect(scrollEl.scrollTop()).toBe(scroll0)
+            done()
+          }, 0)
+        }
+      }, calendarEl)
+      scrollEl = $('.fc-scroller', calendarEl)
 
       setTimeout(function() { // wait until after browser's scroll state is applied
         scrollEl.scrollTop(9999) // all the way
@@ -56,30 +59,30 @@ describe('scroll state', function() {
       var scrollEl
       var scroll0
 
-      options.events = [ {
-        start: '2015-02-20'
-      } ]
-      options.eventAfterAllRender = function() {
-        if (++calls === 1) {
-          eventEl0 = $('#cal .fc-event')
-          expect(eventEl0.length).toBe(1)
+      initCalendar({
+        events: [ {
+          start: '2015-02-20'
+        } ],
+        eventAfterAllRender: function() {
+          if (++calls === 1) {
+            eventEl0 = $('.fc-event', calendarEl)
+            expect(eventEl0.length).toBe(1)
 
-          setTimeout(function() { // wait until after browser's scroll state is applied
-            scrollEl.scrollTop(9999) // all the way
-            scroll0 = scrollEl.scrollTop()
-            $('#cal').fullCalendar('rerenderEvents')
-          }, 0)
-        } else {
-          eventEl1 = $('#cal .fc-event')
-          expect(eventEl1.length).toBe(1)
-          expect(eventEl1[0]).not.toBe(eventEl0[0]) // ensure it a rerender
-          expect(scrollEl.scrollTop()).toBe(scroll0)
-          done()
+            setTimeout(function() { // wait until after browser's scroll state is applied
+              scrollEl.scrollTop(9999) // all the way
+              scroll0 = scrollEl.scrollTop()
+              currentCalendar.rerenderEvents()
+            }, 0)
+          } else {
+            eventEl1 = $('.fc-event', calendarEl)
+            expect(eventEl1.length).toBe(1)
+            expect(eventEl1[0]).not.toBe(eventEl0[0]) // ensure it a rerender
+            expect(scrollEl.scrollTop()).toBe(scroll0)
+            done()
+          }
         }
-      }
-
-      $('#cal').fullCalendar(options)
-      scrollEl = $('#cal .fc-scroller')
+      }, calendarEl)
+      scrollEl = $('.fc-scroller', calendarEl)
     })
   }
 })

+ 13 - 30
tests/automated/legacy/slotDuration.js

@@ -1,29 +1,22 @@
-
 describe('slotDuration', function() {
 
   var minutesInADay = 1440
 
-  beforeEach(function() {
-    affix('#cal')
-  })
-
   describe('when using the default settings', function() {
     describe('in agendaWeek', function() {
       it('should have slots 1440/30 slots', function() {
-        var options = {
+        initCalendar({
           defaultView: 'agendaWeek'
-        }
-        $('#cal').fullCalendar(options)
+        })
         var slotCount = $('.fc-slats tr').length
         expect(slotCount).toEqual(Math.ceil(minutesInADay / 30))
       })
     })
     describe('in agendaDay', function() {
       it('should have slots 1440/30 slots', function() {
-        var options = {
+        initCalendar({
           defaultView: 'agendaDay'
-        }
-        $('#cal').fullCalendar(options)
+        })
         var slotCount = $('.fc-slats tr').length
         expect(slotCount).toEqual(Math.ceil(minutesInADay / 30))
       })
@@ -33,20 +26,18 @@ describe('slotDuration', function() {
   describe('when slotMinutes is set to 30', function() {
     describe('in agendaWeek', function() {
       it('should have slots 1440/30 slots', function() {
-        var options = {
+        initCalendar({
           defaultView: 'agendaWeek'
-        }
-        $('#cal').fullCalendar(options)
+        })
         var slotCount = $('.fc-slats tr').length
         expect(slotCount).toEqual(Math.ceil(minutesInADay / 30))
       })
     })
     describe('in agendaDay', function() {
       it('should have slots 1440/30 slots', function() {
-        var options = {
+        initCalendar({
           defaultView: 'agendaDay'
-        }
-        $('#cal').fullCalendar(options)
+        })
         var slotCount = $('.fc-slats tr').length
         expect(slotCount).toEqual(Math.ceil(minutesInADay / 30))
       })
@@ -58,16 +49,12 @@ describe('slotDuration', function() {
     var slotMinutesList = [ 10, 12, 15, 17, 20, 30, 35, 45, 60, 62, 120, 300 ]
 
     describe('in agendaWeek', function() {
-      beforeEach(function() {
-        affix('#cal2')
-      })
       slotMinutesList.forEach(function(slotMinutes) {
         it('should have slots 1440/x slots', function() {
-          var options = {
+          initCalendar({
             defaultView: 'agendaWeek',
             slotDuration: { minutes: slotMinutes }
-          }
-          $('#cal2').fullCalendar(options)
+          })
           var slotCount = $('.fc-slats tr').length
           var expected = Math.ceil(minutesInADay / slotMinutes)
           expect(slotCount).toEqual(expected)
@@ -76,16 +63,12 @@ describe('slotDuration', function() {
     })
 
     describe('in agendaDay', function() {
-      beforeEach(function() {
-        affix('#cal2')
-      })
       slotMinutesList.forEach(function(slotMinutes) {
         it('should have slots 1440/x slots', function() {
-          var options = {
-            defaultView: 'agendaWeek',
+          initCalendar({
+            defaultView: 'agendaDay',
             slotDuration: { minutes: slotMinutes }
-          }
-          $('#cal2').fullCalendar(options)
+          })
           var slotCount = $('.fc-slats tr').length
           var expected = Math.ceil(minutesInADay / slotMinutes)
           expect(slotCount).toEqual(expected)

+ 194 - 174
tests/automated/legacy/updateEvent.js

@@ -1,23 +1,18 @@
-
 describe('updateEvent', function() {
-  var options
-
-  beforeEach(function() {
-    affix('#cal')
-    options = {
-      defaultDate: '2014-05-01',
-      defaultView: 'month'
-    }
+
+  pushOptions({
+    defaultDate: '2014-05-01',
+    defaultView: 'month'
   })
 
   function getMainEvent() {
-    return $('#cal').fullCalendar('clientEvents', function(event) {
+    return currentCalendar.clientEvents(function(event) {
       return event.className[0] === 'mainEvent'
     })[0]
   }
 
   function getRelatedEvent() {
-    return $('#cal').fullCalendar('clientEvents', function(event) {
+    return currentCalendar.clientEvents(function(event) {
       return event.className[0] === 'relatedEvent'
     })[0]
   }
@@ -27,15 +22,16 @@ describe('updateEvent', function() {
       it('should move the start by the delta and leave the end as null', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.start.add(2, 'days')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.start).toEqualMoment('2014-05-03')
@@ -50,17 +46,18 @@ describe('updateEvent', function() {
       it('should move the start and end by the delta', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', end: '2014-05-12', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', end: '2014-05-12', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.start.add(2, 'days')
         expect(event.start).toEqualMoment('2014-05-03')
         expect(event.end).toBeNull()
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         relatedEvent = getRelatedEvent()
         expect(relatedEvent.start).toEqualMoment('2014-05-12')
@@ -74,15 +71,16 @@ describe('updateEvent', function() {
       it('should move the start by the delta and leave the end as null', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01T12:00:00', allDay: false, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10T06:00:00', allDay: false, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01T12:00:00', allDay: false, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10T06:00:00', allDay: false, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.start.add({ days: 2, hours: 2 })
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
         expect(event.start).toEqualMoment('2014-05-03T14:00:00')
         expect(event.end).toBeNull()
 
@@ -95,15 +93,16 @@ describe('updateEvent', function() {
       it('should move the start and end by the delta', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01T12:00:00', allDay: false, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-12T08:00:00', allDay: false, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01T12:00:00', allDay: false, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-12T08:00:00', allDay: false, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.start.add({ days: 2, hours: 2 })
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.start).toEqualMoment('2014-05-03T14:00:00')
@@ -121,16 +120,17 @@ describe('updateEvent', function() {
       it('should give the end a default duration plus the delta', function() {
         var event, relatedEvent
 
-        options.defaultAllDayEventDuration = { days: 2 }
-        options.events = [
-          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          defaultAllDayEventDuration: { days: 2 },
+          events: [
+            { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.end.add(1, 'days')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.start).toEqualMoment('2014-05-01')
@@ -145,15 +145,16 @@ describe('updateEvent', function() {
       it('should move the end by the delta', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.end.add(1, 'days')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.start).toEqualMoment('2014-05-01')
@@ -172,17 +173,18 @@ describe('updateEvent', function() {
         it('should give the end a default duration plus the delta', function() {
           var event, relatedEvent
 
-          options.forceEventDuration = false
-          options.defaultTimedEventDuration = { hours: 2 }
-          options.events = [
-            { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T15:00:00', allDay: false, className: 'mainEvent' },
-            { id: '1', start: '2014-05-10T16:00:00', allDay: false, className: 'relatedEvent' }
-          ]
-          $('#cal').fullCalendar(options)
+          initCalendar({
+            forceEventDuration: false,
+            defaultTimedEventDuration: { hours: 2 },
+            events: [
+              { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T15:00:00', allDay: false, className: 'mainEvent' },
+              { id: '1', start: '2014-05-10T16:00:00', allDay: false, className: 'relatedEvent' }
+            ]
+          })
 
           event = getMainEvent()
           event.end.add({ days: 1, hours: 1 })
-          $('#cal').fullCalendar('updateEvent', event)
+          currentCalendar.updateEvent(event)
 
           event = getMainEvent()
           expect(event.start).toEqualMoment('2014-05-01T12:00:00')
@@ -197,18 +199,19 @@ describe('updateEvent', function() {
         it('should reset end based on defaultTimedEventDuration', function() {
           var event, relatedEvent
 
-          options.forceEventDuration = true
-          options.defaultTimedEventDuration = { hours: 2 }
-          options.events = [
-            { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T15:00:00', allDay: false, className: 'mainEvent' },
-            { id: '1', start: '2014-05-10T16:00:00', end: '2014-05-10T19:00:00', allDay: false, className: 'relatedEvent' }
-          ]
-          $('#cal').fullCalendar(options)
+          initCalendar({
+            forceEventDuration: true,
+            defaultTimedEventDuration: { hours: 2 },
+            events: [
+              { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T15:00:00', allDay: false, className: 'mainEvent' },
+              { id: '1', start: '2014-05-10T16:00:00', end: '2014-05-10T19:00:00', allDay: false, className: 'relatedEvent' }
+            ]
+          })
 
           event = getMainEvent()
           event.start.add({ days: 1, hours: -12 })
           event.end = null
-          $('#cal').fullCalendar('updateEvent', event)
+          currentCalendar.updateEvent(event)
 
           event = getMainEvent()
           expect(event.start).toEqualMoment('2014-05-02T00:00:00')
@@ -223,17 +226,18 @@ describe('updateEvent', function() {
         it('should force a duration when updateEvent called', function() {
           var event
 
-          options.defaultTimedEventDuration = { hours: 1 }
-          options.events = [
-            { id: '1', start: '2014-05-01T12:00:00', allDay: false, className: 'mainEvent' }
-          ]
-          $('#cal').fullCalendar(options)
+          initCalendar({
+            defaultTimedEventDuration: { hours: 1 },
+            events: [
+              { id: '1', start: '2014-05-01T12:00:00', allDay: false, className: 'mainEvent' }
+            ]
+          })
 
           event = getMainEvent()
           expect(event.start).toEqualMoment('2014-05-01T12:00:00')
           expect(event.end).toBeNull()
 
-          $('#cal').fullCalendar('option', {
+          currentCalendar.option({
             forceEventDuration: true
           })
 
@@ -243,7 +247,7 @@ describe('updateEvent', function() {
           expect(event.end).toBeNull()
 
           event.start.add({ days: 1, hours: -12 })
-          $('#cal').fullCalendar('updateEvent', event)
+          currentCalendar.updateEvent(event)
 
           // should generate an end
           event = getMainEvent()
@@ -256,15 +260,16 @@ describe('updateEvent', function() {
       it('should move the end by the delta', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T14:00:00', allDay: false, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10T16:00:00', end: '2014-05-10T19:00:00', allDay: false, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T14:00:00', allDay: false, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10T16:00:00', end: '2014-05-10T19:00:00', allDay: false, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.end.add({ days: 1, hours: 1 })
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.start).toEqualMoment('2014-05-01T12:00:00')
@@ -281,16 +286,17 @@ describe('updateEvent', function() {
     it('should move the start and end of related events', function() {
       var event, relatedEvent
 
-      options.events = [
-        { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-        { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-      ]
-      $('#cal').fullCalendar(options)
+      initCalendar({
+        events: [
+          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+          { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+        ]
+      })
 
       event = getMainEvent()
       event.start.add(2, 'days')
       event.end.add(3, 'day')
-      $('#cal').fullCalendar('updateEvent', event)
+      currentCalendar.updateEvent(event)
 
       event = getMainEvent()
       expect(event.start).toEqualMoment('2014-05-03')
@@ -306,16 +312,17 @@ describe('updateEvent', function() {
     it('should move the start and end of related events', function() {
       var event, relatedEvent
 
-      options.events = [
-        { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
-        { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
-      ]
-      $('#cal').fullCalendar(options)
+      initCalendar({
+        events: [
+          { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
+          { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
+        ]
+      })
 
       event = getMainEvent()
       event.start.add({ days: 2, hours: 1 })
       event.end.add({ days: 3, hours: 2 })
-      $('#cal').fullCalendar('updateEvent', event)
+      currentCalendar.updateEvent(event)
 
       event = getMainEvent()
       expect(event.start).toEqualMoment('2014-05-03T07:00:00')
@@ -331,15 +338,16 @@ describe('updateEvent', function() {
     it('should erase the start\'s time and keep the event all-day', function() {
       var event, relatedEvent
 
-      options.events = [
-        { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-        { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-      ]
-      $('#cal').fullCalendar(options)
+      initCalendar({
+        events: [
+          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+          { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+        ]
+      })
 
       event = getMainEvent()
       event.start.time('18:00')
-      $('#cal').fullCalendar('updateEvent', event)
+      currentCalendar.updateEvent(event)
 
       event = getMainEvent()
       expect(event.allDay).toEqual(true)
@@ -358,15 +366,16 @@ describe('updateEvent', function() {
     it('should erase the start and end\'s times and keep the event all-day', function() {
       var event
 
-      options.events = [
-        { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' }
-      ]
-      $('#cal').fullCalendar(options)
+      initCalendar({
+        events: [
+          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' }
+        ]
+      })
 
       event = getMainEvent()
       event.start = moment('2014-05-01') // won't have an ambig time
       event.end = moment('2014-05-03') // "
-      $('#cal').fullCalendar('updateEvent', event)
+      currentCalendar.updateEvent(event)
 
       event = getMainEvent()
       expect(event.allDay).toEqual(true)
@@ -380,15 +389,16 @@ describe('updateEvent', function() {
       it('should make the event and related events allDay=false and 00:00', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.allDay = false
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.allDay).toEqual(false)
@@ -405,16 +415,17 @@ describe('updateEvent', function() {
       it('should adjust the event and related event\'s allDay/start/end', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.allDay = false
         event.start.time('14:00')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.allDay).toEqual(false)
@@ -431,16 +442,17 @@ describe('updateEvent', function() {
       it('should adjust the event and related event\'s allDay/start/end', function() {
         var event, relatedEvent
 
-        options.events = [
-          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.allDay = false
         event.start.add(1, 'days')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
 
         event = getMainEvent()
         expect(event.allDay).toEqual(false)
@@ -459,15 +471,16 @@ describe('updateEvent', function() {
     it('should adjust the event and related event\'s allDay/start/end', function() {
       var event, relatedEvent
 
-      options.events = [
-        { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
-        { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
-      ]
-      $('#cal').fullCalendar(options)
+      initCalendar({
+        events: [
+          { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
+          { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
+        ]
+      })
 
       event = getMainEvent()
       event.allDay = true
-      $('#cal').fullCalendar('updateEvent', event)
+      currentCalendar.updateEvent(event)
 
       event = getMainEvent()
       expect(event.allDay).toEqual(true)
@@ -482,16 +495,17 @@ describe('updateEvent', function() {
     it('should adjust the event and related event\'s allDay/start/end and account for a new start', function() {
       var event, relatedEvent
 
-      options.events = [
-        { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
-        { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
-      ]
-      $('#cal').fullCalendar(options)
+      initCalendar({
+        events: [
+          { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
+          { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
+        ]
+      })
 
       event = getMainEvent()
       event.allDay = true
       event.start.add(1, 'days')
-      $('#cal').fullCalendar('updateEvent', event)
+      currentCalendar.updateEvent(event)
 
       event = getMainEvent()
       expect(event.allDay).toEqual(true)
@@ -508,16 +522,17 @@ describe('updateEvent', function() {
   it('should accept moments that have unnormalized start/end', function() {
     var event, relatedEvent
 
-    options.events = [
-      { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
-      { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
-    ]
-    $('#cal').fullCalendar(options)
+    initCalendar({
+      events: [
+        { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false, className: 'mainEvent' },
+        { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false, className: 'relatedEvent' }
+      ]
+    })
 
     event = getMainEvent()
     event.start = '2014-05-02T06:00:00' // move by 1 day
     event.end = '2014-05-05T06:00:00' // increase duration by 1 day
-    $('#cal').fullCalendar('updateEvent', event)
+    currentCalendar.updateEvent(event)
 
     event = getMainEvent()
     expect(event.allDay).toEqual(false)
@@ -537,15 +552,16 @@ describe('updateEvent', function() {
   it('should copy color-related properties to related events', function() {
     var event, relatedEvent
 
-    options.events = [
-      { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-      { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-    ]
-    $('#cal').fullCalendar(options)
+    initCalendar({
+      events: [
+        { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+        { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+      ]
+    })
 
     event = getMainEvent()
     event.color = 'red'
-    $('#cal').fullCalendar('updateEvent', event)
+    currentCalendar.updateEvent(event)
 
     relatedEvent = getRelatedEvent()
     expect(relatedEvent.color).toBe('red')
@@ -555,16 +571,17 @@ describe('updateEvent', function() {
     var event, relatedEvent
     var specialObj = {}
 
-    options.events = [
-      { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-      { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-    ]
-    $('#cal').fullCalendar(options)
+    initCalendar({
+      events: [
+        { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+        { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+      ]
+    })
 
     event = getMainEvent()
     event.someForeignKey = '123'
     event.myObj = specialObj
-    $('#cal').fullCalendar('updateEvent', event)
+    currentCalendar.updateEvent(event)
 
     relatedEvent = getRelatedEvent()
     expect(relatedEvent.someForeignKey).toBe('123')
@@ -576,15 +593,16 @@ describe('updateEvent', function() {
       beforeEach(function() {
         var event
 
-        options.events = [
-          { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false, className: 'mainEvent' },
-          { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false, className: 'mainEvent' },
+            { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.start.add(2, 'hours')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
       })
       should()
     })
@@ -595,15 +613,16 @@ describe('updateEvent', function() {
       beforeEach(function() {
         var event
 
-        options.events = [
-          { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false, className: 'mainEvent' },
-          { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false, className: 'mainEvent' },
+            { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.end.add(2, 'hours')
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
       })
       should()
     })
@@ -614,15 +633,16 @@ describe('updateEvent', function() {
       beforeEach(function() {
         var event
 
-        options.events = [
-          { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
-          { id: '1', start: '2014-05-11', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
-        ]
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          events: [
+            { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true, className: 'mainEvent' },
+            { id: '1', start: '2014-05-11', end: '2014-05-13', allDay: true, className: 'relatedEvent' }
+          ]
+        })
 
         event = getMainEvent()
         event.allDay = false
-        $('#cal').fullCalendar('updateEvent', event)
+        currentCalendar.updateEvent(event)
       })
       should()
     })
@@ -688,8 +708,8 @@ describe('updateEvent', function() {
   }
 
   describe('when calendar has no timezone', function() {
-    beforeEach(function() {
-      options.timezone = false
+    pushOptions({
+      timezone: false
     })
     whenMovingStart(shouldBeAmbiguouslyZoned)
     whenMovingEnd(shouldBeAmbiguouslyZoned)
@@ -697,8 +717,8 @@ describe('updateEvent', function() {
   })
 
   describe('when calendar has a local timezone', function() {
-    beforeEach(function() {
-      options.timezone = 'local'
+    pushOptions({
+      timezone: 'local'
     })
     whenMovingStart(shouldBeLocal)
     whenMovingEnd(shouldBeLocal)
@@ -706,8 +726,8 @@ describe('updateEvent', function() {
   })
 
   describe('when calendar has a UTC timezone', function() {
-    beforeEach(function() {
-      options.timezone = 'UTC'
+    pushOptions({
+      timezone: 'UTC'
     })
     whenMovingStart(shouldBeUTC)
     whenMovingEnd(shouldBeUTC)
@@ -715,8 +735,8 @@ describe('updateEvent', function() {
   })
 
   describe('when calendar has a custom timezone', function() {
-    beforeEach(function() {
-      options.timezone = 'America/Chicago'
+    pushOptions({
+      timezone: 'America/Chicago'
     })
     whenMovingStart(shouldBeAmbiguouslyZoned)
     whenMovingEnd(shouldBeAmbiguouslyZoned)

+ 26 - 38
tests/automated/legacy/viewDestroy.js

@@ -1,60 +1,48 @@
-
 describe('viewDestroy', function() {
-  var options
 
-  beforeEach(function() {
-    options = {
-      defaultDate: '2015-02-20'
-    }
-    affix('#cal')
+  pushOptions({
+    defaultDate: '2015-02-20'
   })
 
   describe('when in month view', function() {
-    beforeEach(function() {
-      options = {
-        defaultView: 'month'
-      }
+    pushOptions({
+      defaultView: 'month'
     })
     defineTests()
   })
 
   describe('when in agendaWeek view', function() {
-    beforeEach(function() {
-      options = {
-        defaultView: 'agendaWeek'
-      }
+    pushOptions({
+      defaultView: 'agendaWeek'
     })
     defineTests()
   })
 
   function defineTests() {
-
     it('fires before the view is unrendered, with correct arguments', function(done) {
       var viewRenderCalls = 0
       var viewDestroyCalls = 0
-
-      options.viewRender = function() {
-        ++viewRenderCalls
-      }
-
-      options.viewDestroy = function(givenViewObj, givenViewEl) {
-        if (++viewDestroyCalls === 1) { // because done() calls destroy
-
-          // the viewDestroy should be called before the next viewRender
-          expect(viewRenderCalls).toBe(1)
-
-          var viewObj = $('#cal').fullCalendar('getView')
-          var viewEl = $('#cal .fc-view')
-
-          expect(viewObj).toBe(givenViewObj)
-          expect(viewEl[0]).toBe(givenViewEl)
-          expect(viewEl.children().length >= 1).toBe(true) // is the content still rendered?
-          done()
+      initCalendar({
+        viewRender: function() {
+          ++viewRenderCalls
+        },
+        viewDestroy: function(givenViewObj, givenViewEl) {
+          if (++viewDestroyCalls === 1) { // because done() calls destroy
+
+            // the viewDestroy should be called before the next viewRender
+            expect(viewRenderCalls).toBe(1)
+
+            var viewObj = currentCalendar.getView()
+            var viewEl = $('.fc-view', currentCalendar.el)
+
+            expect(viewObj).toBe(givenViewObj)
+            expect(viewEl[0]).toBe(givenViewEl)
+            expect(viewEl.children().length >= 1).toBe(true) // is the content still rendered?
+            done()
+          }
         }
-      }
-
-      $('#cal').fullCalendar(options)
-      $('#cal').fullCalendar('next') // will trigger a viewDestroy/viewRender
+      })
+      currentCalendar.next() // trigger viewDestroy/viewRender
     })
   }
 })

+ 26 - 31
tests/automated/legacy/weekNumberCalculation.js

@@ -1,12 +1,7 @@
-
 describe('weekNumberCalculation', function() {
 
-  var options
-
-  beforeEach(function() {
-    options = {
-      weekNumbers: true
-    }
+  pushOptions({
+    weekNumbers: true
   })
 
   function getRenderedWeekText() {
@@ -19,52 +14,52 @@ describe('weekNumberCalculation', function() {
     return parseInt(text.replace(/\D/g, ''), 10)
   }
 
-  beforeEach(function() {
-    affix('#cal')
-  });
-
   [ 'basicDay', 'agendaDay' ].forEach(function(viewType) {
     describe('when in ' + viewType + ' view', function() {
-
-      beforeEach(function() {
-        options.defaultView = viewType
+      pushOptions({
+        defaultView: viewType
       })
 
       it('should display the American standard when using \'local\'', function() {
-        options.defaultDate = '2013-11-23' // a Saturday
-        options.weekNumberCalculation = 'local'
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          defaultDate: '2013-11-23', // a Saturday
+          weekNumberCalculation: 'local'
+        })
         expect(getRenderedWeekNumber()).toBe(47)
       })
 
       it('should display a locale-specific local week number', function() {
-        options.defaultDate = '2013-11-23' // a Saturday
-        options.locale = 'ar'
-        options.weekNumberCalculation = 'local'
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          defaultDate: '2013-11-23', // a Saturday
+          locale: 'ar',
+          weekNumberCalculation: 'local'
+        })
         expect(getRenderedWeekText()).toMatch(/٤٨|48/)
       })
 
       // another local test, but to make sure it is different from ISO
       it('should display the American standard when using \'local\'', function() {
-        options.defaultDate = '2013-11-17' // a Sunday
-        options.weekNumberCalculation = 'local'
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          defaultDate: '2013-11-17', // a Sunday
+          weekNumberCalculation: 'local'
+        })
         expect(getRenderedWeekNumber()).toBe(47)
       })
 
       it('should display ISO standard when using \'ISO\'', function() {
-        options.defaultDate = '2013-11-17' // a Sunday
-        options.weekNumberCalculation = 'ISO'
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          defaultDate: '2013-11-17', // a Sunday
+          weekNumberCalculation: 'ISO'
+        })
         expect(getRenderedWeekNumber()).toBe(46)
       })
 
       it('should display the calculated number when a custom function', function() {
-        options.weekNumberCalculation = function() {
-          return 4
-        }
-        $('#cal').fullCalendar(options)
+        initCalendar({
+          weekNumberCalculation: function() {
+            return 4
+          }
+        })
         expect(getRenderedWeekNumber()).toBe(4)
       })
     })

+ 13 - 0
webpack.config.js

@@ -14,6 +14,8 @@ const MODULES = {
   'dist/fullcalendar.css': './src/main.scss',
   'dist/fullcalendar.print.css': './src/common/print.scss',
   'dist/gcal': './plugins/gcal/main.ts',
+  'dist/jquery-ui-draggable': './plugins/jquery-ui-draggable/main.ts',
+  'dist/dragula': './plugins/dragula/main.ts',
   'tmp/automated-tests': './tests/automated/index'
 }
 
@@ -34,6 +36,17 @@ module.exports = {
     // use our external reference instead.
     '../moment': 'moment',
 
+    // for plugins that might need jQuery
+    jquery: {
+      commonjs: 'jquery',
+      commonjs2: 'jquery',
+      amd: 'jquery',
+      root: 'jQuery'
+    },
+
+    // for plugins
+    dragula: 'dragula',
+
     // plugins reference the root 'fullcalendar' namespace
     fullcalendar: {
       commonjs: 'fullcalendar',

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä