
/*!
FullCalendar Scheduler v1.7.0
Docs & License: https://fullcalendar.io/scheduler/
(c) 2017 Adam Shaw
 */
(function(factory) {
	if (typeof define === 'function' && define.amd) {
		define([ 'jquery', 'moment', 'fullcalendar' ], factory);
	}
	else if (typeof exports === 'object') { // Node/CommonJS
		module.exports = factory(
			require('jquery'),
			require('moment'),
			require('fullcalendar')
		);
	}
	else {
		factory(jQuery, moment);
	}
})(function($, moment) {;
var COL_MIN_WIDTH, Calendar, CalendarExtension, Class, ClippedScroller, ComponentFootprint, CoordCache, DEFAULT_GRID_DURATION, DragListener, EmitterMixin, EnhancedScroller, EventDef, EventDefMutation, EventFootprint, EventRow, EventSource, FC, Grid, HRowGroup, LICENSE_INFO_URL, ListenerMixin, MAX_AUTO_CELLS, MAX_AUTO_SLOTS_PER_LABEL, MAX_CELLS, MIN_AUTO_LABELS, PRESET_LICENSE_KEYS, Promise, RELEASE_DATE, Resource, ResourceAgendaView, ResourceBasicView, ResourceComponentFootprint, ResourceDayGrid, ResourceDayTableMixin, ResourceGridMixin, ResourceManager, ResourceMonthView, ResourceRow, ResourceTimeGrid, ResourceTimelineGrid, ResourceTimelineView, ResourceViewMixin, RowGroup, RowParent, STOCK_SUB_DURATIONS, ScrollFollower, ScrollFollowerSprite, ScrollJoiner, ScrollerCanvas, Spreadsheet, TaskQueue, TimelineGrid, TimelineView, UPGRADE_WINDOW, UnzonedRange, VRowGroup, VertResourceViewMixin, View, _filterResourcesWithEvents, applyAll, capitaliseFirstLetter, compareByFieldSpecs, computeGreatestUnit, computeOffsetForSeg, computeOffsetForSegs, copyRect, cssToStr, debounce, detectWarningInContainer, divideDurationByDuration, divideRangeByDuration, durationHasTime, flexibleCompare, getContentRect, getOuterRect, getOwnCells, getRectHeight, getRectWidth, getScrollbarWidths, hContainRect, htmlEscape, intersectRects, isImmuneUrl, isInt, isValidKey, joinRects, multiplyDuration, oldMutateSingle, origApplyOtherRawProps, origEventDefClone, origEventDefToLegacy, origGetSegCustomClasses, origGetSegDefaultBackgroundColor, origGetSegDefaultBorderColor, origGetSegDefaultTextColor, origOnBaseRender, origRemoveElement, origSetElement, parseFieldSpecs, processLicenseKey, proxy, removeExact, renderingWarningInContainer, testRectContains, testRectHContains, testRectVContains, timeRowSegsCollide, vContainRect,
  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  hasProp = {}.hasOwnProperty,
  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

FC = $.fullCalendar;

FC.schedulerVersion = "1.7.0";

if (FC.internalApiVersion !== 9) {
  FC.warn('v' + FC.schedulerVersion + ' of FullCalendar Scheduler ' + 'is incompatible with v' + FC.version + ' of the core.\n' + 'Please see http://fullcalendar.io/support/ for more information.');
  return;
}

Calendar = FC.Calendar;

Class = FC.Class;

View = FC.View;

Grid = FC.Grid;

debounce = FC.debounce;

isInt = FC.isInt;

removeExact = FC.removeExact;

getScrollbarWidths = FC.getScrollbarWidths;

DragListener = FC.DragListener;

htmlEscape = FC.htmlEscape;

computeGreatestUnit = FC.computeGreatestUnit;

proxy = FC.proxy;

capitaliseFirstLetter = FC.capitaliseFirstLetter;

applyAll = FC.applyAll;

EmitterMixin = FC.EmitterMixin;

ListenerMixin = FC.ListenerMixin;

durationHasTime = FC.durationHasTime;

divideRangeByDuration = FC.divideRangeByDuration;

divideDurationByDuration = FC.divideDurationByDuration;

multiplyDuration = FC.multiplyDuration;

parseFieldSpecs = FC.parseFieldSpecs;

compareByFieldSpecs = FC.compareByFieldSpecs;

flexibleCompare = FC.flexibleCompare;

intersectRects = FC.intersectRects;

CoordCache = FC.CoordCache;

getContentRect = FC.getContentRect;

getOuterRect = FC.getOuterRect;

Promise = FC.Promise;

TaskQueue = FC.TaskQueue;

UnzonedRange = FC.UnzonedRange;

ComponentFootprint = FC.ComponentFootprint;

EventDef = FC.EventDef;

EventSource = FC.EventSource;

EventFootprint = FC.EventFootprint;

EventDefMutation = FC.EventDefMutation;


/*
Given a jQuery <tr> set, returns the <td>'s that do not have multi-line rowspans.
Would use the [rowspan] selector, but never not defined in IE8.
 */

getOwnCells = function(trs) {
  return trs.find('> td').filter(function(i, tdNode) {
    return tdNode.rowSpan <= 1;
  });
};


/*
A Scroller with additional functionality:
- optional ScrollerCanvas for content
- fired events for scroll start/end
- cross-browser normalization of horizontal scroll for RTL
 */

EnhancedScroller = (function(superClass) {
  var detectRtlScrollSystem, rtlScrollSystem;

  extend(EnhancedScroller, superClass);

  EnhancedScroller.mixin(EmitterMixin);

  EnhancedScroller.mixin(ListenerMixin);

  EnhancedScroller.prototype.canvas = null;

  EnhancedScroller.prototype.isScrolling = false;

  EnhancedScroller.prototype.isTouching = false;

  EnhancedScroller.prototype.isTouchedEver = false;

  EnhancedScroller.prototype.isMoving = false;

  EnhancedScroller.prototype.isTouchScrollEnabled = true;

  EnhancedScroller.prototype.preventTouchScrollHandler = null;

  function EnhancedScroller() {
    EnhancedScroller.__super__.constructor.apply(this, arguments);
    this.requestMovingEnd = debounce(this.reportMovingEnd, 500);
  }

  EnhancedScroller.prototype.render = function() {
    EnhancedScroller.__super__.render.apply(this, arguments);
    if (this.canvas) {
      this.canvas.render();
      this.canvas.el.appendTo(this.scrollEl);
    }
    return this.bindHandlers();
  };

  EnhancedScroller.prototype.destroy = function() {
    EnhancedScroller.__super__.destroy.apply(this, arguments);
    return this.unbindHandlers();
  };

  EnhancedScroller.prototype.disableTouchScroll = function() {
    this.isTouchScrollEnabled = false;
    return this.bindPreventTouchScroll();
  };

  EnhancedScroller.prototype.enableTouchScroll = function() {
    this.isTouchScrollEnabled = true;
    if (!this.isTouching) {
      return this.unbindPreventTouchScroll();
    }
  };

  EnhancedScroller.prototype.bindPreventTouchScroll = function() {
    if (!this.preventTouchScrollHandler) {
      return this.scrollEl.on('touchmove', this.preventTouchScrollHandler = FC.preventDefault);
    }
  };

  EnhancedScroller.prototype.unbindPreventTouchScroll = function() {
    if (this.preventTouchScrollHandler) {
      this.scrollEl.off('touchmove', this.preventTouchScrollHandler);
      return this.preventTouchScrollHandler = null;
    }
  };

  EnhancedScroller.prototype.bindHandlers = function() {
    return this.listenTo(this.scrollEl, {
      scroll: this.reportScroll,
      touchstart: this.reportTouchStart,
      touchend: this.reportTouchEnd
    });
  };

  EnhancedScroller.prototype.unbindHandlers = function() {
    return this.stopListeningTo(this.scrollEl);
  };

  EnhancedScroller.prototype.reportScroll = function() {
    if (!this.isScrolling) {
      this.reportScrollStart();
    }
    this.trigger('scroll');
    this.isMoving = true;
    return this.requestMovingEnd();
  };

  EnhancedScroller.prototype.reportScrollStart = function() {
    if (!this.isScrolling) {
      this.isScrolling = true;
      return this.trigger('scrollStart', this.isTouching);
    }
  };

  EnhancedScroller.prototype.requestMovingEnd = null;

  EnhancedScroller.prototype.reportMovingEnd = function() {
    this.isMoving = false;
    if (!this.isTouching) {
      return this.reportScrollEnd();
    }
  };

  EnhancedScroller.prototype.reportScrollEnd = function() {
    if (this.isScrolling) {
      this.trigger('scrollEnd');
      return this.isScrolling = false;
    }
  };

  EnhancedScroller.prototype.reportTouchStart = function() {
    this.isTouching = true;
    return this.isTouchedEver = true;
  };

  EnhancedScroller.prototype.reportTouchEnd = function() {
    if (this.isTouching) {
      this.isTouching = false;
      if (this.isTouchScrollEnabled) {
        this.unbindPreventTouchScroll();
      }
      if (!this.isMoving) {
        return this.reportScrollEnd();
      }
    }
  };


  /*
  	If RTL, and scrolled to the left, returns NEGATIVE value (like Firefox)
   */

  EnhancedScroller.prototype.getScrollLeft = function() {
    var direction, node, val;
    direction = this.scrollEl.css('direction');
    node = this.scrollEl[0];
    val = node.scrollLeft;
    if (direction === 'rtl') {
      switch (rtlScrollSystem) {
        case 'positive':
          val = val + node.clientWidth - node.scrollWidth;
          break;
        case 'reverse':
          val = -val;
      }
    }
    return val;
  };


  /*
  	Accepts a NEGATIVE value for when scrolled in RTL
   */

  EnhancedScroller.prototype.setScrollLeft = function(val) {
    var direction, node;
    direction = this.scrollEl.css('direction');
    node = this.scrollEl[0];
    if (direction === 'rtl') {
      switch (rtlScrollSystem) {
        case 'positive':
          val = val - node.clientWidth + node.scrollWidth;
          break;
        case 'reverse':
          val = -val;
      }
    }
    return node.scrollLeft = val;
  };


  /*
  	Always returns the number of pixels scrolled from the leftmost position (even if RTL).
  	Always positive.
   */

  EnhancedScroller.prototype.getScrollFromLeft = function() {
    var direction, node, val;
    direction = this.scrollEl.css('direction');
    node = this.scrollEl[0];
    val = node.scrollLeft;
    if (direction === 'rtl') {
      switch (rtlScrollSystem) {
        case 'negative':
          val = val - node.clientWidth + node.scrollWidth;
          break;
        case 'reverse':
          val = -val - node.clientWidth + node.scrollWidth;
      }
    }
    return val;
  };

  EnhancedScroller.prototype.getNativeScrollLeft = function() {
    return this.scrollEl[0].scrollLeft;
  };

  EnhancedScroller.prototype.setNativeScrollLeft = function(val) {
    return this.scrollEl[0].scrollLeft = val;
  };

  rtlScrollSystem = null;

  detectRtlScrollSystem = function() {
    var el, node, system;
    el = $('<div style=" position: absolute; top: -1000px; width: 1px; height: 1px; overflow: scroll; direction: rtl; font-size: 100px; ">A</div>').appendTo('body');
    node = el[0];
    system = node.scrollLeft > 0 ? 'positive' : (node.scrollLeft = 1, el.scrollLeft > 0 ? 'reverse' : 'negative');
    el.remove();
    return system;
  };

  $(function() {
    return rtlScrollSystem = detectRtlScrollSystem();
  });

  return EnhancedScroller;

})(FC.Scroller);


/*
A Scroller, but with a wrapping div that allows "clipping" away of native scrollbars,
giving the appearance that there are no scrollbars.
 */

ClippedScroller = (function(superClass) {
  extend(ClippedScroller, superClass);

  ClippedScroller.prototype.isHScrollbarsClipped = false;

  ClippedScroller.prototype.isVScrollbarsClipped = false;


  /*
  	Received overflows can be set to 'clipped', meaning scrollbars shouldn't be visible
  	to the user, but the area should still scroll.
   */

  function ClippedScroller() {
    ClippedScroller.__super__.constructor.apply(this, arguments);
    if (this.overflowX === 'clipped-scroll') {
      this.overflowX = 'scroll';
      this.isHScrollbarsClipped = true;
    }
    if (this.overflowY === 'clipped-scroll') {
      this.overflowY = 'scroll';
      this.isVScrollbarsClipped = true;
    }
  }

  ClippedScroller.prototype.renderEl = function() {
    var scrollEl;
    scrollEl = ClippedScroller.__super__.renderEl.apply(this, arguments);
    return $('<div class="fc-scroller-clip" />').append(scrollEl);
  };

  ClippedScroller.prototype.updateSize = function() {
    var cssProps, scrollEl, scrollbarWidths;
    scrollEl = this.scrollEl;
    scrollbarWidths = getScrollbarWidths(scrollEl);
    cssProps = {
      marginLeft: 0,
      marginRight: 0,
      marginTop: 0,
      marginBottom: 0
    };
    if (this.isHScrollbarsClipped) {
      cssProps.marginTop = -scrollbarWidths.top;
      cssProps.marginBottom = -scrollbarWidths.bottom;
    }
    if (this.isVScrollbarsClipped) {
      cssProps.marginLeft = -scrollbarWidths.left;
      cssProps.marginRight = -scrollbarWidths.right;
    }
    scrollEl.css(cssProps);
    return scrollEl.toggleClass('fc-no-scrollbars', (this.isHScrollbarsClipped || this.overflowX === 'hidden') && (this.isVScrollbarsClipped || this.overflowY === 'hidden') && !(scrollbarWidths.top || scrollbarWidths.bottom || scrollbarWidths.left || scrollbarWidths.right));
  };


  /*
  	Accounts for 'clipped' scrollbars
   */

  ClippedScroller.prototype.getScrollbarWidths = function() {
    var widths;
    widths = getScrollbarWidths(this.scrollEl);
    if (this.isHScrollbarsClipped) {
      widths.top = 0;
      widths.bottom = 0;
    }
    if (this.isVScrollbarsClipped) {
      widths.left = 0;
      widths.right = 0;
    }
    return widths;
  };

  return ClippedScroller;

})(EnhancedScroller);


/*
A rectangular area of content that lives within a Scroller.
Can have "gutters", areas of dead spacing around the perimeter.
Also very useful for forcing a width, which a Scroller cannot do alone.
Has a content area that lives above a background area.
 */

ScrollerCanvas = (function() {
  ScrollerCanvas.prototype.el = null;

  ScrollerCanvas.prototype.contentEl = null;

  ScrollerCanvas.prototype.bgEl = null;

  ScrollerCanvas.prototype.gutters = null;

  ScrollerCanvas.prototype.width = null;

  ScrollerCanvas.prototype.minWidth = null;

  function ScrollerCanvas() {
    this.gutters = {};
  }

  ScrollerCanvas.prototype.render = function() {
    this.el = $('<div class="fc-scroller-canvas"> <div class="fc-content"></div> <div class="fc-bg"></div> </div>');
    this.contentEl = this.el.find('.fc-content');
    return this.bgEl = this.el.find('.fc-bg');
  };


  /*
  	If falsy, resets all the gutters to 0
   */

  ScrollerCanvas.prototype.setGutters = function(gutters) {
    if (!gutters) {
      this.gutters = {};
    } else {
      $.extend(this.gutters, gutters);
    }
    return this.updateSize();
  };

  ScrollerCanvas.prototype.setWidth = function(width1) {
    this.width = width1;
    return this.updateSize();
  };

  ScrollerCanvas.prototype.setMinWidth = function(minWidth1) {
    this.minWidth = minWidth1;
    return this.updateSize();
  };

  ScrollerCanvas.prototype.clearWidth = function() {
    this.width = null;
    this.minWidth = null;
    return this.updateSize();
  };

  ScrollerCanvas.prototype.updateSize = function() {
    var gutters;
    gutters = this.gutters;
    this.el.toggleClass('fc-gutter-left', Boolean(gutters.left)).toggleClass('fc-gutter-right', Boolean(gutters.right)).toggleClass('fc-gutter-top', Boolean(gutters.top)).toggleClass('fc-gutter-bottom', Boolean(gutters.bottom)).css({
      paddingLeft: gutters.left || '',
      paddingRight: gutters.right || '',
      paddingTop: gutters.top || '',
      paddingBottom: gutters.bottom || '',
      width: this.width != null ? this.width + (gutters.left || 0) + (gutters.right || 0) : '',
      minWidth: this.minWidth != null ? this.minWidth + (gutters.left || 0) + (gutters.right || 0) : ''
    });
    return this.bgEl.css({
      left: gutters.left || '',
      right: gutters.right || '',
      top: gutters.top || '',
      bottom: gutters.bottom || ''
    });
  };

  return ScrollerCanvas;

})();

ScrollJoiner = (function() {
  ScrollJoiner.prototype.axis = null;

  ScrollJoiner.prototype.scrollers = null;

  ScrollJoiner.prototype.masterScroller = null;

  function ScrollJoiner(axis, scrollers) {
    var j, len, ref, scroller;
    this.axis = axis;
    this.scrollers = scrollers;
    ref = this.scrollers;
    for (j = 0, len = ref.length; j < len; j++) {
      scroller = ref[j];
      this.initScroller(scroller);
    }
    return;
  }

  ScrollJoiner.prototype.initScroller = function(scroller) {
    scroller.scrollEl.on('wheel mousewheel DomMouseScroll MozMousePixelScroll', (function(_this) {
      return function() {
        _this.assignMasterScroller(scroller);
      };
    })(this));
    return scroller.on('scrollStart', (function(_this) {
      return function() {
        if (!_this.masterScroller) {
          return _this.assignMasterScroller(scroller);
        }
      };
    })(this)).on('scroll', (function(_this) {
      return function() {
        var j, len, otherScroller, ref, results;
        if (scroller === _this.masterScroller) {
          ref = _this.scrollers;
          results = [];
          for (j = 0, len = ref.length; j < len; j++) {
            otherScroller = ref[j];
            if (otherScroller !== scroller) {
              switch (_this.axis) {
                case 'horizontal':
                  results.push(otherScroller.setNativeScrollLeft(scroller.getNativeScrollLeft()));
                  break;
                case 'vertical':
                  results.push(otherScroller.setScrollTop(scroller.getScrollTop()));
                  break;
                default:
                  results.push(void 0);
              }
            } else {
              results.push(void 0);
            }
          }
          return results;
        }
      };
    })(this)).on('scrollEnd', (function(_this) {
      return function() {
        if (scroller === _this.masterScroller) {
          return _this.unassignMasterScroller();
        }
      };
    })(this));
  };

  ScrollJoiner.prototype.assignMasterScroller = function(scroller) {
    var j, len, otherScroller, ref;
    this.unassignMasterScroller();
    this.masterScroller = scroller;
    ref = this.scrollers;
    for (j = 0, len = ref.length; j < len; j++) {
      otherScroller = ref[j];
      if (otherScroller !== scroller) {
        otherScroller.disableTouchScroll();
      }
    }
  };

  ScrollJoiner.prototype.unassignMasterScroller = function() {
    var j, len, otherScroller, ref;
    if (this.masterScroller) {
      ref = this.scrollers;
      for (j = 0, len = ref.length; j < len; j++) {
        otherScroller = ref[j];
        otherScroller.enableTouchScroll();
      }
      this.masterScroller = null;
    }
  };

  ScrollJoiner.prototype.update = function() {
    var allWidths, i, j, k, len, len1, maxBottom, maxLeft, maxRight, maxTop, ref, scroller, widths;
    allWidths = (function() {
      var j, len, ref, results;
      ref = this.scrollers;
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        scroller = ref[j];
        results.push(scroller.getScrollbarWidths());
      }
      return results;
    }).call(this);
    maxLeft = maxRight = maxTop = maxBottom = 0;
    for (j = 0, len = allWidths.length; j < len; j++) {
      widths = allWidths[j];
      maxLeft = Math.max(maxLeft, widths.left);
      maxRight = Math.max(maxRight, widths.right);
      maxTop = Math.max(maxTop, widths.top);
      maxBottom = Math.max(maxBottom, widths.bottom);
    }
    ref = this.scrollers;
    for (i = k = 0, len1 = ref.length; k < len1; i = ++k) {
      scroller = ref[i];
      widths = allWidths[i];
      scroller.canvas.setGutters(this.axis === 'horizontal' ? {
        left: maxLeft - widths.left,
        right: maxRight - widths.right
      } : {
        top: maxTop - widths.top,
        bottom: maxBottom - widths.bottom
      });
    }
  };

  return ScrollJoiner;

})();

ScrollFollower = (function() {
  ScrollFollower.prototype.scroller = null;

  ScrollFollower.prototype.scrollbarWidths = null;

  ScrollFollower.prototype.sprites = null;

  ScrollFollower.prototype.viewportRect = null;

  ScrollFollower.prototype.contentOffset = null;

  ScrollFollower.prototype.isHFollowing = true;

  ScrollFollower.prototype.isVFollowing = false;

  ScrollFollower.prototype.allowPointerEvents = false;

  ScrollFollower.prototype.containOnNaturalLeft = false;

  ScrollFollower.prototype.containOnNaturalRight = false;

  ScrollFollower.prototype.minTravel = 0;

  ScrollFollower.prototype.isTouch = false;

  ScrollFollower.prototype.isForcedRelative = false;

  function ScrollFollower(scroller, allowPointerEvents) {
    this.allowPointerEvents = allowPointerEvents != null ? allowPointerEvents : false;
    this.scroller = scroller;
    this.sprites = [];
    scroller.on('scroll', (function(_this) {
      return function() {
        if (scroller.isTouchedEver) {
          _this.isTouch = true;
          return _this.isForcedRelative = true;
        } else {
          _this.isTouch = false;
          _this.isForcedRelative = false;
          return _this.handleScroll();
        }
      };
    })(this));
    scroller.on('scrollEnd', (function(_this) {
      return function() {
        return _this.handleScroll();
      };
    })(this));
  }

  ScrollFollower.prototype.setSprites = function(sprites) {
    var j, len, sprite;
    this.clearSprites();
    if (sprites instanceof $) {
      return this.sprites = (function() {
        var j, len, results;
        results = [];
        for (j = 0, len = sprites.length; j < len; j++) {
          sprite = sprites[j];
          results.push(new ScrollFollowerSprite($(sprite), this));
        }
        return results;
      }).call(this);
    } else {
      for (j = 0, len = sprites.length; j < len; j++) {
        sprite = sprites[j];
        sprite.follower = this;
      }
      return this.sprites = sprites;
    }
  };

  ScrollFollower.prototype.clearSprites = function() {
    var j, len, ref, sprite;
    ref = this.sprites;
    for (j = 0, len = ref.length; j < len; j++) {
      sprite = ref[j];
      sprite.clear();
    }
    return this.sprites = [];
  };

  ScrollFollower.prototype.handleScroll = function() {
    this.updateViewport();
    return this.updatePositions();
  };

  ScrollFollower.prototype.cacheDimensions = function() {
    var j, len, ref, sprite;
    this.updateViewport();
    this.scrollbarWidths = this.scroller.getScrollbarWidths();
    this.contentOffset = this.scroller.canvas.el.offset();
    ref = this.sprites;
    for (j = 0, len = ref.length; j < len; j++) {
      sprite = ref[j];
      sprite.cacheDimensions();
    }
  };

  ScrollFollower.prototype.updateViewport = function() {
    var left, scroller, top;
    scroller = this.scroller;
    left = scroller.getScrollFromLeft();
    top = scroller.getScrollTop();
    return this.viewportRect = {
      left: left,
      right: left + scroller.getClientWidth(),
      top: top,
      bottom: top + scroller.getClientHeight()
    };
  };

  ScrollFollower.prototype.forceRelative = function() {
    var j, len, ref, results, sprite;
    if (!this.isForcedRelative) {
      this.isForcedRelative = true;
      ref = this.sprites;
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        sprite = ref[j];
        if (sprite.doAbsolute) {
          results.push(sprite.assignPosition());
        } else {
          results.push(void 0);
        }
      }
      return results;
    }
  };

  ScrollFollower.prototype.clearForce = function() {
    var j, len, ref, results, sprite;
    if (this.isForcedRelative && !this.isTouch) {
      this.isForcedRelative = false;
      ref = this.sprites;
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        sprite = ref[j];
        results.push(sprite.assignPosition());
      }
      return results;
    }
  };

  ScrollFollower.prototype.update = function() {
    this.cacheDimensions();
    return this.updatePositions();
  };

  ScrollFollower.prototype.updatePositions = function() {
    var j, len, ref, sprite;
    ref = this.sprites;
    for (j = 0, len = ref.length; j < len; j++) {
      sprite = ref[j];
      sprite.updatePosition();
    }
  };

  ScrollFollower.prototype.getContentRect = function(el) {
    return getContentRect(el, this.contentOffset);
  };

  ScrollFollower.prototype.getBoundingRect = function(el) {
    return getOuterRect(el, this.contentOffset);
  };

  return ScrollFollower;

})();

ScrollFollowerSprite = (function() {
  ScrollFollowerSprite.prototype.follower = null;

  ScrollFollowerSprite.prototype.el = null;

  ScrollFollowerSprite.prototype.absoluteEl = null;

  ScrollFollowerSprite.prototype.naturalRect = null;

  ScrollFollowerSprite.prototype.parentRect = null;

  ScrollFollowerSprite.prototype.containerRect = null;

  ScrollFollowerSprite.prototype.isEnabled = true;

  ScrollFollowerSprite.prototype.isHFollowing = false;

  ScrollFollowerSprite.prototype.isVFollowing = false;

  ScrollFollowerSprite.prototype.doAbsolute = false;

  ScrollFollowerSprite.prototype.isAbsolute = false;

  ScrollFollowerSprite.prototype.isCentered = false;

  ScrollFollowerSprite.prototype.rect = null;

  ScrollFollowerSprite.prototype.isBlock = false;

  ScrollFollowerSprite.prototype.naturalWidth = null;

  function ScrollFollowerSprite(el1, follower1) {
    this.el = el1;
    this.follower = follower1 != null ? follower1 : null;
    this.isBlock = this.el.css('display') === 'block';
    this.el.css('position', 'relative');
  }

  ScrollFollowerSprite.prototype.disable = function() {
    if (this.isEnabled) {
      this.isEnabled = false;
      this.resetPosition();
      return this.unabsolutize();
    }
  };

  ScrollFollowerSprite.prototype.enable = function() {
    if (!this.isEnabled) {
      this.isEnabled = true;
      return this.assignPosition();
    }
  };

  ScrollFollowerSprite.prototype.clear = function() {
    this.disable();
    this.follower = null;
    return this.absoluteEl = null;
  };

  ScrollFollowerSprite.prototype.cacheDimensions = function() {
    var containerRect, follower, isCentered, isHFollowing, isVFollowing, minTravel, naturalRect, parentEl;
    isHFollowing = false;
    isVFollowing = false;
    isCentered = false;
    this.naturalWidth = this.el.width();
    this.resetPosition();
    follower = this.follower;
    naturalRect = this.naturalRect = follower.getBoundingRect(this.el);
    parentEl = this.el.parent();
    this.parentRect = follower.getBoundingRect(parentEl);
    containerRect = this.containerRect = joinRects(follower.getContentRect(parentEl), naturalRect);
    minTravel = follower.minTravel;
    if (follower.containOnNaturalLeft) {
      containerRect.left = naturalRect.left;
    }
    if (follower.containOnNaturalRight) {
      containerRect.right = naturalRect.right;
    }
    if (follower.isHFollowing) {
      if (getRectWidth(containerRect) - getRectWidth(naturalRect) >= minTravel) {
        isCentered = this.el.css('text-align') === 'center';
        isHFollowing = true;
      }
    }
    if (follower.isVFollowing) {
      if (getRectHeight(containerRect) - getRectHeight(naturalRect) >= minTravel) {
        isVFollowing = true;
      }
    }
    this.isHFollowing = isHFollowing;
    this.isVFollowing = isVFollowing;
    return this.isCentered = isCentered;
  };

  ScrollFollowerSprite.prototype.updatePosition = function() {
    this.computePosition();
    return this.assignPosition();
  };

  ScrollFollowerSprite.prototype.resetPosition = function() {
    return this.el.css({
      top: '',
      left: ''
    });
  };

  ScrollFollowerSprite.prototype.computePosition = function() {
    var containerRect, doAbsolute, parentRect, rect, rectWidth, subjectRect, viewportRect, visibleParentRect;
    viewportRect = this.follower.viewportRect;
    parentRect = this.parentRect;
    containerRect = this.containerRect;
    visibleParentRect = intersectRects(viewportRect, parentRect);
    rect = null;
    doAbsolute = false;
    if (visibleParentRect) {
      rect = copyRect(this.naturalRect);
      subjectRect = intersectRects(rect, parentRect);
      if ((this.isCentered && !testRectContains(viewportRect, parentRect)) || (subjectRect && !testRectContains(viewportRect, subjectRect))) {
        doAbsolute = true;
        if (this.isHFollowing) {
          if (this.isCentered) {
            rectWidth = getRectWidth(rect);
            rect.left = (visibleParentRect.left + visibleParentRect.right) / 2 - rectWidth / 2;
            rect.right = rect.left + rectWidth;
          } else {
            if (!hContainRect(rect, viewportRect)) {
              doAbsolute = false;
            }
          }
          if (hContainRect(rect, containerRect)) {
            doAbsolute = false;
          }
        }
        if (this.isVFollowing) {
          if (!vContainRect(rect, viewportRect)) {
            doAbsolute = false;
          }
          if (vContainRect(rect, containerRect)) {
            doAbsolute = false;
          }
        }
        if (!testRectContains(viewportRect, rect)) {
          doAbsolute = false;
        }
      }
    }
    this.rect = rect;
    return this.doAbsolute = doAbsolute;
  };

  ScrollFollowerSprite.prototype.assignPosition = function() {
    var left, top;
    if (this.isEnabled) {
      if (!this.rect) {
        return this.unabsolutize();
      } else if (this.doAbsolute && !this.follower.isForcedRelative) {
        this.absolutize();
        return this.absoluteEl.css({
          top: this.rect.top - this.follower.viewportRect.top + this.follower.scrollbarWidths.top,
          left: this.rect.left - this.follower.viewportRect.left + this.follower.scrollbarWidths.left,
          width: this.isBlock ? this.naturalWidth : ''
        });
      } else {
        top = this.rect.top - this.naturalRect.top;
        left = this.rect.left - this.naturalRect.left;
        this.unabsolutize();
        return this.el.toggleClass('fc-following', Boolean(top || left)).css({
          top: top,
          left: left
        });
      }
    }
  };

  ScrollFollowerSprite.prototype.absolutize = function() {
    if (!this.isAbsolute) {
      if (!this.absoluteEl) {
        this.absoluteEl = this.buildAbsoluteEl();
      }
      this.absoluteEl.appendTo(this.follower.scroller.el);
      this.el.css('visibility', 'hidden');
      return this.isAbsolute = true;
    }
  };

  ScrollFollowerSprite.prototype.unabsolutize = function() {
    if (this.isAbsolute) {
      this.absoluteEl.detach();
      this.el.css('visibility', '');
      return this.isAbsolute = false;
    }
  };

  ScrollFollowerSprite.prototype.buildAbsoluteEl = function() {
    var el;
    el = this.el.clone().addClass('fc-following');
    el.css({
      'position': 'absolute',
      'z-index': 1000,
      'font-weight': this.el.css('font-weight'),
      'font-size': this.el.css('font-size'),
      'font-family': this.el.css('font-family'),
      'text-decoration': this.el.css('text-decoration'),
      'color': this.el.css('color'),
      'padding-top': this.el.css('padding-top'),
      'padding-bottom': this.el.css('padding-bottom'),
      'padding-left': this.el.css('padding-left'),
      'padding-right': this.el.css('padding-right')
    });
    if (!this.follower.allowPointerEvents) {
      el.css('pointer-events', 'none');
    }
    return el;
  };

  return ScrollFollowerSprite;

})();

copyRect = function(rect) {
  return {
    left: rect.left,
    right: rect.right,
    top: rect.top,
    bottom: rect.bottom
  };
};

getRectWidth = function(rect) {
  return rect.right - rect.left;
};

getRectHeight = function(rect) {
  return rect.bottom - rect.top;
};

testRectContains = function(rect, innerRect) {
  return testRectHContains(rect, innerRect) && testRectVContains(rect, innerRect);
};

testRectHContains = function(rect, innerRect) {
  return innerRect.left >= rect.left && innerRect.right <= rect.right;
};

testRectVContains = function(rect, innerRect) {
  return innerRect.top >= rect.top && innerRect.bottom <= rect.bottom;
};

hContainRect = function(rect, outerRect) {
  if (rect.left < outerRect.left) {
    rect.right = outerRect.left + getRectWidth(rect);
    rect.left = outerRect.left;
    return true;
  } else if (rect.right > outerRect.right) {
    rect.left = outerRect.right - getRectWidth(rect);
    rect.right = outerRect.right;
    return true;
  } else {
    return false;
  }
};

vContainRect = function(rect, outerRect) {
  if (rect.top < outerRect.top) {
    rect.bottom = outerRect.top + getRectHeight(rect);
    rect.top = outerRect.top;
    return true;
  } else if (rect.bottom > outerRect.bottom) {
    rect.top = outerRect.bottom - getRectHeight(rect);
    rect.bottom = outerRect.bottom;
    return true;
  } else {
    return false;
  }
};

joinRects = function(rect1, rect2) {
  return {
    left: Math.min(rect1.left, rect2.left),
    right: Math.max(rect1.right, rect2.right),
    top: Math.min(rect1.top, rect2.top),
    bottom: Math.max(rect1.bottom, rect2.bottom)
  };
};

CalendarExtension = (function(superClass) {
  extend(CalendarExtension, superClass);

  function CalendarExtension() {
    return CalendarExtension.__super__.constructor.apply(this, arguments);
  }

  CalendarExtension.prototype.resourceManager = null;

  CalendarExtension.prototype.initialize = function() {
    return this.resourceManager = new ResourceManager(this);
  };

  CalendarExtension.prototype.instantiateView = function(viewType) {
    var spec, viewClass;
    spec = this.getViewSpec(viewType);
    viewClass = spec['class'];
    if (this.opt('resources') && spec.options.resources !== false) {
      if (spec.queryResourceClass) {
        viewClass = spec.queryResourceClass(spec) || viewClass;
      } else if (spec.resourceClass) {
        viewClass = spec.resourceClass;
      }
    }
    return new viewClass(this, spec);
  };

  CalendarExtension.prototype.getResources = function() {
    return Array.prototype.slice.call(this.resourceManager.topLevelResources);
  };

  CalendarExtension.prototype.addResource = function(resourceInput, scroll) {
    var promise;
    if (scroll == null) {
      scroll = false;
    }
    promise = this.resourceManager.addResource(resourceInput);
    if (scroll && this.view.scrollToResource) {
      promise.then((function(_this) {
        return function(resource) {
          return _this.view.scrollToResource(resource);
        };
      })(this));
    }
  };

  CalendarExtension.prototype.removeResource = function(idOrResource) {
    return this.resourceManager.removeResource(idOrResource);
  };

  CalendarExtension.prototype.refetchResources = function() {
    this.resourceManager.clear();
    this.view.flash('initialResources');
  };

  CalendarExtension.prototype.rerenderResources = function() {
    this.resourceManager.resetCurrentResources();
  };

  CalendarExtension.prototype.isFootprintAllowed = function(footprint, peerEventFootprints, constraintVal, overlapVal, subjectEventInstance) {
    var constrainToResourceIds, ref;
    if (typeof constraintVal === 'object') {
      constrainToResourceIds = Resource.extractIds(constraintVal, this);
      if (constrainToResourceIds.length && (!(footprint instanceof ResourceComponentFootprint) || !(ref = footprint.resourceId, indexOf.call(constrainToResourceIds, ref) >= 0))) {
        return false;
      }
    }
    return CalendarExtension.__super__.isFootprintAllowed.apply(this, arguments);
  };


  /*
  	TODO: somehow more DRY with ResourceGridMixin.eventRangeToEventFootprints
   */

  CalendarExtension.prototype.eventRangeToEventFootprints = function(eventRange) {
    var eventDef, j, len, resourceId, resourceIds, results;
    eventDef = eventRange.eventDef;
    resourceIds = eventDef.getResourceIds();
    if (resourceIds.length) {
      results = [];
      for (j = 0, len = resourceIds.length; j < len; j++) {
        resourceId = resourceIds[j];
        results.push(new EventFootprint(new ResourceComponentFootprint(eventRange.unzonedRange, eventDef.isAllDay(), resourceId), eventDef, eventRange.eventInstance));
      }
      return results;
    } else {
      return CalendarExtension.__super__.eventRangeToEventFootprints.apply(this, arguments);
    }
  };

  CalendarExtension.prototype.getPeerEventInstances = function(subjectEventDef) {
    var peerInstances, subjectResourceIds;
    subjectResourceIds = subjectEventDef.getResourceIds();
    peerInstances = CalendarExtension.__super__.getPeerEventInstances.apply(this, arguments);
    if (!subjectResourceIds.length) {
      return peerInstances;
    } else {
      return peerInstances.filter(function(peerInstance) {
        var j, len, resourceId;
        if (!peerInstance.def.resourceIds.length) {
          return true;
        }
        for (j = 0, len = subjectResourceIds.length; j < len; j++) {
          resourceId = subjectResourceIds[j];
          if (peerInstance.def.hasResourceId(resourceId)) {
            return true;
          }
        }
        return false;
      });
    }
  };

  CalendarExtension.prototype.parseFootprints = function(input) {
    var footprints, j, k, len, len1, plainFootprint, plainFootprints, resourceId, resourceIds;
    plainFootprints = CalendarExtension.__super__.parseFootprints.apply(this, arguments);
    resourceIds = input.resourceIds || [];
    if (input.resourceId) {
      resourceIds = [input.resourceId].concat(resourceIds);
    }
    if (resourceIds.length) {
      footprints = [];
      for (j = 0, len = resourceIds.length; j < len; j++) {
        resourceId = resourceIds[j];
        for (k = 0, len1 = plainFootprints.length; k < len1; k++) {
          plainFootprint = plainFootprints[k];
          footprints.push(new ResourceComponentFootprint(plainFootprint.unzonedRange, plainFootprint.isAllDay, resourceId));
        }
      }
      return footprints;
    } else {
      return plainFootprints;
    }
  };

  CalendarExtension.prototype.footprintContainsFootprint = function(outerFootprint, innerFootprint) {
    if (outerFootprint instanceof ResourceComponentFootprint && (!(innerFootprint instanceof ResourceComponentFootprint) || innerFootprint.resourceId !== outerFootprint.resourceId)) {
      return false;
    }
    return CalendarExtension.__super__.footprintContainsFootprint.apply(this, arguments);
  };

  CalendarExtension.prototype.footprintsIntersect = function(footprint0, footprint1) {
    if (footprint0 instanceof ResourceComponentFootprint && footprint1 instanceof ResourceComponentFootprint && footprint0.resourceId !== footprint1.resourceId) {
      return false;
    }
    return CalendarExtension.__super__.footprintsIntersect.apply(this, arguments);
  };

  CalendarExtension.prototype.buildCurrentBusinessFootprints = function(wholeDay) {
    var anyCustomBusinessHours, flatResources, footprints, j, k, l, len, len1, len2, plainFootprint, plainFootprints, resource;
    flatResources = this.resourceManager.getFlatResources();
    anyCustomBusinessHours = false;
    for (j = 0, len = flatResources.length; j < len; j++) {
      resource = flatResources[j];
      if (resource.businessHours) {
        anyCustomBusinessHours = true;
      }
    }
    if (anyCustomBusinessHours) {
      footprints = [];
      for (k = 0, len1 = flatResources.length; k < len1; k++) {
        resource = flatResources[k];
        plainFootprints = this._buildCurrentBusinessFootprints(wholeDay, resource.businessHours || this.opt('businessHours'));
        for (l = 0, len2 = plainFootprints.length; l < len2; l++) {
          plainFootprint = plainFootprints[l];
          footprints.push(new ResourceComponentFootprint(plainFootprint.unzonedRange, plainFootprint.isAllDay, resource.id));
        }
      }
      return footprints;
    } else {
      return CalendarExtension.__super__.buildCurrentBusinessFootprints.call(this, wholeDay);
    }
  };

  CalendarExtension.prototype.buildSelectFootprint = function(zonedStartInput, zonedEndInput, resourceId) {
    var plainFootprint;
    plainFootprint = CalendarExtension.__super__.buildSelectFootprint.apply(this, arguments);
    if (resourceId) {
      return new ResourceComponentFootprint(plainFootprint.unzonedRange, plainFootprint.isAllDay, resourceId);
    } else {
      return plainFootprint;
    }
  };

  CalendarExtension.prototype.getResourceById = function(id) {
    return this.resourceManager.getResourceById(id);
  };

  CalendarExtension.prototype.getEventResourceId = function(event) {
    return this.getEventResourceIds(event)[0];
  };

  CalendarExtension.prototype.getEventResourceIds = function(event) {
    var eventDef;
    eventDef = this.eventManager.getEventDefByUid(event._id);
    if (eventDef) {
      return eventDef.getResourceIds();
    } else {
      return [];
    }
  };

  CalendarExtension.prototype.setEventResourceId = function(event, resourceId) {
    return this.setEventResourceIds(event, resourceId ? [resourceId] : []);
  };

  CalendarExtension.prototype.setEventResourceIds = function(event, resourceIds) {
    var eventDef, rawResourceId;
    eventDef = this.eventManager.getEventDefByUid(event._id);
    if (eventDef) {
      return eventDef.resourceIds = (function() {
        var j, len, results;
        results = [];
        for (j = 0, len = resourceIds.length; j < len; j++) {
          rawResourceId = resourceIds[j];
          results.push(Resource.normalizeId(rawResourceId));
        }
        return results;
      })();
    }
  };

  CalendarExtension.prototype.getResourceEvents = function(idOrResource) {
    var resource;
    resource = typeof idOrResource === 'object' ? idOrResource : this.getResourceById(idOrResource);
    if (resource) {
      return this.clientEvents((function(_this) {
        return function(event) {
          return $.inArray(resource.id, _this.getEventResourceIds(event)) !== -1;
        };
      })(this));
    } else {
      return [];
    }
  };

  CalendarExtension.prototype.getEventResource = function(idOrEvent) {
    return this.getEventResources(idOrEvent)[0];
  };

  CalendarExtension.prototype.getEventResources = function(idOrEvent) {
    var event, j, len, ref, resource, resourceId, resources;
    event = typeof idOrEvent === 'object' ? idOrEvent : this.clientEvents(idOrEvent)[0];
    resources = [];
    if (event) {
      ref = this.getEventResourceIds(event);
      for (j = 0, len = ref.length; j < len; j++) {
        resourceId = ref[j];
        resource = this.getResourceById(resourceId);
        if (resource) {
          resources.push(resource);
        }
      }
    }
    return resources;
  };

  return CalendarExtension;

})(Calendar);

Calendar.prototype = CalendarExtension.prototype;

origSetElement = View.prototype.setElement;

origRemoveElement = View.prototype.removeElement;

origOnBaseRender = View.prototype.onBaseRender;

Calendar.defaults.refetchResourcesOnNavigate = false;

View.prototype.canHandleSpecificResources = false;

View.prototype.isDestroying = false;

View.prototype.setElement = function() {
  origSetElement.apply(this, arguments);
  return this.watchResources();
};

View.prototype.removeElement = function() {
  this.isDestroying = true;
  this.unwatchResources();
  origRemoveElement.apply(this, arguments);
  return this.isDestroying = false;
};

View.prototype.onBaseRender = function() {
  processLicenseKey(this.calendar.opt('schedulerLicenseKey'), this.el);
  return origOnBaseRender.apply(this, arguments);
};

View.prototype.watchResources = function() {
  var bindingDepNames, initialDepNames;
  initialDepNames = [];
  bindingDepNames = ['initialResources'];
  if (this.opt('refetchResourcesOnNavigate')) {
    initialDepNames.push('dateProfile');
  }
  if (this.opt('filterResourcesWithEvents')) {
    bindingDepNames.push('currentEvents');
  }
  this.watch('initialResources', initialDepNames, (function(_this) {
    return function(deps) {
      return _this.getInitialResources(deps.dateProfile);
    };
  })(this));
  return this.watch('bindingResources', bindingDepNames, (function(_this) {
    return function(deps) {
      _this.bindResourceChanges(deps.currentEvents);
      _this.setResources(deps.initialResources, deps.currentEvents);
    };
  })(this), (function(_this) {
    return function() {
      _this.unbindResourceChanges();
      _this.unsetResources();
    };
  })(this));
};

View.prototype.unwatchResources = function() {
  this.unwatch('initialResources');
  return this.unwatch('bindingResources');
};

View.prototype.getInitialResources = function(dateProfile) {
  var calendar;
  calendar = this.calendar;
  if (dateProfile) {
    return calendar.resourceManager.getResources(calendar.msToMoment(dateProfile.activeUnzonedRange.startMs, dateProfile.isRangeAllDay), calendar.msToMoment(dateProfile.activeUnzonedRange.endMs, dateProfile.isRangeAllDay));
  } else {
    return calendar.resourceManager.getResources();
  }
};

View.prototype.bindResourceChanges = function(eventsPayload) {
  return this.listenTo(this.calendar.resourceManager, {
    set: (function(_this) {
      return function(resources) {
        return _this.setResources(resources, eventsPayload);
      };
    })(this),
    unset: (function(_this) {
      return function() {
        return _this.unsetResources();
      };
    })(this),
    reset: (function(_this) {
      return function(resources) {
        return _this.resetResources(resources, eventsPayload);
      };
    })(this),
    add: (function(_this) {
      return function(resource, allResources) {
        return _this.addResource(resource, allResources, eventsPayload);
      };
    })(this),
    remove: (function(_this) {
      return function(resource, allResources) {
        return _this.removeResource(resource, allResources, eventsPayload);
      };
    })(this)
  });
};

View.prototype.unbindResourceChanges = function() {
  return this.stopListeningTo(this.calendar.resourceManager);
};

View.watch('displayingEvents', ['displayingDates', 'hasEvents', 'currentResources'], function(deps) {
  return this.requestEventsRender(this.get('currentEvents'));
}, function() {
  return this.requestEventsUnrender();
});

View.prototype.setResources = function(resources, eventsPayload) {
  if (eventsPayload) {
    resources = this.filterResourcesWithEvents(resources, eventsPayload);
  }
  this.set('currentResources', resources);
  this.set('hasResources', true);
  return this.handleResourcesSet(resources);
};

View.prototype.unsetResources = function() {
  this.unset('currentResources');
  this.unset('hasResources');
  return this.handleResourcesUnset();
};

View.prototype.resetResources = function(resources, eventsPayload) {
  this.startBatchRender();
  this.unsetResources();
  this.setResources(resources, eventsPayload);
  return this.stopBatchRender();
};

View.prototype.addResource = function(resource, allResources, eventsPayload) {
  var a;
  if (!this.canHandleSpecificResources) {
    return this.resetResources(allResources, eventsPayload);
  }
  if (eventsPayload) {
    a = this.filterResourcesWithEvents([resource], eventsPayload);
    if (!a.length) {
      resource = null;
    }
  }
  if (resource) {
    this.set('currentResources', allResources);
    return this.handleResourceAdd(resource);
  }
};

View.prototype.removeResource = function(resource, allResources, eventsPayload) {
  if (!this.canHandleSpecificResources) {
    return this.resetResources(allResources, eventsPayload);
  }
  this.set('currentResources', allResources);
  return this.handleResourceRemove(resource);
};

View.prototype.handleResourcesSet = function(resources) {};

View.prototype.handleResourcesUnset = function(resources) {};

View.prototype.handleResourceAdd = function(resource) {};

View.prototype.handleResourceRemove = function(resource) {};

View.prototype.filterResourcesWithEvents = function(resources, eventsPayload) {
  var eventInstance, eventInstanceGroup, id, j, k, len, len1, ref, ref1, resourceId, resourceIdHits;
  resourceIdHits = {};
  for (id in eventsPayload) {
    eventInstanceGroup = eventsPayload[id];
    ref = eventInstanceGroup.eventInstances;
    for (j = 0, len = ref.length; j < len; j++) {
      eventInstance = ref[j];
      ref1 = eventInstance.def.getResourceIds();
      for (k = 0, len1 = ref1.length; k < len1; k++) {
        resourceId = ref1[k];
        resourceIdHits[resourceId] = true;
      }
    }
  }
  return _filterResourcesWithEvents(resources, resourceIdHits);
};

_filterResourcesWithEvents = function(sourceResources, resourceIdHits) {
  var filteredChildren, filteredResource, filteredResources, j, len, sourceResource;
  filteredResources = [];
  for (j = 0, len = sourceResources.length; j < len; j++) {
    sourceResource = sourceResources[j];
    if (sourceResource.children.length) {
      filteredChildren = _filterResourcesWithEvents(sourceResource.children, resourceIdHits);
      if (filteredChildren.length || resourceIdHits[sourceResource.id]) {
        filteredResource = Object.create(sourceResource);
        filteredResource.children = filteredChildren;
        filteredResources.push(filteredResource);
      }
    } else {
      if (resourceIdHits[sourceResource.id]) {
        filteredResources.push(sourceResource);
      }
    }
  }
  return filteredResources;
};

origGetSegCustomClasses = Grid.prototype.getSegCustomClasses;

origGetSegDefaultBackgroundColor = Grid.prototype.getSegDefaultBackgroundColor;

origGetSegDefaultBorderColor = Grid.prototype.getSegDefaultBorderColor;

origGetSegDefaultTextColor = Grid.prototype.getSegDefaultTextColor;

Grid.prototype.getSegCustomClasses = function(seg) {
  var classes, j, len, ref, resource;
  classes = origGetSegCustomClasses.apply(this, arguments);
  ref = this.getSegResources(seg);
  for (j = 0, len = ref.length; j < len; j++) {
    resource = ref[j];
    classes = classes.concat(resource.eventClassName || []);
  }
  return classes;
};

Grid.prototype.getSegDefaultBackgroundColor = function(seg) {
  var currentResource, j, len, resources, val;
  resources = this.getSegResources(seg);
  for (j = 0, len = resources.length; j < len; j++) {
    currentResource = resources[j];
    while (currentResource) {
      val = currentResource.eventBackgroundColor || currentResource.eventColor;
      if (val) {
        return val;
      }
      currentResource = currentResource._parent;
    }
  }
  return origGetSegDefaultBackgroundColor.apply(this, arguments);
};

Grid.prototype.getSegDefaultBorderColor = function(seg) {
  var currentResource, j, len, resources, val;
  resources = this.getSegResources(seg);
  for (j = 0, len = resources.length; j < len; j++) {
    currentResource = resources[j];
    while (currentResource) {
      val = currentResource.eventBorderColor || currentResource.eventColor;
      if (val) {
        return val;
      }
      currentResource = currentResource._parent;
    }
  }
  return origGetSegDefaultBorderColor.apply(this, arguments);
};

Grid.prototype.getSegDefaultTextColor = function(seg) {
  var currentResource, j, len, resources, val;
  resources = this.getSegResources(seg);
  for (j = 0, len = resources.length; j < len; j++) {
    currentResource = resources[j];
    while (currentResource) {
      val = currentResource.eventTextColor;
      if (val) {
        return val;
      }
      currentResource = currentResource._parent;
    }
  }
  return origGetSegDefaultTextColor.apply(this, arguments);
};

Grid.prototype.getSegResources = function(seg) {
  if (seg.resource) {
    return [seg.resource];
  } else {
    return this.view.calendar.getEventResources(seg.footprint.getEventLegacy());
  }
};

origEventDefClone = EventDef.prototype.clone;

origEventDefToLegacy = EventDef.prototype.toLegacy;

origApplyOtherRawProps = EventDef.prototype.applyOtherRawProps;

EventDef.prototype.standardPropMap.resourceEditable = true;


/*
NOTE: will always be populated by applyOtherRawProps
 */

EventDef.prototype.resourceIds = null;

EventDef.prototype.resourceEditable = null;


/*
resourceId should already be normalized
 */

EventDef.prototype.hasResourceId = function(resourceId) {
  return indexOf.call(this.resourceIds, resourceId) >= 0;
};


/*
resourceId should already be normalized
 */

EventDef.prototype.removeResourceId = function(resourceId) {
  return removeExact(this.resourceIds, resourceId);
};


/*
resourceId should already be normalized
 */

EventDef.prototype.addResourceId = function(resourceId) {
  if (!this.hasResourceId(resourceId)) {
    return this.resourceIds.push(resourceId);
  }
};

EventDef.prototype.getResourceIds = function() {
  if (this.resourceIds) {
    return this.resourceIds.slice();
  } else {
    return [];
  }
};

EventDef.prototype.clone = function() {
  var def;
  def = origEventDefClone.apply(this, arguments);
  def.resourceIds = this.getResourceIds();
  return def;
};

EventDef.prototype.toLegacy = function() {
  var obj, resourceIds;
  obj = origEventDefToLegacy.apply(this, arguments);
  resourceIds = this.getResourceIds();
  obj.resourceId = resourceIds.length === 1 ? resourceIds[0] : null;
  obj.resourceIds = resourceIds.length > 1 ? resourceIds : null;
  if (this.resourceEditable != null) {
    obj.resourceEditable = this.resourceEditable;
  }
  return obj;
};

EventDef.prototype.applyOtherRawProps = function(rawProps) {
  rawProps = $.extend({}, rawProps);
  this.resourceIds = Resource.extractIds(rawProps, this.source.calendar);
  delete rawProps.resourceId;
  delete rawProps.resourceIds;
  return origApplyOtherRawProps.apply(this, arguments);
};

EventSource.prototype.standardPropMap.resourceEditable = true;

oldMutateSingle = EventDefMutation.prototype.mutateSingle;

EventDefMutation.prototype.oldResourceId = null;

EventDefMutation.prototype.newResourceId = null;

EventDefMutation.prototype.mutateSingle = function(eventDef) {
  var savedResourceIds, undo;
  undo = oldMutateSingle.apply(this, arguments);
  savedResourceIds = null;
  if (this.oldResourceId && eventDef.hasResourceId(this.oldResourceId)) {
    savedResourceIds = eventDef.getResourceIds();
    eventDef.removeResourceId(this.oldResourceId);
    eventDef.addResourceId(this.newResourceId);
  }
  return (function(_this) {
    return function() {
      undo();
      if (savedResourceIds) {
        return eventDef.resourceIds = savedResourceIds;
      }
    };
  })(this);
};

Resource = (function() {
  function Resource() {}

  return Resource;

})();

Resource.extractIds = function(rawProps, calendar) {
  var j, len, rawResourceId, ref, resourceField, resourceIds;
  resourceField = calendar.opt('eventResourceField') || 'resourceId';
  resourceIds = [];
  if (rawProps.resourceIds) {
    ref = rawProps.resourceIds;
    for (j = 0, len = ref.length; j < len; j++) {
      rawResourceId = ref[j];
      resourceIds.push(Resource.normalizeId(rawResourceId));
    }
  }
  if (rawProps[resourceField] != null) {
    resourceIds.push(Resource.normalizeId(rawProps[resourceField]));
  }
  return resourceIds;
};

Resource.normalizeId = function(rawId) {
  return String(rawId);
};

ResourceManager = (function(superClass) {
  extend(ResourceManager, superClass);

  ResourceManager.mixin(EmitterMixin);

  ResourceManager.resourceGuid = 1;

  ResourceManager.ajaxDefaults = {
    dataType: 'json',
    cache: false
  };

  ResourceManager.prototype.calendar = null;

  ResourceManager.prototype.fetchId = 0;

  ResourceManager.prototype.topLevelResources = null;

  ResourceManager.prototype.resourcesById = null;

  ResourceManager.prototype.fetching = null;

  ResourceManager.prototype.currentStart = null;

  ResourceManager.prototype.currentEnd = null;

  function ResourceManager(calendar1) {
    this.calendar = calendar1;
    this.initializeCache();
  }


  /*
  	Like fetchResources, but won't refetch if already fetched.
   */

  ResourceManager.prototype.getResources = function(start, end) {
    var isSameRange;
    isSameRange = (!start && !this.currentStart) || (start && this.currentStart && start.isSame(this.currentStart) && end.isSame(this.currentEnd));
    if (!this.fetching || !isSameRange) {
      return this.fetchResources(start, end);
    } else {
      return this.fetching;
    }
  };


  /*
  	Will always fetch, even if done previously.
  	Accepts optional chrono-related params to pass on to the raw resource sources.
  	Returns a promise.
   */

  ResourceManager.prototype.fetchResources = function(start, end) {
    var currentFetchId;
    currentFetchId = (this.fetchId += 1);
    return this.fetching = Promise.construct((function(_this) {
      return function(resolve, reject) {
        return _this.fetchResourceInputs(function(resourceInputs) {
          if (currentFetchId === _this.fetchId) {
            _this.setResources(resourceInputs);
            return resolve(_this.topLevelResources);
          } else {
            return reject();
          }
        }, start, end);
      };
    })(this));
  };


  /*
  	Accepts optional chrono-related params to pass on to the raw resource sources.
  	Calls callback when done.
   */

  ResourceManager.prototype.fetchResourceInputs = function(callback, start, end) {
    var calendar, requestParams, source, timezone;
    calendar = this.calendar;
    source = calendar.opt('resources');
    timezone = calendar.opt('timezone');
    if ($.type(source) === 'string') {
      source = {
        url: source
      };
    }
    switch ($.type(source)) {
      case 'function':
        this.calendar.pushLoading();
        return source((function(_this) {
          return function(resourceInputs) {
            _this.calendar.popLoading();
            return callback(resourceInputs);
          };
        })(this), start, end, calendar.opt('timezone'));
      case 'object':
        calendar.pushLoading();
        requestParams = {};
        if (start && end) {
          requestParams[calendar.opt('startParam')] = start.format();
          requestParams[calendar.opt('endParam')] = end.format();
          if (timezone && timezone !== 'local') {
            requestParams[calendar.opt('timezoneParam')] = timezone;
          }
        }
        return $.ajax($.extend({
          data: requestParams
        }, ResourceManager.ajaxDefaults, source)).then((function(_this) {
          return function(resourceInputs) {
            calendar.popLoading();
            return callback(resourceInputs);
          };
        })(this));
      case 'array':
        return callback(source);
      default:
        return callback([]);
    }
  };

  ResourceManager.prototype.getResourceById = function(id) {
    return this.resourcesById[id];
  };

  ResourceManager.prototype.getFlatResources = function() {
    var id, results;
    results = [];
    for (id in this.resourcesById) {
      results.push(this.resourcesById[id]);
    }
    return results;
  };

  ResourceManager.prototype.initializeCache = function() {
    this.topLevelResources = [];
    return this.resourcesById = {};
  };

  ResourceManager.prototype.setResources = function(resourceInputs) {
    var j, len, resource, resourceInput, resources, validResources, wasSet;
    wasSet = Boolean(this.topLevelResources);
    this.initializeCache();
    resources = (function() {
      var j, len, results;
      results = [];
      for (j = 0, len = resourceInputs.length; j < len; j++) {
        resourceInput = resourceInputs[j];
        results.push(this.buildResource(resourceInput));
      }
      return results;
    }).call(this);
    validResources = (function() {
      var j, len, results;
      results = [];
      for (j = 0, len = resources.length; j < len; j++) {
        resource = resources[j];
        if (this.addResourceToIndex(resource)) {
          results.push(resource);
        }
      }
      return results;
    }).call(this);
    for (j = 0, len = validResources.length; j < len; j++) {
      resource = validResources[j];
      this.addResourceToTree(resource);
    }
    if (wasSet) {
      this.trigger('reset', this.topLevelResources);
    } else {
      this.trigger('set', this.topLevelResources);
    }
    return this.calendar.publiclyTrigger('resourcesSet', [this.topLevelResources]);
  };

  ResourceManager.prototype.resetCurrentResources = function() {
    if (this.topLevelResources) {
      return this.trigger('reset', this.topLevelResources);
    }
  };

  ResourceManager.prototype.clear = function() {
    this.topLevelResources = null;
    return this.fetching = null;
  };

  ResourceManager.prototype.addResource = function(resourceInput) {
    if (this.fetching) {
      return this.fetching.then((function(_this) {
        return function() {
          var resource;
          resource = _this.buildResource(resourceInput);
          if (_this.addResourceToIndex(resource)) {
            _this.addResourceToTree(resource);
            _this.trigger('add', resource, _this.topLevelResources);
            return resource;
          } else {
            return false;
          }
        };
      })(this));
    } else {
      return Promise.reject();
    }
  };

  ResourceManager.prototype.addResourceToIndex = function(resource) {
    var child, j, len, ref;
    if (this.resourcesById[resource.id]) {
      return false;
    } else {
      this.resourcesById[resource.id] = resource;
      ref = resource.children;
      for (j = 0, len = ref.length; j < len; j++) {
        child = ref[j];
        this.addResourceToIndex(child);
      }
      return true;
    }
  };

  ResourceManager.prototype.addResourceToTree = function(resource) {
    var parent, parentId, ref, siblings;
    if (!resource.parent) {
      parentId = String((ref = resource['parentId']) != null ? ref : '');
      if (parentId) {
        parent = this.resourcesById[parentId];
        if (parent) {
          resource.parent = parent;
          siblings = parent.children;
        } else {
          return false;
        }
      } else {
        siblings = this.topLevelResources;
      }
      siblings.push(resource);
    }
    return true;
  };

  ResourceManager.prototype.removeResource = function(idOrResource) {
    var id;
    id = typeof idOrResource === 'object' ? idOrResource.id : idOrResource;
    if (this.fetching) {
      return this.fetching.then((function(_this) {
        return function() {
          var resource;
          resource = _this.removeResourceFromIndex(id);
          if (resource) {
            _this.removeResourceFromTree(resource);
            _this.trigger('remove', resource, _this.topLevelResources);
          }
          return resource;
        };
      })(this));
    } else {
      return Promise.reject();
    }
  };

  ResourceManager.prototype.removeResourceFromIndex = function(resourceId) {
    var child, j, len, ref, resource;
    resource = this.resourcesById[resourceId];
    if (resource) {
      delete this.resourcesById[resourceId];
      ref = resource.children;
      for (j = 0, len = ref.length; j < len; j++) {
        child = ref[j];
        this.removeResourceFromIndex(child.id);
      }
      return resource;
    } else {
      return false;
    }
  };

  ResourceManager.prototype.removeResourceFromTree = function(resource, siblings) {
    var i, j, len, sibling;
    if (siblings == null) {
      siblings = this.topLevelResources;
    }
    for (i = j = 0, len = siblings.length; j < len; i = ++j) {
      sibling = siblings[i];
      if (sibling === resource) {
        resource.parent = null;
        siblings.splice(i, 1);
        return true;
      }
      if (this.removeResourceFromTree(resource, sibling.children)) {
        return true;
      }
    }
    return false;
  };

  ResourceManager.prototype.buildResource = function(resourceInput) {
    var child, childInput, rawClassName, ref, resource;
    resource = $.extend({}, resourceInput);
    resource.id = String((ref = resourceInput.id) != null ? ref : '_fc' + ResourceManager.resourceGuid++);
    rawClassName = resourceInput.eventClassName;
    resource.eventClassName = (function() {
      switch ($.type(rawClassName)) {
        case 'string':
          return rawClassName.split(/\s+/);
        case 'array':
          return rawClassName;
        default:
          return [];
      }
    })();
    resource.children = (function() {
      var j, len, ref1, ref2, results;
      ref2 = (ref1 = resourceInput.children) != null ? ref1 : [];
      results = [];
      for (j = 0, len = ref2.length; j < len; j++) {
        childInput = ref2[j];
        child = this.buildResource(childInput);
        child.parent = resource;
        results.push(child);
      }
      return results;
    }).call(this);
    return resource;
  };

  return ResourceManager;

})(Class);

Calendar.defaults.filterResourcesWithEvents = false;


/*
A view that structurally distinguishes events by resource
 */

ResourceViewMixin = {
  isResourcesRendered: false,
  resourceTextFunc: null,
  setElement: function() {
    View.prototype.setElement.apply(this, arguments);
    this.watch('displayingResources', ['hasResources'], (function(_this) {
      return function() {
        return _this.requestResourcesRender(_this.get('currentResources'));
      };
    })(this), (function(_this) {
      return function() {
        return _this.requestResourcesUnrender();
      };
    })(this));
    return this.watch('displayingEvents', ['displayingDates', 'hasEvents', 'displayingResources'], (function(_this) {
      return function() {
        return _this.requestEventsRender(_this.get('currentEvents'));
      };
    })(this), (function(_this) {
      return function() {
        return _this.requestEventsUnrender();
      };
    })(this));
  },
  queryScroll: function() {
    var scroll;
    scroll = View.prototype.queryScroll.apply(this, arguments);
    if (this.isResourcesRendered) {
      $.extend(scroll, this.queryResourceScroll());
    }
    return scroll;
  },
  applyScroll: function(scroll) {
    View.prototype.applyScroll.apply(this, arguments);
    if (this.isResourcesRendered) {
      return this.applyResourceScroll(scroll);
    }
  },
  queryResourceScroll: function() {
    return {};
  },
  applyResourceScroll: function() {},
  bindBaseRenderHandlers: function() {
    var isDatesRendered, isResourcesRendered;
    isResourcesRendered = false;
    isDatesRendered = false;
    this.on('resourcesRendered.baseHandler', function() {
      if (!isResourcesRendered) {
        isResourcesRendered = true;
        if (isDatesRendered) {
          return this.onBaseRender();
        }
      }
    });
    this.on('datesRendered.baseHandler', function() {
      if (!isDatesRendered) {
        isDatesRendered = true;
        if (isResourcesRendered) {
          return this.onBaseRender();
        }
      }
    });
    this.on('before:resourcesUnrendered.baseHandler', function() {
      if (isResourcesRendered) {
        return isResourcesRendered = false;
      }
    });
    return this.on('before:datesUnrendered.baseHandler', function() {
      if (isDatesRendered) {
        isDatesRendered = false;
        return this.onBeforeBaseUnrender();
      }
    });
  },
  handleResourcesSet: function(resources) {},
  handleResourcesUnset: function() {},
  handleResourceAdd: function(resource) {
    return this.requestResourceRender(resource);
  },
  handleResourceRemove: function(resource) {
    return this.requestResourceUnrender(resource);
  },
  requestResourcesRender: function(resources) {
    return this.renderQueue.queue((function(_this) {
      return function() {
        return _this.executeResourcesRender(resources);
      };
    })(this), 'resource', 'init');
  },
  requestResourcesUnrender: function() {
    return this.renderQueue.queue((function(_this) {
      return function() {
        return _this.executeResourcesUnrender();
      };
    })(this), 'resource', 'destroy');
  },
  requestResourceRender: function(resource) {
    return this.renderQueue.queue((function(_this) {
      return function() {
        return _this.executeResourceRender(resource);
      };
    })(this), 'resource', 'add');
  },
  requestResourceUnrender: function(resource) {
    return this.renderQueue.queue((function(_this) {
      return function() {
        return _this.executeResourceUnrender(resource);
      };
    })(this), 'resource', 'remove');
  },
  executeResourcesRender: function(resources) {
    this.renderResources(resources);
    this.isResourcesRendered = true;
    return this.trigger('resourcesRendered');
  },
  executeResourcesUnrender: function() {
    this.trigger('before:resourcesUnrendered');
    this.unrenderResources();
    return this.isResourcesRendered = false;
  },
  executeResourceRender: function(resource) {
    return this.renderResource(resource);
  },
  executeResourceUnrender: function(resource) {
    return this.unrenderResource(resource);
  },
  renderResources: function(resources) {},
  unrenderResources: function() {},
  renderResource: function(resource) {},
  unrenderResource: function(resource) {},
  isEventDefDraggable: function(eventDef) {
    return this.isEventDefResourceEditable(eventDef) || View.prototype.isEventDefDraggable.call(this, eventDef);
  },
  isEventDefResourceEditable: function(eventDef) {
    var ref, ref1, ref2;
    return (ref = (ref1 = (ref2 = eventDef.resourceEditable) != null ? ref2 : (eventDef.source || {}).resourceEditable) != null ? ref1 : this.opt('eventResourceEditable')) != null ? ref : this.isEventDefGenerallyEditable(eventDef);
  },
  getResourceText: function(resource) {
    return this.getResourceTextFunc()(resource);
  },
  getResourceTextFunc: function() {
    var func;
    if (this.resourceTextFunc) {
      return this.resourceTextFunc;
    } else {
      func = this.opt('resourceText');
      if (typeof func !== 'function') {
        func = function(resource) {
          return resource.title || resource.id;
        };
      }
      return this.resourceTextFunc = func;
    }
  },

  /*
  	footprint is a ResourceComponentFootprint
   */
  triggerDayClick: function(footprint, dayEl, ev) {
    var dateProfile;
    dateProfile = this.calendar.footprintToDateProfile(footprint);
    return this.publiclyTrigger('dayClick', {
      context: dayEl,
      args: [dateProfile.start, ev, this, this.calendar.resourceManager.getResourceById(footprint.resourceId)]
    });
  },

  /*
  	footprint is a ResourceComponentFootprint
   */
  triggerSelect: function(footprint, ev) {
    var dateProfile;
    dateProfile = this.calendar.footprintToDateProfile(footprint);
    return this.publiclyTrigger('select', {
      context: this,
      args: [dateProfile.start, dateProfile.end, ev, this, this.calendar.resourceManager.getResourceById(footprint.resourceId)]
    });
  },
  triggerExternalDrop: function(singleEventDef, isEvent, el, ev, ui) {
    this.publiclyTrigger('drop', {
      context: el[0],
      args: [singleEventDef.dateProfile.start.clone(), ev, ui, singleEventDef.getResourceIds()[0], this]
    });
    if (isEvent) {
      return this.publiclyTrigger('eventReceive', {
        context: this,
        args: [singleEventDef.buildInstance().toLegacy(), this]
      });
    }
  }
};


/*
For vertical resource view.
For views that rely on grids that render their resources and dates in the same pass.
 */

VertResourceViewMixin = $.extend({}, ResourceViewMixin, {
  setElement: function() {
    var isDisplayingBoth, isDisplayingDatesOnly, needsScroll;
    ResourceViewMixin.setElement.apply(this, arguments);
    isDisplayingDatesOnly = false;
    isDisplayingBoth = false;
    needsScroll = false;
    this.watch('dateProfileOnly', ['dateProfile'], (function(_this) {
      return function() {
        return needsScroll = true;
      };
    })(this));
    this.watch('displayingDatesOnly', ['dateProfile', '?currentResources'], (function(_this) {
      return function(deps) {
        if (!deps.currentResources && !_this.isDestroying) {
          isDisplayingDatesOnly = true;
          return _this.renderQueue.queue(function() {
            _this.executeDateRender(deps.dateProfile, !needsScroll);
            return needsScroll = false;
          }, 'date', 'init');
        }
      };
    })(this), (function(_this) {
      return function() {
        if (isDisplayingDatesOnly) {
          isDisplayingDatesOnly = false;
          return _this.renderQueue.queue(function() {
            return _this.executeDateUnrender();
          }, 'date', 'destroy');
        }
      };
    })(this));
    this.watch('displayingDates', ['dateProfile', 'currentResources'], (function(_this) {
      return function(deps) {
        if (!_this.isDestroying) {
          isDisplayingBoth = true;
          return _this.renderQueue.queue(function() {
            _this.setResourcesOnGrids(deps.currentResources);
            _this.executeDateRender(deps.dateProfile, !needsScroll);
            needsScroll = false;
            return _this.trigger('resourcesRendered');
          }, 'resource', 'init');
        }
      };
    })(this), (function(_this) {
      return function() {
        if (isDisplayingBoth) {
          isDisplayingBoth = false;
          return _this.renderQueue.queue(function() {
            _this.trigger('before:resourcesUnrendered');
            _this.unsetResourcesOnGrids();
            return _this.executeDateUnrender();
          }, 'resource', 'destroy');
        }
      };
    })(this));
    return this.watch('displayingResources', ['displayingDates'], (function(_this) {
      return function() {
        return true;
      };
    })(this));
  },
  setResourcesOnGrids: function(resources) {},
  unsetResourcesOnGrids: function() {}
});

ResourceGridMixin = {
  allowCrossResource: true,

  /*
  	TODO: somehow more DRY with Calendar::eventRangeToEventFootprints
  	(However, this DOES have slightly different logic, for rendering).
   */
  eventRangeToEventFootprints: function(eventRange) {
    var eventDef, j, len, resourceId, resourceIds, results;
    eventDef = eventRange.eventDef;
    resourceIds = eventDef.getResourceIds();
    if (resourceIds.length) {
      results = [];
      for (j = 0, len = resourceIds.length; j < len; j++) {
        resourceId = resourceIds[j];
        results.push(new EventFootprint(new ResourceComponentFootprint(eventRange.unzonedRange, eventDef.isAllDay(), resourceId), eventDef, eventRange.eventInstance));
      }
      return results;
    } else if (eventDef.hasBgRendering()) {
      return Grid.prototype.eventRangeToEventFootprints.apply(this, arguments);
    } else {
      return [];
    }
  },
  computeEventDropMutation: function(startFootprint, endFootprint, eventDef) {
    var mutation;
    if (this.view.isEventDefStartEditable(eventDef)) {
      mutation = Grid.prototype.computeEventDropMutation.apply(this, arguments);
    } else {
      mutation = new EventDefMutation();
    }
    if (this.view.isEventDefResourceEditable(eventDef) && startFootprint.resourceId !== endFootprint.resourceId) {
      mutation.oldResourceId = startFootprint.resourceId;
      mutation.newResourceId = endFootprint.resourceId;
    }
    return mutation;
  },
  computeExternalDrop: function(resourceComponentFootprint, meta) {
    var eventDef;
    eventDef = Grid.prototype.computeExternalDrop.apply(this, arguments);
    eventDef.addResourceId(resourceComponentFootprint.resourceId);
    return eventDef;
  },
  computeEventStartResizeMutation: function(startFootprint, endFootprint, eventDef) {
    if (!this.allowCrossResource && startFootprint.resourceId !== endFootprint.resourceId) {
      return;
    }
    return Grid.prototype.computeEventStartResizeMutation.apply(this, arguments);
  },
  computeEventEndResizeMutation: function(startFootprint, endFootprint, eventDef) {
    if (!this.allowCrossResource && startFootprint.resourceId !== endFootprint.resourceId) {
      return;
    }
    return Grid.prototype.computeEventEndResizeMutation.apply(this, arguments);
  },
  computeSelectionFootprint: function(startFootprint, endFootprint) {
    var plainFootprint;
    if (!this.allowCrossResource && startFootprint.resourceId !== endFootprint.resourceId) {
      return;
    }
    plainFootprint = Grid.prototype.computeSelectionFootprint.apply(this, arguments);
    return new ResourceComponentFootprint(plainFootprint.unzonedRange, plainFootprint.isAllDay, startFootprint.resourceId);
  }
};


/*
Requirements:
- must be a Grid
- grid must have a view that's a ResourceView
- DayTableMixin must already be mixed in
 */

ResourceDayTableMixin = {
  flattenedResources: null,
  resourceCnt: 0,
  datesAboveResources: false,
  allowCrossResource: false,
  setResources: function(resources) {
    this.flattenedResources = this.flattenResources(resources);
    this.resourceCnt = this.flattenedResources.length;
    return this.updateDayTableCols();
  },
  unsetResources: function() {
    this.flattenedResources = null;
    this.resourceCnt = 0;
    return this.updateDayTableCols();
  },
  flattenResources: function(resources) {
    var orderSpecs, orderVal, res, sortFunc;
    orderVal = this.opt('resourceOrder');
    if (orderVal) {
      orderSpecs = parseFieldSpecs(orderVal);
      sortFunc = function(a, b) {
        return compareByFieldSpecs(a, b, orderSpecs);
      };
    } else {
      sortFunc = null;
    }
    res = [];
    this.accumulateResources(resources, sortFunc, res);
    return res;
  },
  accumulateResources: function(resources, sortFunc, res) {
    var j, len, resource, results, sortedResources;
    if (sortFunc) {
      sortedResources = resources.slice(0);
      sortedResources.sort(sortFunc);
    } else {
      sortedResources = resources;
    }
    results = [];
    for (j = 0, len = sortedResources.length; j < len; j++) {
      resource = sortedResources[j];
      res.push(resource);
      results.push(this.accumulateResources(resource.children, sortFunc, res));
    }
    return results;
  },
  updateDayTableCols: function() {
    this.datesAboveResources = this.opt('groupByDateAndResource');
    return FC.DayTableMixin.updateDayTableCols.call(this);
  },
  computeColCnt: function() {
    return (this.resourceCnt || 1) * this.daysPerRow;
  },
  getColDayIndex: function(col) {
    if (this.isRTL) {
      col = this.colCnt - 1 - col;
    }
    if (this.datesAboveResources) {
      return Math.floor(col / (this.resourceCnt || 1));
    } else {
      return col % this.daysPerRow;
    }
  },
  getColResource: function(col) {
    return this.flattenedResources[this.getColResourceIndex(col)];
  },
  getColResourceIndex: function(col) {
    if (this.isRTL) {
      col = this.colCnt - 1 - col;
    }
    if (this.datesAboveResources) {
      return col % (this.resourceCnt || 1);
    } else {
      return Math.floor(col / this.daysPerRow);
    }
  },
  indicesToCol: function(resourceIndex, dayIndex) {
    var col;
    col = this.datesAboveResources ? dayIndex * (this.resourceCnt || 1) + resourceIndex : resourceIndex * this.daysPerRow + dayIndex;
    if (this.isRTL) {
      col = this.colCnt - 1 - col;
    }
    return col;
  },
  renderHeadTrHtml: function() {
    if (!this.resourceCnt) {
      return FC.DayTableMixin.renderHeadTrHtml.call(this);
    } else {
      if (this.daysPerRow > 1) {
        if (this.datesAboveResources) {
          return this.renderHeadDateAndResourceHtml();
        } else {
          return this.renderHeadResourceAndDateHtml();
        }
      } else {
        return this.renderHeadResourceHtml();
      }
    }
  },
  renderHeadResourceHtml: function() {
    var j, len, ref, resource, resourceHtmls;
    resourceHtmls = [];
    ref = this.flattenedResources;
    for (j = 0, len = ref.length; j < len; j++) {
      resource = ref[j];
      resourceHtmls.push(this.renderHeadResourceCellHtml(resource));
    }
    return this.wrapTr(resourceHtmls, 'renderHeadIntroHtml');
  },
  renderHeadResourceAndDateHtml: function() {
    var date, dateHtmls, dayIndex, j, k, len, ref, ref1, resource, resourceHtmls;
    resourceHtmls = [];
    dateHtmls = [];
    ref = this.flattenedResources;
    for (j = 0, len = ref.length; j < len; j++) {
      resource = ref[j];
      resourceHtmls.push(this.renderHeadResourceCellHtml(resource, null, this.daysPerRow));
      for (dayIndex = k = 0, ref1 = this.daysPerRow; k < ref1; dayIndex = k += 1) {
        date = this.dayDates[dayIndex].clone();
        dateHtmls.push(this.renderHeadResourceDateCellHtml(date, resource));
      }
    }
    return this.wrapTr(resourceHtmls, 'renderHeadIntroHtml') + this.wrapTr(dateHtmls, 'renderHeadIntroHtml');
  },
  renderHeadDateAndResourceHtml: function() {
    var date, dateHtmls, dayIndex, j, k, len, ref, ref1, resource, resourceHtmls;
    dateHtmls = [];
    resourceHtmls = [];
    for (dayIndex = j = 0, ref = this.daysPerRow; j < ref; dayIndex = j += 1) {
      date = this.dayDates[dayIndex].clone();
      dateHtmls.push(this.renderHeadDateCellHtml(date, this.resourceCnt));
      ref1 = this.flattenedResources;
      for (k = 0, len = ref1.length; k < len; k++) {
        resource = ref1[k];
        resourceHtmls.push(this.renderHeadResourceCellHtml(resource, date));
      }
    }
    return this.wrapTr(dateHtmls, 'renderHeadIntroHtml') + this.wrapTr(resourceHtmls, 'renderHeadIntroHtml');
  },
  renderHeadResourceCellHtml: function(resource, date, colspan) {
    return '<th class="fc-resource-cell"' + ' data-resource-id="' + resource.id + '"' + (date ? ' data-date="' + date.format('YYYY-MM-DD') + '"' : '') + (colspan > 1 ? ' colspan="' + colspan + '"' : '') + '>' + htmlEscape(this.view.getResourceText(resource)) + '</th>';
  },
  renderHeadResourceDateCellHtml: function(date, resource, colspan) {
    return this.renderHeadDateCellHtml(date, colspan, 'data-resource-id="' + resource.id + '"');
  },
  processHeadResourceEls: function(containerEl) {
    return containerEl.find('.fc-resource-cell').each((function(_this) {
      return function(col, node) {
        var resource;
        if (_this.datesAboveResources) {
          resource = _this.getColResource(col);
        } else {
          resource = _this.flattenedResources[_this.isRTL ? _this.flattenedResources.length - 1 - col : col];
        }
        return _this.publiclyTrigger('resourceRender', {
          context: resource,
          args: [resource, $(node), $(), _this.view]
        });
      };
    })(this));
  },
  renderBgCellsHtml: function(row) {
    var col, date, htmls, j, ref, resource;
    if (!this.resourceCnt) {
      return FC.DayTableMixin.renderBgCellsHtml.call(this, row);
    } else {
      htmls = [];
      for (col = j = 0, ref = this.colCnt; j < ref; col = j += 1) {
        date = this.getCellDate(row, col);
        resource = this.getColResource(col);
        htmls.push(this.renderResourceBgCellHtml(date, resource));
      }
      return htmls.join('');
    }
  },
  renderResourceBgCellHtml: function(date, resource) {
    return this.renderBgCellHtml(date, 'data-resource-id="' + resource.id + '"');
  },
  wrapTr: function(cellHtmls, introMethodName) {
    if (this.isRTL) {
      cellHtmls.reverse();
      return '<tr>' + cellHtmls.join('') + this[introMethodName]() + '</tr>';
    } else {
      return '<tr>' + this[introMethodName]() + cellHtmls.join('') + '</tr>';
    }
  },

  /*
  	If there are no per-resource business hour definitions, returns null.
  	Otherwise, returns a list of business hours segs for *every* resource.
  	TODO: make more DRY with Calendar::buildCurrentBusinessFootprints
   */
  buildBusinessHourEventFootprints: function(wholeDay) {
    var eventFootprints, j, k, len, len1, plainEventFootprint, plainEventFootprints, ref, resource;
    if (this.flattenedResources) {
      eventFootprints = [];
      ref = this.flattenedResources;
      for (j = 0, len = ref.length; j < len; j++) {
        resource = ref[j];
        plainEventFootprints = this._buildBusinessHourEventFootprints(wholeDay, resource.businessHours || this.view.calendar.opt('businessHours'));
        for (k = 0, len1 = plainEventFootprints.length; k < len1; k++) {
          plainEventFootprint = plainEventFootprints[k];
          eventFootprints.push(new EventFootprint(new ResourceComponentFootprint(plainEventFootprint.componentFootprint.unzonedRange, plainEventFootprint.componentFootprint.isAllDay, resource.id), plainEventFootprint.eventDef, plainEventFootprint.eventInstance));
        }
      }
      return eventFootprints;
    } else {
      return Grid.prototype.buildBusinessHourEventFootprints.apply(this, arguments);
    }
  }
};

ResourceDayGrid = (function(superClass) {
  extend(ResourceDayGrid, superClass);

  function ResourceDayGrid() {
    return ResourceDayGrid.__super__.constructor.apply(this, arguments);
  }

  ResourceDayGrid.mixin(ResourceGridMixin);

  ResourceDayGrid.mixin(ResourceDayTableMixin);

  ResourceDayGrid.prototype.getHitFootprint = function(hit) {
    var plainFootprint;
    plainFootprint = ResourceDayGrid.__super__.getHitFootprint.apply(this, arguments);
    if (this.resourceCnt) {
      return new ResourceComponentFootprint(plainFootprint.unzonedRange, plainFootprint.isAllDay, this.getColResource(hit.col).id);
    } else {
      return plainFootprint;
    }
  };

  ResourceDayGrid.prototype.componentFootprintToSegs = function(componentFootprint) {
    var copy, genericSegs, j, k, l, len, len1, ref, resourceCnt, resourceIndex, resourceObj, resourceSegs, seg;
    resourceCnt = this.resourceCnt;
    genericSegs = this.datesAboveResources ? this.sliceRangeByDay(componentFootprint.unzonedRange) : this.sliceRangeByRow(componentFootprint.unzonedRange);
    if (!resourceCnt) {
      for (j = 0, len = genericSegs.length; j < len; j++) {
        seg = genericSegs[j];
        if (this.isRTL) {
          seg.leftCol = seg.lastRowDayIndex;
          seg.rightCol = seg.firstRowDayIndex;
        } else {
          seg.leftCol = seg.firstRowDayIndex;
          seg.rightCol = seg.lastRowDayIndex;
        }
      }
      return genericSegs;
    } else {
      resourceSegs = [];
      for (k = 0, len1 = genericSegs.length; k < len1; k++) {
        seg = genericSegs[k];
        for (resourceIndex = l = 0, ref = resourceCnt; l < ref; resourceIndex = l += 1) {
          resourceObj = this.flattenedResources[resourceIndex];
          if (!(componentFootprint instanceof ResourceComponentFootprint) || componentFootprint.resourceId === resourceObj.id) {
            copy = $.extend({}, seg);
            copy.resource = resourceObj;
            if (this.isRTL) {
              copy.leftCol = this.indicesToCol(resourceIndex, seg.lastRowDayIndex);
              copy.rightCol = this.indicesToCol(resourceIndex, seg.firstRowDayIndex);
            } else {
              copy.leftCol = this.indicesToCol(resourceIndex, seg.firstRowDayIndex);
              copy.rightCol = this.indicesToCol(resourceIndex, seg.lastRowDayIndex);
            }
            resourceSegs.push(copy);
          }
        }
      }
      return resourceSegs;
    }
  };

  return ResourceDayGrid;

})(FC.DayGrid);

ResourceTimeGrid = (function(superClass) {
  extend(ResourceTimeGrid, superClass);

  function ResourceTimeGrid() {
    return ResourceTimeGrid.__super__.constructor.apply(this, arguments);
  }

  ResourceTimeGrid.mixin(ResourceGridMixin);

  ResourceTimeGrid.mixin(ResourceDayTableMixin);

  ResourceTimeGrid.prototype.getHitFootprint = function(hit) {
    var plainFootprint;
    plainFootprint = ResourceTimeGrid.__super__.getHitFootprint.apply(this, arguments);
    if (this.resourceCnt) {
      return new ResourceComponentFootprint(plainFootprint.unzonedRange, plainFootprint.isAllDay, this.getColResource(hit.col).id);
    } else {
      return plainFootprint;
    }
  };

  ResourceTimeGrid.prototype.componentFootprintToSegs = function(componentFootprint) {
    var copy, genericSegs, j, k, l, len, len1, ref, resourceCnt, resourceIndex, resourceObj, resourceSegs, seg;
    resourceCnt = this.resourceCnt;
    genericSegs = this.sliceRangeByTimes(componentFootprint.unzonedRange);
    if (!resourceCnt) {
      for (j = 0, len = genericSegs.length; j < len; j++) {
        seg = genericSegs[j];
        seg.col = seg.dayIndex;
      }
      return genericSegs;
    } else {
      resourceSegs = [];
      for (k = 0, len1 = genericSegs.length; k < len1; k++) {
        seg = genericSegs[k];
        for (resourceIndex = l = 0, ref = resourceCnt; l < ref; resourceIndex = l += 1) {
          resourceObj = this.flattenedResources[resourceIndex];
          if (!(componentFootprint instanceof ResourceComponentFootprint) || componentFootprint.resourceId === resourceObj.id) {
            copy = $.extend({}, seg);
            copy.resource = resourceObj;
            copy.col = this.indicesToCol(resourceIndex, seg.dayIndex);
            resourceSegs.push(copy);
          }
        }
      }
      return resourceSegs;
    }
  };

  return ResourceTimeGrid;

})(FC.TimeGrid);

ResourceComponentFootprint = (function(superClass) {
  extend(ResourceComponentFootprint, superClass);

  ResourceComponentFootprint.prototype.resourceId = null;

  function ResourceComponentFootprint(unzonedRange, isAllDay, resourceId) {
    ResourceComponentFootprint.__super__.constructor.apply(this, arguments);
    this.resourceId = resourceId;
  }

  ResourceComponentFootprint.prototype.toLegacy = function(calendar) {
    var obj;
    obj = ResourceComponentFootprint.__super__.toLegacy.apply(this, arguments);
    obj.resourceId = this.resourceId;
    return obj;
  };

  return ResourceComponentFootprint;

})(ComponentFootprint);

TimelineView = (function(superClass) {
  extend(TimelineView, superClass);

  function TimelineView() {
    return TimelineView.__super__.constructor.apply(this, arguments);
  }

  TimelineView.prototype.timeGrid = null;

  TimelineView.prototype.isScrolled = false;

  TimelineView.prototype.usesMinMaxTime = true;

  TimelineView.prototype.initialize = function() {
    this.timeGrid = this.instantiateGrid();
    return this.addChild(this.timeGrid);
  };

  TimelineView.prototype.instantiateGrid = function() {
    return new TimelineGrid(this);
  };

  TimelineView.prototype.setDateProfileForRendering = function(dateProfile) {
    TimelineView.__super__.setDateProfileForRendering.apply(this, arguments);
    this.timeGrid.initScaleProps();
    return this.timeGrid.setRange(this.renderUnzonedRange);
  };

  TimelineView.prototype.getFallbackDuration = function() {
    return this.timeGrid.computeFallbackDuration();
  };

  TimelineView.prototype.renderSkeleton = function() {
    this.el.addClass('fc-timeline');
    if (this.opt('eventOverlap') === false) {
      this.el.addClass('fc-no-overlap');
    }
    this.el.html(this.renderSkeletonHtml());
    return this.renderTimeGridSkeleton();
  };

  TimelineView.prototype.renderSkeletonHtml = function() {
    var theme;
    theme = this.calendar.theme;
    return '<table class="' + theme.getClass('tableGrid') + '"> <thead class="fc-head"> <tr> <td class="fc-time-area ' + theme.getClass('widgetHeader') + '"></td> </tr> </thead> <tbody class="fc-body"> <tr> <td class="fc-time-area ' + theme.getClass('widgetContent') + '"></td> </tr> </tbody> </table>';
  };

  TimelineView.prototype.renderTimeGridSkeleton = function() {
    this.timeGrid.headEl = this.el.find('thead .fc-time-area');
    this.timeGrid.setElement(this.el.find('tbody .fc-time-area'));
    this.isScrolled = false;
    return this.timeGrid.bodyScroller.on('scroll', proxy(this, 'handleBodyScroll'));
  };

  TimelineView.prototype.handleBodyScroll = function(top, left) {
    if (top) {
      if (!this.isScrolled) {
        this.isScrolled = true;
        return this.el.addClass('fc-scrolled');
      }
    } else {
      if (this.isScrolled) {
        this.isScrolled = false;
        return this.el.removeClass('fc-scrolled');
      }
    }
  };

  TimelineView.prototype.unrenderSkeleton = function() {
    this.timeGrid.removeElement();
    this.handleBodyScroll(0);
    return TimelineView.__super__.unrenderSkeleton.apply(this, arguments);
  };

  TimelineView.prototype.renderDates = function() {
    return this.timeGrid.renderDates();
  };

  TimelineView.prototype.unrenderDates = function() {
    return this.timeGrid.unrenderDates();
  };

  TimelineView.prototype.getNowIndicatorUnit = function() {
    return this.timeGrid.getNowIndicatorUnit();
  };

  TimelineView.prototype.updateWidth = function() {
    return this.timeGrid.updateWidth();
  };

  TimelineView.prototype.setHeight = function(totalHeight, isAuto) {
    var bodyHeight;
    if (isAuto) {
      bodyHeight = 'auto';
    } else {
      bodyHeight = totalHeight - this.timeGrid.headHeight() - this.queryMiscHeight();
    }
    return this.timeGrid.bodyScroller.setHeight(bodyHeight);
  };

  TimelineView.prototype.queryMiscHeight = function() {
    return this.el.outerHeight() - this.timeGrid.headScroller.el.outerHeight() - this.timeGrid.bodyScroller.el.outerHeight();
  };

  TimelineView.prototype.computeInitialDateScroll = function() {
    var left, scrollTime;
    left = 0;
    if (this.timeGrid.isTimeScale) {
      scrollTime = this.opt('scrollTime');
      if (scrollTime) {
        scrollTime = moment.duration(scrollTime);
        left = this.timeGrid.dateToCoord(this.activeUnzonedRange.getStart().time(scrollTime));
      }
    }
    return {
      left: left
    };
  };

  TimelineView.prototype.queryDateScroll = function() {
    return {
      left: this.timeGrid.bodyScroller.getScrollLeft()
    };
  };

  TimelineView.prototype.applyDateScroll = function(scroll) {
    if (scroll.left != null) {
      this.timeGrid.headScroller.setScrollLeft(scroll.left);
      return this.timeGrid.bodyScroller.setScrollLeft(scroll.left);
    }
  };

  TimelineView.prototype.renderEventsPayload = function(eventsPayload) {
    this.timeGrid.renderEventsPayload(eventsPayload);
    return this.updateWidth();
  };

  TimelineView.prototype.unrenderEvents = function() {
    this.timeGrid.unrenderEvents();
    return this.updateWidth();
  };

  return TimelineView;

})(View);

cssToStr = FC.cssToStr;

TimelineGrid = (function(superClass) {
  extend(TimelineGrid, superClass);

  TimelineGrid.prototype.unzonedStart = null;

  TimelineGrid.prototype.unzonedEnd = null;

  TimelineGrid.prototype.slotDates = null;

  TimelineGrid.prototype.slotCnt = null;

  TimelineGrid.prototype.snapCnt = null;

  TimelineGrid.prototype.snapsPerSlot = null;

  TimelineGrid.prototype.snapDiffToIndex = null;

  TimelineGrid.prototype.snapIndexToDiff = null;

  TimelineGrid.prototype.headEl = null;

  TimelineGrid.prototype.slatContainerEl = null;

  TimelineGrid.prototype.slatEls = null;

  TimelineGrid.prototype.containerCoordCache = null;

  TimelineGrid.prototype.slatCoordCache = null;

  TimelineGrid.prototype.slatInnerCoordCache = null;

  TimelineGrid.prototype.headScroller = null;

  TimelineGrid.prototype.bodyScroller = null;

  TimelineGrid.prototype.joiner = null;

  TimelineGrid.prototype.follower = null;

  TimelineGrid.prototype.eventTitleFollower = null;

  TimelineGrid.prototype.timeWindowMs = null;

  TimelineGrid.prototype.slotDuration = null;

  TimelineGrid.prototype.snapDuration = null;

  TimelineGrid.prototype.duration = null;

  TimelineGrid.prototype.labelInterval = null;

  TimelineGrid.prototype.headerFormats = null;

  TimelineGrid.prototype.isTimeScale = null;

  TimelineGrid.prototype.largeUnit = null;

  TimelineGrid.prototype.emphasizeWeeks = false;

  TimelineGrid.prototype.titleFollower = null;

  TimelineGrid.prototype.segContainerEl = null;

  TimelineGrid.prototype.segContainerHeight = null;

  TimelineGrid.prototype.bgSegContainerEl = null;

  TimelineGrid.prototype.helperEls = null;

  TimelineGrid.prototype.innerEl = null;

  function TimelineGrid() {
    TimelineGrid.__super__.constructor.apply(this, arguments);
    this.slotWidth = this.opt('slotWidth');
  }

  TimelineGrid.prototype.isValidDate = function(date) {
    var ms;
    if (this.view.isHiddenDay(date)) {
      return false;
    } else if (this.isTimeScale) {
      ms = date.time() - this.view.minTime;
      ms = ((ms % 86400000) + 86400000) % 86400000;
      return ms < this.timeWindowMs;
    } else {
      return true;
    }
  };

  TimelineGrid.prototype.computeDisplayEventTime = function() {
    return !this.isTimeScale;
  };

  TimelineGrid.prototype.computeDisplayEventEnd = function() {
    return false;
  };

  TimelineGrid.prototype.computeEventTimeFormat = function() {
    return this.opt('extraSmallTimeFormat');
  };


  /*
  	Makes the given date consistent with isTimeScale/largeUnit,
  	so, either removes the times, ensures a time, or makes it the startOf largeUnit.
  	Strips all timezones. Returns new copy.
  	TODO: should maybe be called "normalizeRangeDate".
   */

  TimelineGrid.prototype.normalizeGridDate = function(date) {
    var normalDate;
    if (this.isTimeScale) {
      normalDate = date.clone();
      if (!normalDate.hasTime()) {
        normalDate.time(0);
      }
    } else {
      normalDate = date.clone().stripTime();
      if (this.largeUnit) {
        normalDate.startOf(this.largeUnit);
      }
    }
    return normalDate;
  };


  /*
  	TODO: avoid using Moments. use slat system somehow
  	THEN, can have componentFootprintToSegs handle this on its own
   */

  TimelineGrid.prototype.normalizeComponentFootprint = function(componentFootprint) {
    var adjustedEnd, adjustedStart, dayRange, unzonedRange;
    unzonedRange = componentFootprint.unzonedRange;
    if (this.isTimeScale) {
      adjustedStart = this.normalizeGridDate(unzonedRange.getStart());
      adjustedEnd = this.normalizeGridDate(unzonedRange.getEnd());
    } else {
      dayRange = this.view.computeDayRange(unzonedRange);
      if (this.largeUnit) {
        adjustedStart = dayRange.start.clone().startOf(this.largeUnit);
        adjustedEnd = dayRange.end.clone().startOf(this.largeUnit);
        if (!adjustedEnd.isSame(dayRange.end) || !adjustedEnd.isAfter(adjustedStart)) {
          adjustedEnd.add(this.slotDuration);
        }
      } else {
        adjustedStart = dayRange.start;
        adjustedEnd = dayRange.end;
      }
    }
    return new ComponentFootprint(new UnzonedRange(adjustedStart, adjustedEnd), !this.isTimeScale);
  };

  TimelineGrid.prototype.rangeUpdated = function() {
    var date, slotDates;
    this.timeWindowMs = this.view.maxTime - this.view.minTime;
    this.unzonedStart = this.normalizeGridDate(this.unzonedRange.getStart());
    this.unzonedEnd = this.normalizeGridDate(this.unzonedRange.getEnd());
    if (this.isTimeScale) {
      this.unzonedStart.add(this.view.minTime);
      this.unzonedEnd.subtract(1, 'day').add(this.view.maxTime);
    }
    this.unzonedRange = new UnzonedRange(this.unzonedStart, this.unzonedEnd);
    slotDates = [];
    date = this.unzonedStart.clone();
    while (date < this.unzonedEnd) {
      if (this.isValidDate(date)) {
        slotDates.push(date.clone());
      }
      date.add(this.slotDuration);
    }
    this.slotDates = slotDates;
    return this.updateGridDates();
  };

  TimelineGrid.prototype.updateGridDates = function() {
    var date, snapDiff, snapDiffToIndex, snapIndex, snapIndexToDiff;
    snapIndex = -1;
    snapDiff = 0;
    snapDiffToIndex = [];
    snapIndexToDiff = [];
    date = this.unzonedStart.clone();
    while (date < this.unzonedEnd) {
      if (this.isValidDate(date)) {
        snapIndex++;
        snapDiffToIndex.push(snapIndex);
        snapIndexToDiff.push(snapDiff);
      } else {
        snapDiffToIndex.push(snapIndex + 0.5);
      }
      date.add(this.snapDuration);
      snapDiff++;
    }
    this.snapDiffToIndex = snapDiffToIndex;
    this.snapIndexToDiff = snapIndexToDiff;
    this.snapCnt = snapIndex + 1;
    return this.slotCnt = this.snapCnt / this.snapsPerSlot;
  };

  TimelineGrid.prototype.componentFootprintToSegs = function(footprint) {
    var footprintEnd, footprintStart, normalFootprint, segEnd, segRange, segStart, segs;
    footprintStart = footprint.unzonedRange.getStart();
    footprintEnd = footprint.unzonedRange.getEnd();
    normalFootprint = this.normalizeComponentFootprint(footprint);
    segs = [];
    if (this.computeDateSnapCoverage(footprintStart) < this.computeDateSnapCoverage(footprintEnd)) {
      segRange = normalFootprint.unzonedRange.intersect(this.unzonedRange);
      if (segRange) {
        segStart = segRange.getStart();
        segEnd = segRange.getEnd();
        segs.push({
          start: segStart,
          end: segEnd,
          isStart: segRange.isStart && this.isValidDate(segStart),
          isEnd: segRange.isEnd && this.isValidDate(segEnd.clone().subtract(1))
        });
      }
    }
    return segs;
  };

  TimelineGrid.prototype.prepareHits = function() {
    return this.buildCoords();
  };

  TimelineGrid.prototype.queryHit = function(leftOffset, topOffset) {
    var containerCoordCache, localSnapIndex, partial, slatCoordCache, slatIndex, slatLeft, slatRight, slatWidth, snapIndex, snapLeft, snapRight, snapsPerSlot;
    snapsPerSlot = this.snapsPerSlot;
    slatCoordCache = this.slatCoordCache;
    containerCoordCache = this.containerCoordCache;
    if (containerCoordCache.isPointInBounds(leftOffset, topOffset)) {
      slatIndex = slatCoordCache.getHorizontalIndex(leftOffset);
      if (slatIndex != null) {
        slatWidth = slatCoordCache.getWidth(slatIndex);
        if (this.isRTL) {
          slatRight = slatCoordCache.getRightOffset(slatIndex);
          partial = (slatRight - leftOffset) / slatWidth;
          localSnapIndex = Math.floor(partial * snapsPerSlot);
          snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
          snapRight = slatRight - (localSnapIndex / snapsPerSlot) * slatWidth;
          snapLeft = snapRight - ((localSnapIndex + 1) / snapsPerSlot) * slatWidth;
        } else {
          slatLeft = slatCoordCache.getLeftOffset(slatIndex);
          partial = (leftOffset - slatLeft) / slatWidth;
          localSnapIndex = Math.floor(partial * snapsPerSlot);
          snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
          snapLeft = slatLeft + (localSnapIndex / snapsPerSlot) * slatWidth;
          snapRight = slatLeft + ((localSnapIndex + 1) / snapsPerSlot) * slatWidth;
        }
        return {
          snap: snapIndex,
          component: this,
          left: snapLeft,
          right: snapRight,
          top: containerCoordCache.getTopOffset(0),
          bottom: containerCoordCache.getBottomOffset(0)
        };
      }
    }
  };

  TimelineGrid.prototype.getHitFootprint = function(hit) {
    return new ComponentFootprint(this.getSnapUnzonedRange(hit.snap), !this.isTimeScale);
  };

  TimelineGrid.prototype.getHitEl = function(hit) {
    return this.getSnapEl(hit.snap);
  };


  /*
  	TODO: avoid using moments
   */

  TimelineGrid.prototype.getSnapUnzonedRange = function(snapIndex) {
    var end, start;
    start = this.unzonedStart.clone();
    start.add(multiplyDuration(this.snapDuration, this.snapIndexToDiff[snapIndex]));
    end = start.clone().add(this.snapDuration);
    return new UnzonedRange(start, end);
  };

  TimelineGrid.prototype.getSnapEl = function(snapIndex) {
    return this.slatEls.eq(Math.floor(snapIndex / this.snapsPerSlot));
  };

  TimelineGrid.prototype.renderSkeleton = function() {
    this.headScroller = new ClippedScroller({
      overflowX: 'clipped-scroll',
      overflowY: 'hidden'
    });
    this.headScroller.canvas = new ScrollerCanvas();
    this.headScroller.render();
    this.headEl.append(this.headScroller.el);
    this.bodyScroller = new ClippedScroller();
    this.bodyScroller.canvas = new ScrollerCanvas();
    this.bodyScroller.render();
    this.el.append(this.bodyScroller.el);
    this.innerEl = this.bodyScroller.canvas.contentEl;
    this.slatContainerEl = $('<div class="fc-slats"/>').appendTo(this.bodyScroller.canvas.bgEl);
    this.segContainerEl = $('<div class="fc-event-container"/>').appendTo(this.bodyScroller.canvas.contentEl);
    this.bgSegContainerEl = this.bodyScroller.canvas.bgEl;
    this.containerCoordCache = new CoordCache({
      els: this.bodyScroller.canvas.el,
      isHorizontal: true,
      isVertical: true
    });
    this.joiner = new ScrollJoiner('horizontal', [this.headScroller, this.bodyScroller]);
    if (true) {
      this.follower = new ScrollFollower(this.headScroller, true);
    }
    if (true) {
      this.eventTitleFollower = new ScrollFollower(this.bodyScroller);
      this.eventTitleFollower.minTravel = 50;
      if (this.isRTL) {
        this.eventTitleFollower.containOnNaturalRight = true;
      } else {
        this.eventTitleFollower.containOnNaturalLeft = true;
      }
    }
    return TimelineGrid.__super__.renderSkeleton.apply(this, arguments);
  };

  TimelineGrid.prototype.headColEls = null;

  TimelineGrid.prototype.slatColEls = null;

  TimelineGrid.prototype.renderDates = function() {
    var date, i, j, len, ref;
    this.headScroller.canvas.contentEl.html(this.renderHeadHtml());
    this.headColEls = this.headScroller.canvas.contentEl.find('col');
    this.slatContainerEl.html(this.renderSlatHtml());
    this.slatColEls = this.slatContainerEl.find('col');
    this.slatEls = this.slatContainerEl.find('td');
    this.slatCoordCache = new CoordCache({
      els: this.slatEls,
      isHorizontal: true
    });
    this.slatInnerCoordCache = new CoordCache({
      els: this.slatEls.find('> div'),
      isHorizontal: true,
      offsetParent: this.bodyScroller.canvas.el
    });
    ref = this.slotDates;
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      date = ref[i];
      this.publiclyTrigger('dayRender', {
        context: this.view,
        args: [date, this.slatEls.eq(i), this.view]
      });
    }
    if (this.follower) {
      return this.follower.setSprites(this.headEl.find('tr:not(:last-child) .fc-cell-text'));
    }
  };

  TimelineGrid.prototype.unrenderDates = function() {
    if (this.follower) {
      this.follower.clearSprites();
    }
    this.headScroller.canvas.contentEl.empty();
    this.slatContainerEl.empty();
    this.headScroller.canvas.clearWidth();
    return this.bodyScroller.canvas.clearWidth();
  };

  TimelineGrid.prototype.renderHeadHtml = function() {
    var cell, cellRows, date, format, formats, headerCellClassNames, html, i, isChrono, isLast, isSingleDay, isSuperRow, isWeekStart, j, k, l, labelInterval, leadingCell, len, len1, len2, len3, len4, len5, len6, m, n, newCell, p, prevWeekNumber, q, row, rowCells, rowUnits, slatHtml, slotCells, slotDates, text, theme, weekNumber;
    theme = this.view.calendar.theme;
    labelInterval = this.labelInterval;
    formats = this.headerFormats;
    cellRows = (function() {
      var j, len, results;
      results = [];
      for (j = 0, len = formats.length; j < len; j++) {
        format = formats[j];
        results.push([]);
      }
      return results;
    })();
    leadingCell = null;
    prevWeekNumber = null;
    slotDates = this.slotDates;
    slotCells = [];
    rowUnits = (function() {
      var j, len, results;
      results = [];
      for (j = 0, len = formats.length; j < len; j++) {
        format = formats[j];
        results.push(FC.queryMostGranularFormatUnit(format));
      }
      return results;
    })();
    for (j = 0, len = slotDates.length; j < len; j++) {
      date = slotDates[j];
      weekNumber = date.week();
      isWeekStart = this.emphasizeWeeks && prevWeekNumber !== null && prevWeekNumber !== weekNumber;
      for (row = k = 0, len1 = formats.length; k < len1; row = ++k) {
        format = formats[row];
        rowCells = cellRows[row];
        leadingCell = rowCells[rowCells.length - 1];
        isSuperRow = formats.length > 1 && row < formats.length - 1;
        newCell = null;
        if (isSuperRow) {
          text = date.format(format);
          if (!leadingCell || leadingCell.text !== text) {
            newCell = this.buildCellObject(date, text, rowUnits[row]);
          } else {
            leadingCell.colspan += 1;
          }
        } else {
          if (!leadingCell || isInt(divideRangeByDuration(this.unzonedStart, date, labelInterval))) {
            text = date.format(format);
            newCell = this.buildCellObject(date, text, rowUnits[row]);
          } else {
            leadingCell.colspan += 1;
          }
        }
        if (newCell) {
          newCell.weekStart = isWeekStart;
          rowCells.push(newCell);
        }
      }
      slotCells.push({
        weekStart: isWeekStart
      });
      prevWeekNumber = weekNumber;
    }
    isChrono = labelInterval > this.slotDuration;
    isSingleDay = this.slotDuration.as('days') === 1;
    html = '<table class="' + theme.getClass('tableGrid') + '">';
    html += '<colgroup>';
    for (l = 0, len2 = slotDates.length; l < len2; l++) {
      date = slotDates[l];
      html += '<col/>';
    }
    html += '</colgroup>';
    html += '<tbody>';
    for (i = m = 0, len3 = cellRows.length; m < len3; i = ++m) {
      rowCells = cellRows[i];
      isLast = i === cellRows.length - 1;
      html += '<tr' + (isChrono && isLast ? ' class="fc-chrono"' : '') + '>';
      for (n = 0, len4 = rowCells.length; n < len4; n++) {
        cell = rowCells[n];
        headerCellClassNames = [theme.getClass('widgetHeader')];
        if (cell.weekStart) {
          headerCellClassNames.push('fc-em-cell');
        }
        if (isSingleDay) {
          headerCellClassNames = headerCellClassNames.concat(this.getDayClasses(cell.date, true));
        }
        html += '<th class="' + headerCellClassNames.join(' ') + '"' + ' data-date="' + cell.date.format() + '"' + (cell.colspan > 1 ? ' colspan="' + cell.colspan + '"' : '') + '>' + '<div class="fc-cell-content">' + cell.spanHtml + '</div>' + '</th>';
      }
      html += '</tr>';
    }
    html += '</tbody></table>';
    slatHtml = '<table class="' + theme.getClass('tableGrid') + '">';
    slatHtml += '<colgroup>';
    for (p = 0, len5 = slotCells.length; p < len5; p++) {
      cell = slotCells[p];
      slatHtml += '<col/>';
    }
    slatHtml += '</colgroup>';
    slatHtml += '<tbody><tr>';
    for (i = q = 0, len6 = slotCells.length; q < len6; i = ++q) {
      cell = slotCells[i];
      date = slotDates[i];
      slatHtml += this.slatCellHtml(date, cell.weekStart);
    }
    slatHtml += '</tr></tbody></table>';
    this._slatHtml = slatHtml;
    return html;
  };

  TimelineGrid.prototype.buildCellObject = function(date, text, rowUnit) {
    var spanHtml;
    date = date.clone();
    spanHtml = this.view.buildGotoAnchorHtml({
      date: date,
      type: rowUnit,
      forceOff: !rowUnit
    }, {
      'class': 'fc-cell-text'
    }, htmlEscape(text));
    return {
      text: text,
      spanHtml: spanHtml,
      date: date,
      colspan: 1
    };
  };

  TimelineGrid.prototype.renderSlatHtml = function() {
    return this._slatHtml;
  };

  TimelineGrid.prototype.slatCellHtml = function(date, isEm) {
    var classes, theme;
    theme = this.view.calendar.theme;
    if (this.isTimeScale) {
      classes = [];
      classes.push(isInt(divideRangeByDuration(this.unzonedStart, date, this.labelInterval)) ? 'fc-major' : 'fc-minor');
    } else {
      classes = this.getDayClasses(date);
      classes.push('fc-day');
    }
    classes.unshift(theme.getClass('widgetContent'));
    if (isEm) {
      classes.push('fc-em-cell');
    }
    return '<td class="' + classes.join(' ') + '"' + ' data-date="' + date.format() + '"' + '><div /></td>';
  };

  TimelineGrid.prototype.businessHourSegs = null;

  TimelineGrid.prototype.renderBusinessHours = function() {
    var segs;
    if (!this.largeUnit) {
      segs = this.businessHourSegs = this.buildBusinessHourSegs(!this.isTimeScale);
      return this.renderFill('businessHours', segs, 'bgevent');
    }
  };

  TimelineGrid.prototype.unrenderBusinessHours = function() {
    return this.unrenderFill('businessHours');
  };

  TimelineGrid.prototype.nowIndicatorEls = null;

  TimelineGrid.prototype.getNowIndicatorUnit = function() {
    if (this.isTimeScale) {
      return computeGreatestUnit(this.slotDuration);
    }
  };

  TimelineGrid.prototype.renderNowIndicator = function(date) {
    var coord, css, nodes;
    nodes = [];
    date = this.normalizeGridDate(date);
    if (this.unzonedRange.containsDate(date)) {
      coord = this.dateToCoord(date);
      css = this.isRTL ? {
        right: -coord
      } : {
        left: coord
      };
      nodes.push($("<div class='fc-now-indicator fc-now-indicator-arrow'></div>").css(css).appendTo(this.headScroller.canvas.el)[0]);
      nodes.push($("<div class='fc-now-indicator fc-now-indicator-line'></div>").css(css).appendTo(this.bodyScroller.canvas.el)[0]);
    }
    return this.nowIndicatorEls = $(nodes);
  };

  TimelineGrid.prototype.unrenderNowIndicator = function() {
    if (this.nowIndicatorEls) {
      this.nowIndicatorEls.remove();
      return this.nowIndicatorEls = null;
    }
  };

  TimelineGrid.prototype.explicitSlotWidth = null;

  TimelineGrid.prototype.defaultSlotWidth = null;

  TimelineGrid.prototype.updateWidth = function() {
    var availableWidth, containerMinWidth, containerWidth, isDatesRendered, nonLastSlotWidth, slotWidth;
    isDatesRendered = this.headColEls;
    if (isDatesRendered) {
      slotWidth = Math.round(this.slotWidth || (this.slotWidth = this.computeSlotWidth()));
      containerWidth = slotWidth * this.slotDates.length;
      containerMinWidth = '';
      nonLastSlotWidth = slotWidth;
      availableWidth = this.bodyScroller.getClientWidth();
      if (availableWidth > containerWidth) {
        containerMinWidth = availableWidth;
        containerWidth = '';
        nonLastSlotWidth = Math.floor(availableWidth / this.slotDates.length);
      }
    } else {
      containerWidth = '';
      containerMinWidth = '';
    }
    this.headScroller.canvas.setWidth(containerWidth);
    this.headScroller.canvas.setMinWidth(containerMinWidth);
    this.bodyScroller.canvas.setWidth(containerWidth);
    this.bodyScroller.canvas.setMinWidth(containerMinWidth);
    if (isDatesRendered) {
      this.headColEls.slice(0, -1).add(this.slatColEls.slice(0, -1)).width(nonLastSlotWidth);
    }
    this.headScroller.updateSize();
    this.bodyScroller.updateSize();
    this.joiner.update();
    if (isDatesRendered) {
      this.buildCoords();
      this.updateSegPositions();
      this.view.updateNowIndicator();
    }
    if (this.follower) {
      this.follower.update();
    }
    if (this.eventTitleFollower) {
      return this.eventTitleFollower.update();
    }
  };

  TimelineGrid.prototype.computeSlotWidth = function() {
    var headerWidth, innerEls, maxInnerWidth, minWidth, slotWidth, slotsPerLabel;
    maxInnerWidth = 0;
    innerEls = this.headEl.find('tr:last-child th .fc-cell-text');
    innerEls.each(function(i, node) {
      var innerWidth;
      innerWidth = $(node).outerWidth();
      return maxInnerWidth = Math.max(maxInnerWidth, innerWidth);
    });
    headerWidth = maxInnerWidth + 1;
    slotsPerLabel = divideDurationByDuration(this.labelInterval, this.slotDuration);
    slotWidth = Math.ceil(headerWidth / slotsPerLabel);
    minWidth = this.headColEls.eq(0).css('min-width');
    if (minWidth) {
      minWidth = parseInt(minWidth, 10);
      if (minWidth) {
        slotWidth = Math.max(slotWidth, minWidth);
      }
    }
    return slotWidth;
  };

  TimelineGrid.prototype.buildCoords = function() {
    this.containerCoordCache.build();
    this.slatCoordCache.build();
    return this.slatInnerCoordCache.build();
  };

  TimelineGrid.prototype.computeDateSnapCoverage = function(date) {
    var snapCoverage, snapDiff, snapDiffInt;
    snapDiff = divideRangeByDuration(this.unzonedStart, date, this.snapDuration);
    if (snapDiff < 0) {
      return 0;
    } else if (snapDiff >= this.snapDiffToIndex.length) {
      return this.snapCnt;
    } else {
      snapDiffInt = Math.floor(snapDiff);
      snapCoverage = this.snapDiffToIndex[snapDiffInt];
      if (isInt(snapCoverage)) {
        snapCoverage += snapDiff - snapDiffInt;
      } else {
        snapCoverage = Math.ceil(snapCoverage);
      }
      return snapCoverage;
    }
  };

  TimelineGrid.prototype.dateToCoord = function(date) {
    var coordCache, partial, slotCoverage, slotIndex, snapCoverage;
    snapCoverage = this.computeDateSnapCoverage(date);
    slotCoverage = snapCoverage / this.snapsPerSlot;
    slotIndex = Math.floor(slotCoverage);
    slotIndex = Math.min(slotIndex, this.slotCnt - 1);
    partial = slotCoverage - slotIndex;
    coordCache = this.slatInnerCoordCache;
    if (this.isRTL) {
      return (coordCache.getRightPosition(slotIndex) - coordCache.getWidth(slotIndex) * partial) - this.containerCoordCache.getWidth(0);
    } else {
      return coordCache.getLeftPosition(slotIndex) + coordCache.getWidth(slotIndex) * partial;
    }
  };

  TimelineGrid.prototype.rangeToCoords = function(range) {
    if (this.isRTL) {
      return {
        right: this.dateToCoord(range.start),
        left: this.dateToCoord(range.end)
      };
    } else {
      return {
        left: this.dateToCoord(range.start),
        right: this.dateToCoord(range.end)
      };
    }
  };

  TimelineGrid.prototype.headHeight = function() {
    var table;
    table = this.headScroller.canvas.contentEl.find('table');
    return table.height.apply(table, arguments);
  };

  TimelineGrid.prototype.updateSegPositions = function() {
    var coords, j, len, seg, segs;
    segs = (this.segs || []).concat(this.businessHourSegs || []);
    for (j = 0, len = segs.length; j < len; j++) {
      seg = segs[j];
      coords = this.rangeToCoords(seg);
      seg.el.css({
        left: (seg.left = coords.left),
        right: -(seg.right = coords.right)
      });
    }
  };

  TimelineGrid.prototype.renderFgSegs = function(segs) {
    segs = this.renderFgSegEls(segs);
    this.renderFgSegsInContainers([[this, segs]]);
    this.updateSegFollowers(segs);
    return segs;
  };

  TimelineGrid.prototype.unrenderFgSegs = function() {
    this.clearSegFollowers();
    return this.unrenderFgContainers([this]);
  };

  TimelineGrid.prototype.renderFgSegsInContainers = function(pairs) {
    var container, coords, j, k, l, len, len1, len2, len3, len4, len5, len6, len7, m, n, p, q, r, ref, ref1, ref2, ref3, results, seg, segs;
    for (j = 0, len = pairs.length; j < len; j++) {
      ref = pairs[j], container = ref[0], segs = ref[1];
      for (k = 0, len1 = segs.length; k < len1; k++) {
        seg = segs[k];
        coords = this.rangeToCoords(seg);
        seg.el.css({
          left: (seg.left = coords.left),
          right: -(seg.right = coords.right)
        });
      }
    }
    for (l = 0, len2 = pairs.length; l < len2; l++) {
      ref1 = pairs[l], container = ref1[0], segs = ref1[1];
      for (m = 0, len3 = segs.length; m < len3; m++) {
        seg = segs[m];
        seg.el.appendTo(container.segContainerEl);
      }
    }
    for (n = 0, len4 = pairs.length; n < len4; n++) {
      ref2 = pairs[n], container = ref2[0], segs = ref2[1];
      for (p = 0, len5 = segs.length; p < len5; p++) {
        seg = segs[p];
        seg.height = seg.el.outerHeight(true);
      }
      this.buildSegLevels(segs);
      container.segContainerHeight = computeOffsetForSegs(segs);
    }
    results = [];
    for (q = 0, len6 = pairs.length; q < len6; q++) {
      ref3 = pairs[q], container = ref3[0], segs = ref3[1];
      for (r = 0, len7 = segs.length; r < len7; r++) {
        seg = segs[r];
        seg.el.css('top', seg.top);
      }
      results.push(container.segContainerEl.height(container.segContainerHeight));
    }
    return results;
  };

  TimelineGrid.prototype.buildSegLevels = function(segs) {
    var belowSeg, isLevelCollision, j, k, l, len, len1, len2, level, placedSeg, ref, ref1, segLevels, unplacedSeg;
    segLevels = [];
    this.sortEventSegs(segs);
    for (j = 0, len = segs.length; j < len; j++) {
      unplacedSeg = segs[j];
      unplacedSeg.above = [];
      level = 0;
      while (level < segLevels.length) {
        isLevelCollision = false;
        ref = segLevels[level];
        for (k = 0, len1 = ref.length; k < len1; k++) {
          placedSeg = ref[k];
          if (timeRowSegsCollide(unplacedSeg, placedSeg)) {
            unplacedSeg.above.push(placedSeg);
            isLevelCollision = true;
          }
        }
        if (isLevelCollision) {
          level += 1;
        } else {
          break;
        }
      }
      (segLevels[level] || (segLevels[level] = [])).push(unplacedSeg);
      level += 1;
      while (level < segLevels.length) {
        ref1 = segLevels[level];
        for (l = 0, len2 = ref1.length; l < len2; l++) {
          belowSeg = ref1[l];
          if (timeRowSegsCollide(unplacedSeg, belowSeg)) {
            belowSeg.above.push(unplacedSeg);
          }
        }
        level += 1;
      }
    }
    return segLevels;
  };

  TimelineGrid.prototype.unrenderFgContainers = function(containers) {
    var container, j, len, results;
    results = [];
    for (j = 0, len = containers.length; j < len; j++) {
      container = containers[j];
      container.segContainerEl.empty();
      container.segContainerEl.height('');
      results.push(container.segContainerHeight = null);
    }
    return results;
  };

  TimelineGrid.prototype.fgSegHtml = function(seg, disableResizing) {
    var classes, eventDef, isDraggable, isResizableFromEnd, isResizableFromStart, timeText;
    eventDef = seg.footprint.eventDef;
    isDraggable = this.view.isEventDefDraggable(eventDef);
    isResizableFromStart = seg.isStart && this.view.isEventDefResizableFromStart(eventDef);
    isResizableFromEnd = seg.isEnd && this.view.isEventDefResizableFromEnd(eventDef);
    classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd);
    classes.unshift('fc-timeline-event', 'fc-h-event');
    timeText = this.getEventTimeText(seg.footprint);
    return '<a class="' + classes.join(' ') + '" style="' + cssToStr(this.getSegSkinCss(seg)) + '"' + (eventDef.url ? ' href="' + htmlEscape(eventDef.url) + '"' : '') + '>' + '<div class="fc-content">' + (timeText ? '<span class="fc-time">' + htmlEscape(timeText) + '</span>' : '') + '<span class="fc-title">' + (eventDef.title ? htmlEscape(eventDef.title) : '&nbsp;') + '</span>' + '</div>' + '<div class="fc-bg" />' + (isResizableFromStart ? '<div class="fc-resizer fc-start-resizer"></div>' : '') + (isResizableFromEnd ? '<div class="fc-resizer fc-end-resizer"></div>' : '') + '</a>';
  };

  TimelineGrid.prototype.updateSegFollowers = function(segs) {
    var j, len, seg, sprites, titleEl;
    if (this.eventTitleFollower) {
      sprites = [];
      for (j = 0, len = segs.length; j < len; j++) {
        seg = segs[j];
        titleEl = seg.el.find('.fc-title');
        if (titleEl.length) {
          sprites.push(new ScrollFollowerSprite(titleEl));
        }
      }
      return this.eventTitleFollower.setSprites(sprites);
    }
  };

  TimelineGrid.prototype.clearSegFollowers = function() {
    if (this.eventTitleFollower) {
      return this.eventTitleFollower.clearSprites();
    }
  };

  TimelineGrid.prototype.segDragStart = function() {
    TimelineGrid.__super__.segDragStart.apply(this, arguments);
    if (this.eventTitleFollower) {
      return this.eventTitleFollower.forceRelative();
    }
  };

  TimelineGrid.prototype.segDragEnd = function() {
    TimelineGrid.__super__.segDragEnd.apply(this, arguments);
    if (this.eventTitleFollower) {
      return this.eventTitleFollower.clearForce();
    }
  };

  TimelineGrid.prototype.segResizeStart = function() {
    TimelineGrid.__super__.segResizeStart.apply(this, arguments);
    if (this.eventTitleFollower) {
      return this.eventTitleFollower.forceRelative();
    }
  };

  TimelineGrid.prototype.segResizeEnd = function() {
    TimelineGrid.__super__.segResizeEnd.apply(this, arguments);
    if (this.eventTitleFollower) {
      return this.eventTitleFollower.clearForce();
    }
  };

  TimelineGrid.prototype.renderHelperEventFootprintEls = function(eventFootprints, sourceSeg) {
    var segs;
    segs = this.eventFootprintsToSegs(eventFootprints);
    segs = this.renderFgSegEls(segs);
    return this.renderHelperSegsInContainers([[this, segs]], sourceSeg);
  };

  TimelineGrid.prototype.renderHelperSegsInContainers = function(pairs, sourceSeg) {
    var containerObj, coords, helperContainerEl, helperNodes, j, k, l, len, len1, len2, len3, m, ref, ref1, ref2, seg, segNodes, segs;
    helperNodes = [];
    segNodes = [];
    for (j = 0, len = pairs.length; j < len; j++) {
      ref = pairs[j], containerObj = ref[0], segs = ref[1];
      for (k = 0, len1 = segs.length; k < len1; k++) {
        seg = segs[k];
        coords = this.rangeToCoords(seg);
        seg.el.css({
          left: (seg.left = coords.left),
          right: -(seg.right = coords.right)
        });
        if (sourceSeg && sourceSeg.resourceId === ((ref1 = containerObj.resource) != null ? ref1.id : void 0)) {
          seg.el.css('top', sourceSeg.el.css('top'));
        } else {
          seg.el.css('top', 0);
        }
      }
    }
    for (l = 0, len2 = pairs.length; l < len2; l++) {
      ref2 = pairs[l], containerObj = ref2[0], segs = ref2[1];
      helperContainerEl = $('<div class="fc-event-container fc-helper-container"/>').appendTo(containerObj.innerEl);
      helperNodes.push(helperContainerEl[0]);
      for (m = 0, len3 = segs.length; m < len3; m++) {
        seg = segs[m];
        helperContainerEl.append(seg.el);
        segNodes.push(seg.el[0]);
      }
    }
    if (this.helperEls) {
      this.helperEls = this.helperEls.add($(helperNodes));
    } else {
      this.helperEls = $(helperNodes);
    }
    return $(segNodes);
  };

  TimelineGrid.prototype.unrenderHelper = function() {
    if (this.helperEls) {
      this.helperEls.remove();
      return this.helperEls = null;
    }
  };

  TimelineGrid.prototype.renderEventResize = function(eventFootprints, seg) {
    var eventFootprint, j, len;
    for (j = 0, len = eventFootprints.length; j < len; j++) {
      eventFootprint = eventFootprints[j];
      this.renderHighlight(eventFootprint.componentFootprint);
    }
    return this.renderHelperEventFootprints(eventFootprints, seg);
  };

  TimelineGrid.prototype.unrenderEventResize = function() {
    this.unrenderHighlight();
    return this.unrenderHelper();
  };

  TimelineGrid.prototype.renderFill = function(type, segs, className) {
    segs = this.renderFillSegEls(type, segs);
    this.renderFillInContainers(type, [[this, segs]], className);
    return segs;
  };

  TimelineGrid.prototype.renderFillInContainers = function(type, pairs, className) {
    var containerObj, j, len, ref, results, segs;
    results = [];
    for (j = 0, len = pairs.length; j < len; j++) {
      ref = pairs[j], containerObj = ref[0], segs = ref[1];
      results.push(this.renderFillInContainer(type, containerObj, segs, className));
    }
    return results;
  };

  TimelineGrid.prototype.renderFillInContainer = function(type, containerObj, segs, className) {
    var containerEl, coords, j, len, seg;
    if (segs.length) {
      className || (className = type.toLowerCase());
      containerEl = $('<div class="fc-' + className + '-container" />').appendTo(containerObj.bgSegContainerEl);
      for (j = 0, len = segs.length; j < len; j++) {
        seg = segs[j];
        coords = this.rangeToCoords(seg);
        seg.el.css({
          left: (seg.left = coords.left),
          right: -(seg.right = coords.right)
        });
        seg.el.appendTo(containerEl);
      }
      if (this.elsByFill[type]) {
        return this.elsByFill[type] = this.elsByFill[type].add(containerEl);
      } else {
        return this.elsByFill[type] = containerEl;
      }
    }
  };

  TimelineGrid.prototype.renderDrag = function(eventFootprints, seg) {
    var eventFootprint, j, len;
    if (seg) {
      return this.renderHelperEventFootprints(eventFootprints);
    } else {
      for (j = 0, len = eventFootprints.length; j < len; j++) {
        eventFootprint = eventFootprints[j];
        this.renderHighlight(eventFootprint.componentFootprint);
      }
      return null;
    }
  };

  TimelineGrid.prototype.unrenderDrag = function() {
    this.unrenderHelper();
    return this.unrenderHighlight();
  };

  return TimelineGrid;

})(Grid);

computeOffsetForSegs = function(segs) {
  var j, len, max, seg;
  max = 0;
  for (j = 0, len = segs.length; j < len; j++) {
    seg = segs[j];
    max = Math.max(max, computeOffsetForSeg(seg));
  }
  return max;
};

computeOffsetForSeg = function(seg) {
  if (seg.top == null) {
    seg.top = computeOffsetForSegs(seg.above);
  }
  return seg.top + seg.height;
};

timeRowSegsCollide = function(seg0, seg1) {
  return seg0.left < seg1.right && seg0.right > seg1.left;
};

MIN_AUTO_LABELS = 18;

MAX_AUTO_SLOTS_PER_LABEL = 6;

MAX_AUTO_CELLS = 200;

MAX_CELLS = 1000;

DEFAULT_GRID_DURATION = {
  months: 1
};

STOCK_SUB_DURATIONS = [
  {
    years: 1
  }, {
    months: 1
  }, {
    days: 1
  }, {
    hours: 1
  }, {
    minutes: 30
  }, {
    minutes: 15
  }, {
    minutes: 10
  }, {
    minutes: 5
  }, {
    minutes: 1
  }, {
    seconds: 30
  }, {
    seconds: 15
  }, {
    seconds: 10
  }, {
    seconds: 5
  }, {
    seconds: 1
  }, {
    milliseconds: 500
  }, {
    milliseconds: 100
  }, {
    milliseconds: 10
  }, {
    milliseconds: 1
  }
];

TimelineGrid.prototype.initScaleProps = function() {
  var input, slotUnit, type;
  this.labelInterval = this.queryDurationOption('slotLabelInterval');
  this.slotDuration = this.queryDurationOption('slotDuration');
  this.validateLabelAndSlot();
  this.ensureLabelInterval();
  this.ensureSlotDuration();
  input = this.opt('slotLabelFormat');
  type = $.type(input);
  this.headerFormats = type === 'array' ? input : type === 'string' ? [input] : this.computeHeaderFormats();
  this.isTimeScale = durationHasTime(this.slotDuration);
  this.largeUnit = !this.isTimeScale ? (slotUnit = computeGreatestUnit(this.slotDuration), /year|month|week/.test(slotUnit) ? slotUnit : void 0) : void 0;
  this.emphasizeWeeks = this.slotDuration.as('days') === 1 && this.view.currentRangeAs('weeks') >= 2 && !this.opt('businessHours');

  /*
  	console.log('label interval =', @labelInterval.humanize())
  	console.log('slot duration =', @slotDuration.humanize())
  	console.log('header formats =', @headerFormats)
  	console.log('isTimeScale', @isTimeScale)
  	console.log('largeUnit', @largeUnit)
   */
  this.snapDuration = (input = this.opt('snapDuration')) ? moment.duration(input) : this.slotDuration;
  return this.snapsPerSlot = divideDurationByDuration(this.slotDuration, this.snapDuration);
};

TimelineGrid.prototype.queryDurationOption = function(name) {
  var dur, input;
  input = this.opt(name);
  if (input != null) {
    dur = moment.duration(input);
    if (+dur) {
      return dur;
    }
  }
};

TimelineGrid.prototype.validateLabelAndSlot = function() {
  var currentUnzonedRange, labelCnt, slotCnt, slotsPerLabel;
  currentUnzonedRange = this.view.currentUnzonedRange;
  if (this.labelInterval) {
    labelCnt = divideRangeByDuration(currentUnzonedRange.getStart(), currentUnzonedRange.getEnd(), this.labelInterval);
    if (labelCnt > MAX_CELLS) {
      FC.warn('slotLabelInterval results in too many cells');
      this.labelInterval = null;
    }
  }
  if (this.slotDuration) {
    slotCnt = divideRangeByDuration(currentUnzonedRange.getStart(), currentUnzonedRange.getEnd(), this.slotDuration);
    if (slotCnt > MAX_CELLS) {
      FC.warn('slotDuration results in too many cells');
      this.slotDuration = null;
    }
  }
  if (this.labelInterval && this.slotDuration) {
    slotsPerLabel = divideDurationByDuration(this.labelInterval, this.slotDuration);
    if (!isInt(slotsPerLabel) || slotsPerLabel < 1) {
      FC.warn('slotLabelInterval must be a multiple of slotDuration');
      return this.slotDuration = null;
    }
  }
};

TimelineGrid.prototype.computeFallbackDuration = function() {
  var duration, input, j, labelCnt, labelInterval;
  duration = null;
  if (!this.labelInterval && !this.slotDuration) {
    duration = moment.duration(DEFAULT_GRID_DURATION);
  } else {
    labelInterval = this.ensureLabelInterval();
    for (j = STOCK_SUB_DURATIONS.length - 1; j >= 0; j += -1) {
      input = STOCK_SUB_DURATIONS[j];
      duration = moment.duration(input);
      labelCnt = divideDurationByDuration(duration, labelInterval);
      if (labelCnt >= MIN_AUTO_LABELS) {
        break;
      }
    }
  }
  return duration;
};

TimelineGrid.prototype.ensureLabelInterval = function() {
  var currentUnzonedRange, input, j, k, labelCnt, labelInterval, len, len1, slotsPerLabel, tryLabelInterval;
  currentUnzonedRange = this.view.currentUnzonedRange;
  labelInterval = this.labelInterval;
  if (!labelInterval) {
    if (this.slotDuration) {
      for (j = 0, len = STOCK_SUB_DURATIONS.length; j < len; j++) {
        input = STOCK_SUB_DURATIONS[j];
        tryLabelInterval = moment.duration(input);
        slotsPerLabel = divideDurationByDuration(tryLabelInterval, this.slotDuration);
        if (isInt(slotsPerLabel) && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
          labelInterval = tryLabelInterval;
          break;
        }
      }
      if (!labelInterval) {
        labelInterval = this.slotDuration;
      }
    } else {
      for (k = 0, len1 = STOCK_SUB_DURATIONS.length; k < len1; k++) {
        input = STOCK_SUB_DURATIONS[k];
        labelInterval = moment.duration(input);
        labelCnt = divideRangeByDuration(currentUnzonedRange.getStart(), currentUnzonedRange.getEnd(), labelInterval);
        if (labelCnt >= MIN_AUTO_LABELS) {
          break;
        }
      }
    }
    this.labelInterval = labelInterval;
  }
  return labelInterval;
};

TimelineGrid.prototype.ensureSlotDuration = function() {
  var currentUnzonedRange, input, j, labelInterval, len, slotCnt, slotDuration, slotsPerLabel, trySlotDuration;
  currentUnzonedRange = this.view.currentUnzonedRange;
  slotDuration = this.slotDuration;
  if (!slotDuration) {
    labelInterval = this.ensureLabelInterval();
    for (j = 0, len = STOCK_SUB_DURATIONS.length; j < len; j++) {
      input = STOCK_SUB_DURATIONS[j];
      trySlotDuration = moment.duration(input);
      slotsPerLabel = divideDurationByDuration(labelInterval, trySlotDuration);
      if (isInt(slotsPerLabel) && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
        slotDuration = trySlotDuration;
        break;
      }
    }
    if (slotDuration) {
      slotCnt = divideRangeByDuration(currentUnzonedRange.getStart(), currentUnzonedRange.getEnd(), slotDuration);
      if (slotCnt > MAX_AUTO_CELLS) {
        slotDuration = null;
      }
    }
    if (!slotDuration) {
      slotDuration = labelInterval;
    }
    this.slotDuration = slotDuration;
  }
  return slotDuration;
};

TimelineGrid.prototype.computeHeaderFormats = function() {
  var format0, format1, format2, labelInterval, unit, view, weekNumbersVisible;
  view = this.view;
  labelInterval = this.labelInterval;
  unit = computeGreatestUnit(labelInterval);
  weekNumbersVisible = this.opt('weekNumbers');
  format0 = format1 = format2 = null;
  if (unit === 'week' && !weekNumbersVisible) {
    unit = 'day';
  }
  switch (unit) {
    case 'year':
      format0 = 'YYYY';
      break;
    case 'month':
      if (view.currentRangeAs('years') > 1) {
        format0 = 'YYYY';
      }
      format1 = 'MMM';
      break;
    case 'week':
      if (view.currentRangeAs('years') > 1) {
        format0 = 'YYYY';
      }
      format1 = this.opt('shortWeekFormat');
      break;
    case 'day':
      if (view.currentRangeAs('years') > 1) {
        format0 = this.opt('monthYearFormat');
      } else if (view.currentRangeAs('months') > 1) {
        format0 = 'MMMM';
      }
      if (weekNumbersVisible) {
        format1 = this.opt('weekFormat');
      }
      format2 = 'dd D';
      break;
    case 'hour':
      if (weekNumbersVisible) {
        format0 = this.opt('weekFormat');
      }
      if (view.currentRangeAs('days') > 1) {
        format1 = this.opt('dayOfMonthFormat');
      }
      format2 = this.opt('smallTimeFormat');
      break;
    case 'minute':
      if (labelInterval.asMinutes() / 60 >= MAX_AUTO_SLOTS_PER_LABEL) {
        format0 = this.opt('hourFormat');
        format1 = '[:]mm';
      } else {
        format0 = this.opt('mediumTimeFormat');
      }
      break;
    case 'second':
      if (labelInterval.asSeconds() / 60 >= MAX_AUTO_SLOTS_PER_LABEL) {
        format0 = 'LT';
        format1 = '[:]ss';
      } else {
        format0 = 'LTS';
      }
      break;
    case 'millisecond':
      format0 = 'LTS';
      format1 = '[.]SSS';
  }
  return [].concat(format0 || [], format1 || [], format2 || []);
};

FC.views.timeline = {
  "class": TimelineView,
  defaults: {
    eventResizableFromStart: true
  }
};

FC.views.timelineDay = {
  type: 'timeline',
  duration: {
    days: 1
  }
};

FC.views.timelineWeek = {
  type: 'timeline',
  duration: {
    weeks: 1
  }
};

FC.views.timelineMonth = {
  type: 'timeline',
  duration: {
    months: 1
  }
};

FC.views.timelineYear = {
  type: 'timeline',
  duration: {
    years: 1
  }
};

ResourceTimelineView = (function(superClass) {
  extend(ResourceTimelineView, superClass);

  function ResourceTimelineView() {
    return ResourceTimelineView.__super__.constructor.apply(this, arguments);
  }

  ResourceTimelineView.mixin(ResourceViewMixin);

  ResourceTimelineView.prototype.canHandleSpecificResources = true;

  ResourceTimelineView.prototype.resourceGrid = null;

  ResourceTimelineView.prototype.tbodyHash = null;

  ResourceTimelineView.prototype.joiner = null;

  ResourceTimelineView.prototype.dividerEls = null;

  ResourceTimelineView.prototype.superHeaderText = null;

  ResourceTimelineView.prototype.isVGrouping = null;

  ResourceTimelineView.prototype.isHGrouping = null;

  ResourceTimelineView.prototype.groupSpecs = null;

  ResourceTimelineView.prototype.colSpecs = null;

  ResourceTimelineView.prototype.orderSpecs = null;

  ResourceTimelineView.prototype.rowHierarchy = null;

  ResourceTimelineView.prototype.resourceRowHash = null;

  ResourceTimelineView.prototype.nestingCnt = 0;

  ResourceTimelineView.prototype.isNesting = null;

  ResourceTimelineView.prototype.dividerWidth = null;

  ResourceTimelineView.prototype.initialize = function() {
    ResourceTimelineView.__super__.initialize.apply(this, arguments);
    this.processResourceOptions();
    this.resourceGrid = new Spreadsheet(this);
    this.rowHierarchy = new RowParent(this);
    return this.resourceRowHash = {};
  };

  ResourceTimelineView.prototype.instantiateGrid = function() {
    return new ResourceTimelineGrid(this);
  };

  ResourceTimelineView.prototype.processResourceOptions = function() {
    var allColSpecs, allOrderSpecs, colSpec, defaultLabelText, groupColSpecs, groupSpec, groupSpecs, hGroupField, isGroup, isHGrouping, isVGrouping, j, k, l, labelText, len, len1, len2, orderSpec, plainColSpecs, plainOrderSpecs, superHeaderText;
    allColSpecs = this.opt('resourceColumns') || [];
    labelText = this.opt('resourceLabelText');
    defaultLabelText = 'Resources';
    superHeaderText = null;
    if (!allColSpecs.length) {
      allColSpecs.push({
        labelText: labelText || defaultLabelText,
        text: this.getResourceTextFunc()
      });
    } else {
      superHeaderText = labelText;
    }
    plainColSpecs = [];
    groupColSpecs = [];
    groupSpecs = [];
    isVGrouping = false;
    isHGrouping = false;
    for (j = 0, len = allColSpecs.length; j < len; j++) {
      colSpec = allColSpecs[j];
      if (colSpec.group) {
        groupColSpecs.push(colSpec);
      } else {
        plainColSpecs.push(colSpec);
      }
    }
    plainColSpecs[0].isMain = true;
    if (groupColSpecs.length) {
      groupSpecs = groupColSpecs;
      isVGrouping = true;
    } else {
      hGroupField = this.opt('resourceGroupField');
      if (hGroupField) {
        isHGrouping = true;
        groupSpecs.push({
          field: hGroupField,
          text: this.opt('resourceGroupText'),
          render: this.opt('resourceGroupRender')
        });
      }
    }
    allOrderSpecs = parseFieldSpecs(this.opt('resourceOrder'));
    plainOrderSpecs = [];
    for (k = 0, len1 = allOrderSpecs.length; k < len1; k++) {
      orderSpec = allOrderSpecs[k];
      isGroup = false;
      for (l = 0, len2 = groupSpecs.length; l < len2; l++) {
        groupSpec = groupSpecs[l];
        if (groupSpec.field === orderSpec.field) {
          groupSpec.order = orderSpec.order;
          isGroup = true;
          break;
        }
      }
      if (!isGroup) {
        plainOrderSpecs.push(orderSpec);
      }
    }
    this.superHeaderText = superHeaderText;
    this.isVGrouping = isVGrouping;
    this.isHGrouping = isHGrouping;
    this.groupSpecs = groupSpecs;
    this.colSpecs = groupColSpecs.concat(plainColSpecs);
    return this.orderSpecs = plainOrderSpecs;
  };

  ResourceTimelineView.prototype.renderSkeleton = function() {
    ResourceTimelineView.__super__.renderSkeleton.apply(this, arguments);
    this.renderResourceGridSkeleton();
    this.tbodyHash = {
      spreadsheet: this.resourceGrid.tbodyEl,
      event: this.timeGrid.tbodyEl
    };
    this.joiner = new ScrollJoiner('vertical', [this.resourceGrid.bodyScroller, this.timeGrid.bodyScroller]);
    return this.initDividerMoving();
  };

  ResourceTimelineView.prototype.renderSkeletonHtml = function() {
    var theme;
    theme = this.calendar.theme;
    return '<table class="' + theme.getClass('tableGrid') + '"> <thead class="fc-head"> <tr> <td class="fc-resource-area ' + theme.getClass('widgetHeader') + '"></td> <td class="fc-divider fc-col-resizer ' + theme.getClass('widgetHeader') + '"></td> <td class="fc-time-area ' + theme.getClass('widgetHeader') + '"></td> </tr> </thead> <tbody class="fc-body"> <tr> <td class="fc-resource-area ' + theme.getClass('widgetContent') + '"></td> <td class="fc-divider fc-col-resizer ' + theme.getClass('widgetHeader') + '"></td> <td class="fc-time-area ' + theme.getClass('widgetContent') + '"></td> </tr> </tbody> </table>';
  };

  ResourceTimelineView.prototype.renderResourceGridSkeleton = function() {
    this.resourceGrid.el = this.el.find('tbody .fc-resource-area');
    this.resourceGrid.headEl = this.el.find('thead .fc-resource-area');
    return this.resourceGrid.renderSkeleton();
  };

  ResourceTimelineView.prototype.initDividerMoving = function() {
    var ref;
    this.dividerEls = this.el.find('.fc-divider');
    this.dividerWidth = (ref = this.opt('resourceAreaWidth')) != null ? ref : this.resourceGrid.tableWidth;
    if (this.dividerWidth != null) {
      this.positionDivider(this.dividerWidth);
    }
    return this.dividerEls.on('mousedown', (function(_this) {
      return function(ev) {
        return _this.dividerMousedown(ev);
      };
    })(this));
  };

  ResourceTimelineView.prototype.dividerMousedown = function(ev) {
    var dragListener, isRTL, maxWidth, minWidth, origWidth;
    isRTL = this.opt('isRTL');
    minWidth = 30;
    maxWidth = this.el.width() - 30;
    origWidth = this.getNaturalDividerWidth();
    dragListener = new DragListener({
      dragStart: (function(_this) {
        return function() {
          return _this.dividerEls.addClass('fc-active');
        };
      })(this),
      drag: (function(_this) {
        return function(dx, dy) {
          var width;
          if (isRTL) {
            width = origWidth - dx;
          } else {
            width = origWidth + dx;
          }
          width = Math.max(width, minWidth);
          width = Math.min(width, maxWidth);
          _this.dividerWidth = width;
          _this.positionDivider(width);
          return _this.updateWidth();
        };
      })(this),
      dragEnd: (function(_this) {
        return function() {
          return _this.dividerEls.removeClass('fc-active');
        };
      })(this)
    });
    return dragListener.startInteraction(ev);
  };

  ResourceTimelineView.prototype.getNaturalDividerWidth = function() {
    return this.el.find('.fc-resource-area').width();
  };

  ResourceTimelineView.prototype.positionDivider = function(w) {
    return this.el.find('.fc-resource-area').width(w);
  };

  ResourceTimelineView.prototype.renderEventsPayload = function(eventsPayload) {
    this.timeGrid.renderEventsPayload(eventsPayload);
    this.syncRowHeights();
    return this.updateWidth();
  };

  ResourceTimelineView.prototype.unrenderEvents = function() {
    this.timeGrid.unrenderEvents();
    this.syncRowHeights();
    return this.updateWidth();
  };

  ResourceTimelineView.prototype.updateWidth = function() {
    ResourceTimelineView.__super__.updateWidth.apply(this, arguments);
    this.resourceGrid.updateWidth();
    this.joiner.update();
    if (this.cellFollower) {
      return this.cellFollower.update();
    }
  };

  ResourceTimelineView.prototype.updateHeight = function(isResize) {
    ResourceTimelineView.__super__.updateHeight.apply(this, arguments);
    if (isResize) {
      return this.syncRowHeights();
    }
  };

  ResourceTimelineView.prototype.setHeight = function(totalHeight, isAuto) {
    var bodyHeight, headHeight;
    headHeight = this.syncHeadHeights();
    if (isAuto) {
      bodyHeight = 'auto';
    } else {
      bodyHeight = totalHeight - headHeight - this.queryMiscHeight();
    }
    this.timeGrid.bodyScroller.setHeight(bodyHeight);
    return this.resourceGrid.bodyScroller.setHeight(bodyHeight);
  };

  ResourceTimelineView.prototype.queryMiscHeight = function() {
    return this.el.outerHeight() - Math.max(this.resourceGrid.headScroller.el.outerHeight(), this.timeGrid.headScroller.el.outerHeight()) - Math.max(this.resourceGrid.bodyScroller.el.outerHeight(), this.timeGrid.bodyScroller.el.outerHeight());
  };

  ResourceTimelineView.prototype.syncHeadHeights = function() {
    var headHeight;
    this.resourceGrid.headHeight('auto');
    this.timeGrid.headHeight('auto');
    headHeight = Math.max(this.resourceGrid.headHeight(), this.timeGrid.headHeight());
    this.resourceGrid.headHeight(headHeight);
    this.timeGrid.headHeight(headHeight);
    return headHeight;
  };

  ResourceTimelineView.prototype.renderResources = function(resources) {
    var j, len, resource;
    this.batchRows();
    for (j = 0, len = resources.length; j < len; j++) {
      resource = resources[j];
      this.insertResource(resource);
    }
    this.rowHierarchy.show();
    this.unbatchRows();
    return this.reinitializeCellFollowers();
  };

  ResourceTimelineView.prototype.unrenderResources = function() {
    this.batchRows();
    this.rowHierarchy.removeChildren();
    this.unbatchRows();
    return this.reinitializeCellFollowers();
  };


  /*
  	TODO: the scenario where there were previously unassociated events that are now
  	 attached to this resource. should render those events immediately.
  
  	Responsible for rendering the new resource
   */

  ResourceTimelineView.prototype.renderResource = function(resource) {
    this.insertResource(resource);
    return this.reinitializeCellFollowers();
  };

  ResourceTimelineView.prototype.unrenderResource = function(resource) {
    var row;
    row = this.getResourceRow(resource.id);
    if (row) {
      this.batchRows();
      row.remove();
      this.unbatchRows();
      return this.reinitializeCellFollowers();
    }
  };

  ResourceTimelineView.prototype.cellFollower = null;

  ResourceTimelineView.prototype.reinitializeCellFollowers = function() {
    var cellContent, j, len, nodes, ref, row;
    if (this.cellFollower) {
      this.cellFollower.clearSprites();
    }
    this.cellFollower = new ScrollFollower(this.resourceGrid.bodyScroller, true);
    this.cellFollower.isHFollowing = false;
    this.cellFollower.isVFollowing = true;
    nodes = [];
    ref = this.rowHierarchy.getNodes();
    for (j = 0, len = ref.length; j < len; j++) {
      row = ref[j];
      if (row instanceof VRowGroup) {
        if (row.groupTd) {
          cellContent = row.groupTd.find('.fc-cell-content');
          if (cellContent.length) {
            nodes.push(cellContent[0]);
          }
        }
      }
    }
    return this.cellFollower.setSprites($(nodes));
  };

  ResourceTimelineView.prototype.insertResource = function(resource, parentResourceRow) {
    var childResource, j, len, parentId, ref, results, row;
    row = new ResourceRow(this, resource);
    if (parentResourceRow == null) {
      parentId = resource.parentId;
      if (parentId) {
        parentResourceRow = this.getResourceRow(parentId);
      }
    }
    if (parentResourceRow) {
      this.insertRowAsChild(row, parentResourceRow);
    } else {
      this.insertRow(row);
    }
    ref = resource.children;
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      childResource = ref[j];
      results.push(this.insertResource(childResource, row));
    }
    return results;
  };

  ResourceTimelineView.prototype.insertRow = function(row, parent, groupSpecs) {
    var group;
    if (parent == null) {
      parent = this.rowHierarchy;
    }
    if (groupSpecs == null) {
      groupSpecs = this.groupSpecs;
    }
    if (groupSpecs.length) {
      group = this.ensureResourceGroup(row, parent, groupSpecs[0]);
      if (group instanceof HRowGroup) {
        return this.insertRowAsChild(row, group);
      } else {
        return this.insertRow(row, group, groupSpecs.slice(1));
      }
    } else {
      return this.insertRowAsChild(row, parent);
    }
  };

  ResourceTimelineView.prototype.insertRowAsChild = function(row, parent) {
    return parent.addChild(row, this.computeChildRowPosition(row, parent));
  };

  ResourceTimelineView.prototype.computeChildRowPosition = function(child, parent) {
    var cmp, i, j, len, ref, sibling;
    if (this.orderSpecs.length) {
      ref = parent.children;
      for (i = j = 0, len = ref.length; j < len; i = ++j) {
        sibling = ref[i];
        cmp = this.compareResources(sibling.resource || {}, child.resource || {});
        if (cmp > 0) {
          return i;
        }
      }
    }
    return null;
  };

  ResourceTimelineView.prototype.compareResources = function(a, b) {
    return compareByFieldSpecs(a, b, this.orderSpecs);
  };

  ResourceTimelineView.prototype.ensureResourceGroup = function(row, parent, spec) {
    var cmp, group, groupValue, i, j, k, len, len1, ref, ref1, testGroup;
    groupValue = (row.resource || {})[spec.field];
    group = null;
    if (spec.order) {
      ref = parent.children;
      for (i = j = 0, len = ref.length; j < len; i = ++j) {
        testGroup = ref[i];
        cmp = flexibleCompare(testGroup.groupValue, groupValue) * spec.order;
        if (cmp === 0) {
          group = testGroup;
          break;
        } else if (cmp > 0) {
          break;
        }
      }
    } else {
      ref1 = parent.children;
      for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) {
        testGroup = ref1[i];
        if (testGroup.groupValue === groupValue) {
          group = testGroup;
          break;
        }
      }
    }
    if (!group) {
      if (this.isVGrouping) {
        group = new VRowGroup(this, spec, groupValue);
      } else {
        group = new HRowGroup(this, spec, groupValue);
      }
      parent.addChild(group, i);
    }
    return group;
  };

  ResourceTimelineView.prototype.pairSegsWithRows = function(segs) {
    var j, len, pair, pairs, pairsById, resourceId, rowObj, seg;
    pairs = [];
    pairsById = {};
    for (j = 0, len = segs.length; j < len; j++) {
      seg = segs[j];
      resourceId = seg.resourceId;
      if (resourceId) {
        rowObj = this.getResourceRow(resourceId);
        if (rowObj) {
          pair = pairsById[resourceId];
          if (!pair) {
            pair = [rowObj, []];
            pairs.push(pair);
            pairsById[resourceId] = pair;
          }
          pair[1].push(seg);
        }
      }
    }
    return pairs;
  };

  ResourceTimelineView.prototype.rowAdded = function(row) {
    var isNesting, wasNesting;
    if (row instanceof ResourceRow) {
      this.resourceRowHash[row.resource.id] = row;
      this.timeGrid.assignRowBusinessHourSegs(row);
    }
    wasNesting = this.isNesting;
    isNesting = Boolean(this.nestingCnt += row.depth ? 1 : 0);
    if (wasNesting !== isNesting) {
      this.el.toggleClass('fc-nested', isNesting);
      this.el.toggleClass('fc-flat', !isNesting);
    }
    return this.isNesting = isNesting;
  };

  ResourceTimelineView.prototype.rowRemoved = function(row) {
    var isNesting, wasNesting;
    if (row instanceof ResourceRow) {
      delete this.resourceRowHash[row.resource.id];
      this.timeGrid.destroyRowBusinessHourSegs(row);
    }
    wasNesting = this.isNesting;
    isNesting = Boolean(this.nestingCnt -= row.depth ? 1 : 0);
    if (wasNesting !== isNesting) {
      this.el.toggleClass('fc-nested', isNesting);
      this.el.toggleClass('fc-flat', !isNesting);
    }
    return this.isNesting = isNesting;
  };

  ResourceTimelineView.prototype.batchRowDepth = 0;

  ResourceTimelineView.prototype.shownRowBatch = null;

  ResourceTimelineView.prototype.hiddenRowBatch = null;

  ResourceTimelineView.prototype.batchRows = function() {
    if (!(this.batchRowDepth++)) {
      this.shownRowBatch = [];
      return this.hiddenRowBatch = [];
    }
  };

  ResourceTimelineView.prototype.unbatchRows = function() {
    if (!(--this.batchRowDepth)) {
      if (this.hiddenRowBatch.length) {
        this.rowsHidden(this.hiddenRowBatch);
      }
      if (this.shownRowBatch.length) {
        this.rowsShown(this.shownRowBatch);
      }
      this.hiddenRowBatch = null;
      return this.shownRowBatch = null;
    }
  };

  ResourceTimelineView.prototype.rowShown = function(row) {
    if (this.shownRowBatch) {
      return this.shownRowBatch.push(row);
    } else {
      return this.rowsShown([row]);
    }
  };

  ResourceTimelineView.prototype.rowHidden = function(row) {
    if (this.hiddenRowBatch) {
      return this.hiddenRowBatch.push(row);
    } else {
      return this.rowsHidden([row]);
    }
  };

  ResourceTimelineView.prototype.rowsShown = function(rows) {
    this.syncRowHeights(rows);
    return this.updateWidth();
  };

  ResourceTimelineView.prototype.rowsHidden = function(rows) {
    return this.updateWidth();
  };

  ResourceTimelineView.prototype.syncRowHeights = function(visibleRows, safe) {
    var h, h1, h2, i, innerHeights, j, k, len, len1, row;
    if (safe == null) {
      safe = false;
    }
    if (visibleRows == null) {
      visibleRows = this.getVisibleRows();
    }
    for (j = 0, len = visibleRows.length; j < len; j++) {
      row = visibleRows[j];
      row.setTrInnerHeight('');
    }
    innerHeights = (function() {
      var k, len1, results;
      results = [];
      for (k = 0, len1 = visibleRows.length; k < len1; k++) {
        row = visibleRows[k];
        h = row.getMaxTrInnerHeight();
        if (safe) {
          h += h % 2;
        }
        results.push(h);
      }
      return results;
    })();
    for (i = k = 0, len1 = visibleRows.length; k < len1; i = ++k) {
      row = visibleRows[i];
      row.setTrInnerHeight(innerHeights[i]);
    }
    if (!safe) {
      h1 = this.resourceGrid.tbodyEl.height();
      h2 = this.timeGrid.tbodyEl.height();
      if (Math.abs(h1 - h2) > 1) {
        return this.syncRowHeights(visibleRows, true);
      }
    }
  };

  ResourceTimelineView.prototype.getVisibleRows = function() {
    var j, len, ref, results, row;
    ref = this.rowHierarchy.getRows();
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      row = ref[j];
      if (row.isShown) {
        results.push(row);
      }
    }
    return results;
  };

  ResourceTimelineView.prototype.getEventRows = function() {
    var j, len, ref, results, row;
    ref = this.rowHierarchy.getRows();
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      row = ref[j];
      if (row instanceof EventRow) {
        results.push(row);
      }
    }
    return results;
  };

  ResourceTimelineView.prototype.getResourceRow = function(resourceId) {
    return this.resourceRowHash[resourceId];
  };

  ResourceTimelineView.prototype.queryResourceScroll = function() {
    var el, elBottom, j, len, ref, rowObj, scroll, scrollerTop;
    scroll = {};
    scrollerTop = this.timeGrid.bodyScroller.scrollEl.offset().top;
    ref = this.getVisibleRows();
    for (j = 0, len = ref.length; j < len; j++) {
      rowObj = ref[j];
      if (rowObj.resource) {
        el = rowObj.getTr('event');
        elBottom = el.offset().top + el.outerHeight();
        if (elBottom > scrollerTop) {
          scroll.resourceId = rowObj.resource.id;
          scroll.bottom = elBottom - scrollerTop;
          break;
        }
      }
    }
    return scroll;
  };

  ResourceTimelineView.prototype.applyResourceScroll = function(scroll) {
    var el, elBottom, innerTop, row, scrollTop;
    if (scroll.resourceId) {
      row = this.getResourceRow(scroll.resourceId);
      if (row) {
        el = row.getTr('event');
        if (el) {
          innerTop = this.timeGrid.bodyScroller.canvas.el.offset().top;
          elBottom = el.offset().top + el.outerHeight();
          scrollTop = elBottom - scroll.bottom - innerTop;
          this.timeGrid.bodyScroller.setScrollTop(scrollTop);
          return this.resourceGrid.bodyScroller.setScrollTop(scrollTop);
        }
      }
    }
  };

  ResourceTimelineView.prototype.scrollToResource = function(resource) {
    var el, innerTop, row, scrollTop;
    row = this.getResourceRow(resource.id);
    if (row) {
      el = row.getTr('event');
      if (el) {
        innerTop = this.timeGrid.bodyScroller.canvas.el.offset().top;
        scrollTop = el.offset().top - innerTop;
        this.timeGrid.bodyScroller.setScrollTop(scrollTop);
        return this.resourceGrid.bodyScroller.setScrollTop(scrollTop);
      }
    }
  };

  return ResourceTimelineView;

})(TimelineView);

ResourceTimelineGrid = (function(superClass) {
  extend(ResourceTimelineGrid, superClass);

  function ResourceTimelineGrid() {
    return ResourceTimelineGrid.__super__.constructor.apply(this, arguments);
  }

  ResourceTimelineGrid.mixin(ResourceGridMixin);

  ResourceTimelineGrid.prototype.eventRows = null;

  ResourceTimelineGrid.prototype.shownEventRows = null;

  ResourceTimelineGrid.prototype.tbodyEl = null;

  ResourceTimelineGrid.prototype.rowCoordCache = null;


  /*
  	footprint is a ResourceComponentFootprint
   */

  ResourceTimelineGrid.prototype.componentFootprintToSegs = function(footprint) {
    var calendar, j, len, resourceId, seg, segs;
    segs = ResourceTimelineGrid.__super__.componentFootprintToSegs.apply(this, arguments);
    calendar = this.view.calendar;
    resourceId = footprint.resourceId;
    if (resourceId) {
      for (j = 0, len = segs.length; j < len; j++) {
        seg = segs[j];
        seg.resource = calendar.getResourceById(resourceId);
        seg.resourceId = resourceId;
      }
    }
    return segs;
  };

  ResourceTimelineGrid.prototype.prepareHits = function() {
    var row, trArray;
    ResourceTimelineGrid.__super__.prepareHits.apply(this, arguments);
    this.eventRows = this.view.getEventRows();
    this.shownEventRows = (function() {
      var j, len, ref, results;
      ref = this.eventRows;
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        row = ref[j];
        if (row.isShown) {
          results.push(row);
        }
      }
      return results;
    }).call(this);
    trArray = (function() {
      var j, len, ref, results;
      ref = this.shownEventRows;
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        row = ref[j];
        results.push(row.getTr('event')[0]);
      }
      return results;
    }).call(this);
    this.rowCoordCache = new CoordCache({
      els: trArray,
      isVertical: true
    });
    return this.rowCoordCache.build();
  };

  ResourceTimelineGrid.prototype.releaseHits = function() {
    ResourceTimelineGrid.__super__.releaseHits.apply(this, arguments);
    this.eventRows = null;
    this.shownEventRows = null;
    return this.rowCoordCache.clear();
  };

  ResourceTimelineGrid.prototype.queryHit = function(leftOffset, topOffset) {
    var rowIndex, simpleHit;
    simpleHit = ResourceTimelineGrid.__super__.queryHit.apply(this, arguments);
    if (simpleHit) {
      rowIndex = this.rowCoordCache.getVerticalIndex(topOffset);
      if (rowIndex != null) {
        return {
          resourceId: this.shownEventRows[rowIndex].resource.id,
          snap: simpleHit.snap,
          component: this,
          left: simpleHit.left,
          right: simpleHit.right,
          top: this.rowCoordCache.getTopOffset(rowIndex),
          bottom: this.rowCoordCache.getBottomOffset(rowIndex)
        };
      }
    }
  };

  ResourceTimelineGrid.prototype.getHitFootprint = function(hit) {
    var componentFootprint;
    componentFootprint = ResourceTimelineGrid.__super__.getHitFootprint.apply(this, arguments);
    return new ResourceComponentFootprint(componentFootprint.unzonedRange, componentFootprint.isAllDay, hit.resourceId);
  };

  ResourceTimelineGrid.prototype.getHitEl = function(hit) {
    return this.getSnapEl(hit.snap);
  };

  ResourceTimelineGrid.prototype.renderSkeleton = function() {
    var rowContainerEl, theme;
    ResourceTimelineGrid.__super__.renderSkeleton.apply(this, arguments);
    theme = this.view.calendar.theme;
    this.segContainerEl.remove();
    this.segContainerEl = null;
    rowContainerEl = $('<div class="fc-rows"> <table class="' + theme.getClass('tableGrid') + '"> <tbody/> </table> </div>').appendTo(this.bodyScroller.canvas.contentEl);
    return this.tbodyEl = rowContainerEl.find('tbody');
  };

  ResourceTimelineGrid.prototype.renderFgSegs = function(segs) {
    var containerObj, containerSegs, j, len, pair, pairs, visiblePairs;
    segs = this.renderFgSegEls(segs);
    pairs = this.view.pairSegsWithRows(segs);
    visiblePairs = [];
    for (j = 0, len = pairs.length; j < len; j++) {
      pair = pairs[j];
      containerObj = pair[0], containerSegs = pair[1];
      containerObj.fgSegs = containerSegs;
      if (containerObj.isShown) {
        containerObj.isSegsRendered = true;
        visiblePairs.push(pair);
      }
    }
    this.renderFgSegsInContainers(visiblePairs);
    this.updateSegFollowers(segs);
    return segs;
  };

  ResourceTimelineGrid.prototype.unrenderFgSegs = function() {
    var eventRow, eventRows, j, len;
    this.clearSegFollowers();
    eventRows = this.view.getEventRows();
    for (j = 0, len = eventRows.length; j < len; j++) {
      eventRow = eventRows[j];
      eventRow.fgSegs = null;
      eventRow.isSegsRendered = false;
    }
    return this.unrenderFgContainers(eventRows);
  };

  ResourceTimelineGrid.prototype.unrenderBgSegs = function() {
    var eventRow, eventRows, j, len, results;
    ResourceTimelineGrid.__super__.unrenderBgSegs.apply(this, arguments);
    eventRows = this.view.getEventRows();
    results = [];
    for (j = 0, len = eventRows.length; j < len; j++) {
      eventRow = eventRows[j];
      results.push(eventRow.bgSegs = null);
    }
    return results;
  };

  ResourceTimelineGrid.prototype.rowCntWithCustomBusinessHours = 0;

  ResourceTimelineGrid.prototype.renderBusinessHours = function() {
    if (this.rowCntWithCustomBusinessHours) {
      return this.ensureIndividualBusinessHours();
    } else {
      return ResourceTimelineGrid.__super__.renderBusinessHours.apply(this, arguments);
    }
  };

  ResourceTimelineGrid.prototype.unrenderBusinessHours = function() {
    if (this.rowCntWithCustomBusinessHours) {
      return this.clearIndividualBusinessHours();
    } else {
      return ResourceTimelineGrid.__super__.unrenderBusinessHours.apply(this, arguments);
    }
  };


  /*
  	Ensures that all rows have their individual business hours DISPLAYED.
   */

  ResourceTimelineGrid.prototype.ensureIndividualBusinessHours = function() {
    var j, len, ref, results, row;
    ref = this.view.getEventRows();
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      row = ref[j];
      if (this.view.has('dateProfile') && !row.businessHourSegs) {
        this.populateRowBusinessHoursSegs(row);
      }
      if (row.isShown) {
        results.push(row.ensureBusinessHourSegsRendered());
      } else {
        results.push(void 0);
      }
    }
    return results;
  };


  /*
  	Ensures that all rows have their individual business hours CLEARED.
   */

  ResourceTimelineGrid.prototype.clearIndividualBusinessHours = function() {
    var j, len, ref, results, row;
    ref = this.view.getEventRows();
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      row = ref[j];
      results.push(row.clearBusinessHourSegs());
    }
    return results;
  };


  /*
  	Called when a row has been added to the tree data structure, but before it's rendered.
  	Computes and assigns business hour data *if necessary*. To be rendered soon after.
   */

  ResourceTimelineGrid.prototype.assignRowBusinessHourSegs = function(row) {
    if (row.resource.businessHours) {
      if (!this.rowCntWithCustomBusinessHours) {
        TimelineGrid.prototype.unrenderBusinessHours.call(this);
        this.ensureIndividualBusinessHours();
      }
      this.rowCntWithCustomBusinessHours += 1;
    }
    if (this.view.has('dateProfile') && this.rowCntWithCustomBusinessHours) {
      return this.populateRowBusinessHoursSegs(row);
    }
  };


  /*
  	Called when a row has been removed from the tree data structure.
  	Unrenders the row's segs and, if necessary, forces businessHours back to generic rendering.
   */

  ResourceTimelineGrid.prototype.destroyRowBusinessHourSegs = function(row) {
    row.clearBusinessHourSegs();
    if (row.resource.businessHours) {
      this.rowCntWithCustomBusinessHours -= 1;
      if (!this.rowCntWithCustomBusinessHours) {
        this.clearIndividualBusinessHours();
        return TimelineGrid.prototype.renderBusinessHours.call(this);
      }
    }
  };


  /*
  	Compute and assign to row.businessHourSegs unconditionally
   */

  ResourceTimelineGrid.prototype.populateRowBusinessHoursSegs = function(row) {
    var businessHourEventFootprints, businessHourSegs;
    businessHourEventFootprints = this._buildBusinessHourEventFootprints(!this.isTimeScale, row.resource.businessHours || this.opt('businessHours'));
    businessHourSegs = this.eventFootprintsToSegs(businessHourEventFootprints);
    businessHourSegs = this.renderFillSegEls('businessHours', businessHourSegs);
    row.businessHourSegs = businessHourSegs;
  };

  ResourceTimelineGrid.prototype.renderFill = function(type, segs, className) {
    var j, k, len, len1, nonResourceSegs, pair, pairs, resourceSegs, rowObj, rowSegs, seg, visiblePairs;
    segs = this.renderFillSegEls(type, segs);
    resourceSegs = [];
    nonResourceSegs = [];
    for (j = 0, len = segs.length; j < len; j++) {
      seg = segs[j];
      if (seg.resourceId) {
        resourceSegs.push(seg);
      } else {
        nonResourceSegs.push(seg);
      }
    }
    pairs = this.view.pairSegsWithRows(resourceSegs);
    visiblePairs = [];
    for (k = 0, len1 = pairs.length; k < len1; k++) {
      pair = pairs[k];
      rowObj = pair[0], rowSegs = pair[1];
      if (type === 'bgEvent') {
        rowObj.bgSegs = rowSegs;
      }
      if (rowObj.isShown) {
        visiblePairs.push(pair);
      }
    }
    if (nonResourceSegs.length) {
      visiblePairs.unshift([this, nonResourceSegs]);
    }
    this.renderFillInContainers(type, visiblePairs, className);
    return segs;
  };

  ResourceTimelineGrid.prototype.renderHelperEventFootprintEls = function(eventFootprints, sourceSeg) {
    var pairs, segs;
    segs = this.eventFootprintsToSegs(eventFootprints);
    segs = this.renderFgSegEls(segs);
    pairs = this.view.pairSegsWithRows(segs);
    return this.renderHelperSegsInContainers(pairs, sourceSeg);
  };

  return ResourceTimelineGrid;

})(TimelineGrid);

COL_MIN_WIDTH = 30;

Spreadsheet = (function() {
  Spreadsheet.prototype.view = null;

  Spreadsheet.prototype.headEl = null;

  Spreadsheet.prototype.el = null;

  Spreadsheet.prototype.tbodyEl = null;

  Spreadsheet.prototype.headScroller = null;

  Spreadsheet.prototype.bodyScroller = null;

  Spreadsheet.prototype.joiner = null;

  function Spreadsheet(view1) {
    var colSpec;
    this.view = view1;
    this.isRTL = this.view.opt('isRTL');
    this.givenColWidths = this.colWidths = (function() {
      var j, len, ref, results;
      ref = this.view.colSpecs;
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        colSpec = ref[j];
        results.push(colSpec.width);
      }
      return results;
    }).call(this);
  }

  Spreadsheet.prototype.colGroupHtml = '';

  Spreadsheet.prototype.headTable = null;

  Spreadsheet.prototype.headColEls = null;

  Spreadsheet.prototype.headCellEls = null;

  Spreadsheet.prototype.bodyColEls = null;

  Spreadsheet.prototype.bodyTable = null;

  Spreadsheet.prototype.renderSkeleton = function() {
    var theme;
    theme = this.view.calendar.theme;
    this.headScroller = new ClippedScroller({
      overflowX: 'clipped-scroll',
      overflowY: 'hidden'
    });
    this.headScroller.canvas = new ScrollerCanvas();
    this.headScroller.render();
    this.headScroller.canvas.contentEl.html(this.renderHeadHtml());
    this.headEl.append(this.headScroller.el);
    this.bodyScroller = new ClippedScroller({
      overflowY: 'clipped-scroll'
    });
    this.bodyScroller.canvas = new ScrollerCanvas();
    this.bodyScroller.render();
    this.bodyScroller.canvas.contentEl.html('<div class="fc-rows"> <table class="' + theme.getClass('tableGrid') + '">' + this.colGroupHtml + '<tbody/> </table> </div>');
    this.tbodyEl = this.bodyScroller.canvas.contentEl.find('tbody');
    this.el.append(this.bodyScroller.el);
    this.joiner = new ScrollJoiner('horizontal', [this.headScroller, this.bodyScroller]);
    this.headTable = this.headEl.find('table');
    this.headColEls = this.headEl.find('col');
    this.headCellEls = this.headScroller.canvas.contentEl.find('tr:last-child th');
    this.bodyColEls = this.el.find('col');
    this.bodyTable = this.el.find('table');
    this.colMinWidths = this.computeColMinWidths();
    this.applyColWidths();
    return this.initColResizing();
  };

  Spreadsheet.prototype.renderHeadHtml = function() {
    var colGroupHtml, colSpecs, html, i, isLast, isMainCol, j, k, len, len1, o, theme;
    theme = this.view.calendar.theme;
    colSpecs = this.view.colSpecs;
    html = '<table class="' + theme.getClass('tableGrid') + '">';
    colGroupHtml = '<colgroup>';
    for (j = 0, len = colSpecs.length; j < len; j++) {
      o = colSpecs[j];
      if (o.isMain) {
        colGroupHtml += '<col class="fc-main-col"/>';
      } else {
        colGroupHtml += '<col/>';
      }
    }
    colGroupHtml += '</colgroup>';
    this.colGroupHtml = colGroupHtml;
    html += colGroupHtml;
    html += '<tbody>';
    if (this.view.superHeaderText) {
      html += '<tr class="fc-super">' + '<th class="' + theme.getClass('widgetHeader') + '" colspan="' + colSpecs.length + '">' + '<div class="fc-cell-content">' + '<span class="fc-cell-text">' + htmlEscape(this.view.superHeaderText) + '</span>' + '</div>' + '</th>' + '</tr>';
    }
    html += '<tr>';
    isMainCol = true;
    for (i = k = 0, len1 = colSpecs.length; k < len1; i = ++k) {
      o = colSpecs[i];
      isLast = i === colSpecs.length - 1;
      html += '<th class="' + theme.getClass('widgetHeader') + '">' + '<div>' + '<div class="fc-cell-content">' + (o.isMain ? '<span class="fc-expander-space">' + '<span class="fc-icon"></span>' + '</span>' : '') + '<span class="fc-cell-text">' + htmlEscape(o.labelText || '') + '</span>' + '</div>' + (!isLast ? '<div class="fc-col-resizer"></div>' : '') + '</div>' + '</th>';
    }
    html += '</tr>';
    html += '</tbody></table>';
    return html;
  };

  Spreadsheet.prototype.givenColWidths = null;

  Spreadsheet.prototype.colWidths = null;

  Spreadsheet.prototype.colMinWidths = null;

  Spreadsheet.prototype.tableWidth = null;

  Spreadsheet.prototype.tableMinWidth = null;

  Spreadsheet.prototype.initColResizing = function() {
    return this.headEl.find('th .fc-col-resizer').each((function(_this) {
      return function(i, resizerEl) {
        resizerEl = $(resizerEl);
        return resizerEl.on('mousedown', function(ev) {
          return _this.colResizeMousedown(i, ev, resizerEl);
        });
      };
    })(this));
  };

  Spreadsheet.prototype.colResizeMousedown = function(i, ev, resizerEl) {
    var colWidths, dragListener, minWidth, origColWidth;
    colWidths = this.colWidths = this.queryColWidths();
    colWidths.pop();
    colWidths.push(null);
    origColWidth = colWidths[i];
    minWidth = Math.min(this.colMinWidths[i], COL_MIN_WIDTH);
    dragListener = new DragListener({
      dragStart: (function(_this) {
        return function() {
          return resizerEl.addClass('fc-active');
        };
      })(this),
      drag: (function(_this) {
        return function(dx, dy) {
          var width;
          width = origColWidth + (_this.isRTL ? -dx : dx);
          width = Math.max(width, minWidth);
          colWidths[i] = width;
          return _this.applyColWidths();
        };
      })(this),
      dragEnd: (function(_this) {
        return function() {
          return resizerEl.removeClass('fc-active');
        };
      })(this)
    });
    return dragListener.startInteraction(ev);
  };

  Spreadsheet.prototype.applyColWidths = function() {
    var allNumbers, anyPercentages, colMinWidths, colWidth, colWidths, cssWidth, cssWidths, defaultCssWidth, i, j, k, l, len, len1, len2, tableMinWidth, total;
    colMinWidths = this.colMinWidths;
    colWidths = this.colWidths;
    allNumbers = true;
    anyPercentages = false;
    total = 0;
    for (j = 0, len = colWidths.length; j < len; j++) {
      colWidth = colWidths[j];
      if (typeof colWidth === 'number') {
        total += colWidth;
      } else {
        allNumbers = false;
        if (colWidth) {
          anyPercentages = true;
        }
      }
    }
    defaultCssWidth = anyPercentages && !this.view.isHGrouping ? 'auto' : '';
    cssWidths = (function() {
      var k, len1, results;
      results = [];
      for (i = k = 0, len1 = colWidths.length; k < len1; i = ++k) {
        colWidth = colWidths[i];
        results.push(colWidth != null ? colWidth : defaultCssWidth);
      }
      return results;
    })();
    tableMinWidth = 0;
    for (i = k = 0, len1 = cssWidths.length; k < len1; i = ++k) {
      cssWidth = cssWidths[i];
      tableMinWidth += typeof cssWidth === 'number' ? cssWidth : colMinWidths[i];
    }
    for (i = l = 0, len2 = cssWidths.length; l < len2; i = ++l) {
      cssWidth = cssWidths[i];
      this.headColEls.eq(i).width(cssWidth);
      this.bodyColEls.eq(i).width(cssWidth);
    }
    this.headScroller.canvas.setMinWidth(tableMinWidth);
    this.bodyScroller.canvas.setMinWidth(tableMinWidth);
    this.tableMinWidth = tableMinWidth;
    return this.tableWidth = allNumbers ? total : void 0;
  };

  Spreadsheet.prototype.computeColMinWidths = function() {
    var i, j, len, ref, results, width;
    ref = this.givenColWidths;
    results = [];
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      width = ref[i];
      if (typeof width === 'number') {
        results.push(width);
      } else {
        results.push(parseInt(this.headColEls.eq(i).css('min-width')) || COL_MIN_WIDTH);
      }
    }
    return results;
  };

  Spreadsheet.prototype.queryColWidths = function() {
    var j, len, node, ref, results;
    ref = this.headCellEls;
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      node = ref[j];
      results.push($(node).outerWidth());
    }
    return results;
  };

  Spreadsheet.prototype.updateWidth = function() {
    this.headScroller.updateSize();
    this.bodyScroller.updateSize();
    this.joiner.update();
    if (this.follower) {
      return this.follower.update();
    }
  };

  Spreadsheet.prototype.headHeight = function() {
    var table;
    table = this.headScroller.canvas.contentEl.find('table');
    return table.height.apply(table, arguments);
  };

  return Spreadsheet;

})();


/*
An abstract node in a row-hierarchy tree.
May be a self-contained single row, a row with subrows,
OR a grouping of rows without its own distinct row.
 */

RowParent = (function() {
  RowParent.prototype.view = null;

  RowParent.prototype.parent = null;

  RowParent.prototype.prevSibling = null;

  RowParent.prototype.children = null;

  RowParent.prototype.depth = 0;

  RowParent.prototype.hasOwnRow = false;

  RowParent.prototype.trHash = null;

  RowParent.prototype.trs = null;

  RowParent.prototype.isRendered = false;

  RowParent.prototype.isExpanded = true;

  RowParent.prototype.isShown = false;

  function RowParent(view1) {
    this.view = view1;
    this.children = [];
    this.trHash = {};
    this.trs = $();
  }


  /*
  	Adds the given node as a child.
  	Will be inserted at the `index`. If not given, will be appended to the end.
   */

  RowParent.prototype.addChild = function(child, index) {
    var children, j, len, node, ref;
    child.remove();
    children = this.children;
    if (index != null) {
      children.splice(index, 0, child);
    } else {
      index = children.length;
      children.push(child);
    }
    child.prevSibling = index > 0 ? children[index - 1] : null;
    if (index < children.length - 1) {
      children[index + 1].prevSibling = child;
    }
    child.parent = this;
    child.depth = this.depth + (this.hasOwnRow ? 1 : 0);
    ref = child.getNodes();
    for (j = 0, len = ref.length; j < len; j++) {
      node = ref[j];
      node.added();
    }
    if (this.isShown && this.isExpanded) {
      return child.show();
    }
  };


  /*
  	Removes the given child from the node. Assumes it is a direct child.
  	If not a direct child, returns false and nothing happens.
  	Unrenders the child and triggers handlers.
   */

  RowParent.prototype.removeChild = function(child) {
    var children, i, isFound, j, k, len, len1, ref, row, testChild;
    children = this.children;
    isFound = false;
    for (i = j = 0, len = children.length; j < len; i = ++j) {
      testChild = children[i];
      if (testChild === child) {
        isFound = true;
        break;
      }
    }
    if (!isFound) {
      return false;
    } else {
      if (i < children.length - 1) {
        children[i + 1].prevSibling = child.prevSibling;
      }
      children.splice(i, 1);
      child.recursivelyUnrender();
      ref = child.getNodes();
      for (k = 0, len1 = ref.length; k < len1; k++) {
        row = ref[k];
        row.removed();
      }
      child.parent = null;
      child.prevSibling = null;
      return child;
    }
  };


  /*
  	Removes all of the node's children from the hierarchy. Unrenders them and triggers callbacks.
  	NOTE: batchRows/unbatchRows should probably be called before this happens :(
   */

  RowParent.prototype.removeChildren = function() {
    var child, j, k, len, len1, ref, ref1;
    ref = this.children;
    for (j = 0, len = ref.length; j < len; j++) {
      child = ref[j];
      child.recursivelyUnrender();
    }
    ref1 = this.getDescendants();
    for (k = 0, len1 = ref1.length; k < len1; k++) {
      child = ref1[k];
      child.removed();
    }
    return this.children = [];
  };


  /*
  	Removes this node from its parent
   */

  RowParent.prototype.remove = function() {
    if (this.parent) {
      return this.parent.removeChild(this);
    }
  };


  /*
  	Gets the last direct child node
   */

  RowParent.prototype.getLastChild = function() {
    var children;
    children = this.children;
    return children[children.length - 1];
  };


  /*
  	Walks backward in the hierarchy to find the previous row leaf node.
  	When looking at the hierarchy in a flat linear fashion, this is the revealed row just before the current.
   */

  RowParent.prototype.getPrevRow = function() {
    var lastChild, node;
    node = this;
    while (node) {
      if (node.prevSibling) {
        node = node.prevSibling;
        while ((lastChild = node.getLastChild())) {
          node = lastChild;
        }
      } else {
        node = node.parent;
      }
      if (node && node.hasOwnRow && node.isShown) {
        return node;
      }
    }
    return null;
  };


  /*
  	Returns the first node in the subtree that has a revealed row
   */

  RowParent.prototype.getLeadingRow = function() {
    if (this.hasOwnRow) {
      return this;
    } else if (this.isExpanded && this.children.length) {
      return this.children[0].getLeadingRow();
    }
  };


  /*
  	Generates a flat array containing all the row-nodes of the subtree. Descendants + self
   */

  RowParent.prototype.getRows = function(batchArray) {
    var child, j, len, ref;
    if (batchArray == null) {
      batchArray = [];
    }
    if (this.hasOwnRow) {
      batchArray.push(this);
    }
    ref = this.children;
    for (j = 0, len = ref.length; j < len; j++) {
      child = ref[j];
      child.getRows(batchArray);
    }
    return batchArray;
  };


  /*
  	Generates a flat array containing all the nodes (row/non-row) of the subtree. Descendants + self
   */

  RowParent.prototype.getNodes = function(batchArray) {
    var child, j, len, ref;
    if (batchArray == null) {
      batchArray = [];
    }
    batchArray.push(this);
    ref = this.children;
    for (j = 0, len = ref.length; j < len; j++) {
      child = ref[j];
      child.getNodes(batchArray);
    }
    return batchArray;
  };


  /*
  	Generates a flat array containing all the descendant nodes the current node
   */

  RowParent.prototype.getDescendants = function() {
    var batchArray, child, j, len, ref;
    batchArray = [];
    ref = this.children;
    for (j = 0, len = ref.length; j < len; j++) {
      child = ref[j];
      child.getNodes(batchArray);
    }
    return batchArray;
  };


  /*
  	Builds and populates the TRs for each row type. Inserts them into the DOM.
  	Does this only for this single row. Not recursive. If not a row (hasOwnRow=false), does not render anything.
  	PRECONDITION: assumes the parent has already been rendered.
   */

  RowParent.prototype.render = function() {
    var prevRow, ref, renderMethodName, tbody, tr, trNodes, type;
    this.trHash = {};
    trNodes = [];
    if (this.hasOwnRow) {
      prevRow = this.getPrevRow();
      ref = this.view.tbodyHash;
      for (type in ref) {
        tbody = ref[type];
        tr = $('<tr/>');
        this.trHash[type] = tr;
        trNodes.push(tr[0]);
        renderMethodName = 'render' + capitaliseFirstLetter(type) + 'Content';
        if (this[renderMethodName]) {
          this[renderMethodName](tr);
        }
        if (prevRow) {
          prevRow.trHash[type].after(tr);
        } else {
          tbody.prepend(tr);
        }
      }
    }
    this.trs = $(trNodes).on('click', '.fc-expander', proxy(this, 'toggleExpanded'));
    return this.isRendered = true;
  };


  /*
  	Unpopulates and removes all of this row's TRs from the DOM. Only for this single row. Not recursive.
  	Will trigger "hidden".
   */

  RowParent.prototype.unrender = function() {
    var ref, tr, type, unrenderMethodName;
    if (this.isRendered) {
      ref = this.trHash;
      for (type in ref) {
        tr = ref[type];
        unrenderMethodName = 'unrender' + capitaliseFirstLetter(type) + 'Content';
        if (this[unrenderMethodName]) {
          this[unrenderMethodName](tr);
        }
      }
      this.trHash = {};
      this.trs.remove();
      this.trs = $();
      this.isRendered = false;
      this.isShown = false;
      return this.hidden();
    }
  };


  /*
  	Like unrender(), but does it for this row AND all descendants.
  	NOTE: batchRows/unbatchRows should probably be called before this happens :(
   */

  RowParent.prototype.recursivelyUnrender = function() {
    var child, j, len, ref, results;
    this.unrender();
    ref = this.children;
    results = [];
    for (j = 0, len = ref.length; j < len; j++) {
      child = ref[j];
      results.push(child.recursivelyUnrender());
    }
    return results;
  };


  /*
  	A simple getter for retrieving a TR jQuery object of a certain row type
   */

  RowParent.prototype.getTr = function(type) {
    return this.trHash[type];
  };


  /*
  	Renders this row if not already rendered, making sure it is visible.
  	Also renders descendants of this subtree, based on whether they are expanded or not.
  	NOTE: If called externally, batchRows/unbatchRows should probably be called before this happens :(
   */

  RowParent.prototype.show = function() {
    var child, j, len, ref, results;
    if (!this.isShown) {
      if (!this.isRendered) {
        this.render();
      } else {
        this.trs.css('display', '');
      }
      if (this.ensureSegsRendered) {
        this.ensureSegsRendered();
      }
      if (this.isExpanded) {
        this.indicateExpanded();
      } else {
        this.indicateCollapsed();
      }
      this.isShown = true;
      this.shown();
      if (this.isExpanded) {
        ref = this.children;
        results = [];
        for (j = 0, len = ref.length; j < len; j++) {
          child = ref[j];
          results.push(child.show());
        }
        return results;
      }
    }
  };


  /*
  	Temporarily hides this node's TRs (if applicable) as well as all nodes in the subtree
   */

  RowParent.prototype.hide = function() {
    var child, j, len, ref, results;
    if (this.isShown) {
      if (this.isRendered) {
        this.trs.hide();
      }
      this.isShown = false;
      this.hidden();
      if (this.isExpanded) {
        ref = this.children;
        results = [];
        for (j = 0, len = ref.length; j < len; j++) {
          child = ref[j];
          results.push(child.hide());
        }
        return results;
      }
    }
  };


  /*
  	Reveals this node's children if they have not already been revealed. Changes any expander icon.
   */

  RowParent.prototype.expand = function() {
    var child, j, len, ref;
    if (!this.isExpanded) {
      this.isExpanded = true;
      this.indicateExpanded();
      this.view.batchRows();
      ref = this.children;
      for (j = 0, len = ref.length; j < len; j++) {
        child = ref[j];
        child.show();
      }
      this.view.unbatchRows();
      return this.animateExpand();
    }
  };


  /*
  	Hides this node's children if they are not already hidden. Changes any expander icon.
   */

  RowParent.prototype.collapse = function() {
    var child, j, len, ref;
    if (this.isExpanded) {
      this.isExpanded = false;
      this.indicateCollapsed();
      this.view.batchRows();
      ref = this.children;
      for (j = 0, len = ref.length; j < len; j++) {
        child = ref[j];
        child.hide();
      }
      return this.view.unbatchRows();
    }
  };


  /*
  	Switches between expanded/collapsed states
   */

  RowParent.prototype.toggleExpanded = function() {
    if (this.isExpanded) {
      return this.collapse();
    } else {
      return this.expand();
    }
  };


  /*
  	Changes the expander icon to the "expanded" state
   */

  RowParent.prototype.indicateExpanded = function() {
    return this.trs.find('.fc-expander .fc-icon').removeClass(this.getCollapsedIcon()).addClass(this.getExpandedIcon());
  };


  /*
  	Changes the expander icon to the "collapsed" state
   */

  RowParent.prototype.indicateCollapsed = function() {
    return this.trs.find('.fc-expander .fc-icon').removeClass(this.getExpandedIcon()).addClass(this.getCollapsedIcon());
  };


  /*
   */

  RowParent.prototype.enableExpanding = function() {
    return this.trs.find('.fc-expander-space').addClass('fc-expander');
  };


  /*
   */

  RowParent.prototype.disableExpanding = function() {
    return this.trs.find('.fc-expander-space').removeClass('fc-expander').find('.fc-icon').removeClass(this.getExpandedIcon()).removeClass(this.getCollapsedIcon());
  };

  RowParent.prototype.getExpandedIcon = function() {
    return 'fc-icon-down-triangle';
  };

  RowParent.prototype.getCollapsedIcon = function() {
    var dir;
    dir = this.view.isRTL ? 'left' : 'right';
    return 'fc-icon-' + dir + '-triangle';
  };


  /*
  	Causes a slide-down CSS transition to demonstrate that the expand has happened
   */

  RowParent.prototype.animateExpand = function() {
    var ref, ref1, trs;
    trs = (ref = this.children[0]) != null ? (ref1 = ref.getLeadingRow()) != null ? ref1.trs : void 0 : void 0;
    if (trs) {
      trs.addClass('fc-collapsed');
      setTimeout(function() {
        trs.addClass('fc-transitioning');
        return trs.removeClass('fc-collapsed');
      });
      return trs.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function() {
        return trs.removeClass('fc-transitioning');
      });
    }
  };


  /*
  	Find each TRs "inner div" (div within first cell). This div controls each TRs height.
  	Returns the max pixel height.
   */

  RowParent.prototype.getMaxTrInnerHeight = function() {
    var max;
    max = 0;
    $.each(this.trHash, (function(_this) {
      return function(type, tr) {
        var innerEl;
        innerEl = getOwnCells(tr).find('> div:not(.fc-cell-content):first');
        return max = Math.max(innerEl.height(), max);
      };
    })(this));
    return max;
  };


  /*
  	Find each TRs "inner div" and sets all of their heights to the same value.
   */

  RowParent.prototype.setTrInnerHeight = function(height) {
    return $.each(this.trHash, (function(_this) {
      return function(type, tr) {
        return getOwnCells(tr).find('> div:not(.fc-cell-content):first').height(height);
      };
    })(this));
  };


  /*
  	Triggered when the current node has been shown (either freshly rendered or re-shown)
  	when it had previously been unrendered or hidden. `shown` does not bubble up the hierarchy.
   */

  RowParent.prototype.shown = function() {
    if (this.hasOwnRow) {
      return this.rowShown(this);
    }
  };


  /*
  	Triggered when the current node has been hidden (either temporarily or permanently)
  	when it had previously been shown. `hidden` does not bubble up the hierarchy.
   */

  RowParent.prototype.hidden = function() {
    if (this.hasOwnRow) {
      return this.rowHidden(this);
    }
  };


  /*
  	Just like `shown`, but only triggered for nodes that are actual rows. Bubbles up the hierarchy.
   */

  RowParent.prototype.rowShown = function(row) {
    return (this.parent || this.view).rowShown(row);
  };


  /*
  	Just like `hidden`, but only triggered for nodes that are actual rows. Bubbles up the hierarchy.
   */

  RowParent.prototype.rowHidden = function(row) {
    return (this.parent || this.view).rowHidden(row);
  };


  /*
  	Triggered when the current node has been added to the hierarchy. `added` does not bubble up.
   */

  RowParent.prototype.added = function() {
    if (this.hasOwnRow) {
      return this.rowAdded(this);
    }
  };


  /*
  	Triggered when the current node has been removed from the hierarchy. `removed` does not bubble up.
   */

  RowParent.prototype.removed = function() {
    if (this.hasOwnRow) {
      return this.rowRemoved(this);
    }
  };


  /*
  	Just like `added`, but only triggered for nodes that are actual rows. Bubbles up the hierarchy.
   */

  RowParent.prototype.rowAdded = function(row) {
    return (this.parent || this.view).rowAdded(row);
  };


  /*
  	Just like `removed`, but only triggered for nodes that are actual rows. Bubbles up the hierarchy.
   */

  RowParent.prototype.rowRemoved = function(row) {
    return (this.parent || this.view).rowRemoved(row);
  };

  return RowParent;

})();


/*
An abstract node in a row-hierarchy tree that contains other nodes.
Will have some sort of rendered label indicating the grouping,
up to the subclass for determining what to do with it.
 */

RowGroup = (function(superClass) {
  extend(RowGroup, superClass);

  RowGroup.prototype.groupSpec = null;

  RowGroup.prototype.groupValue = null;

  function RowGroup(view, groupSpec1, groupValue1) {
    this.groupSpec = groupSpec1;
    this.groupValue = groupValue1;
    RowGroup.__super__.constructor.apply(this, arguments);
  }


  /*
  	Called when this row (if it renders a row) or a subrow is removed
   */

  RowGroup.prototype.rowRemoved = function(row) {
    RowGroup.__super__.rowRemoved.apply(this, arguments);
    if (row !== this && !this.children.length) {
      return this.remove();
    }
  };


  /*
  	Renders the content wrapper element that will be inserted into this row's TD cell
   */

  RowGroup.prototype.renderGroupContentEl = function() {
    var contentEl, filter;
    contentEl = $('<div class="fc-cell-content" />').append(this.renderGroupTextEl());
    filter = this.groupSpec.render;
    if (typeof filter === 'function') {
      contentEl = filter(contentEl, this.groupValue) || contentEl;
    }
    return contentEl;
  };


  /*
  	Renders the text span element that will be inserted into this row's TD cell.
  	Goes within the content element.
   */

  RowGroup.prototype.renderGroupTextEl = function() {
    var filter, text;
    text = this.groupValue || '';
    filter = this.groupSpec.text;
    if (typeof filter === 'function') {
      text = filter(text) || text;
    }
    return $('<span class="fc-cell-text" />').text(text);
  };

  return RowGroup;

})(RowParent);


/*
A row grouping that renders as a single solid row that spans width-wise (like a horizontal rule)
 */

HRowGroup = (function(superClass) {
  extend(HRowGroup, superClass);

  function HRowGroup() {
    return HRowGroup.__super__.constructor.apply(this, arguments);
  }

  HRowGroup.prototype.hasOwnRow = true;


  /*
  	Renders this row's TR for the "spreadsheet" quadrant, the area with info about each resource
   */

  HRowGroup.prototype.renderSpreadsheetContent = function(tr) {
    var contentEl;
    contentEl = this.renderGroupContentEl();
    contentEl.prepend('<span class="fc-expander">' + '<span class="fc-icon"></span>' + '</span>');
    return $('<td class="fc-divider" />').attr('colspan', this.view.colSpecs.length).append($('<div/>').append(contentEl)).appendTo(tr);
  };


  /*
  	Renders this row's TR for the quadrant that contains a resource's events
   */

  HRowGroup.prototype.renderEventContent = function(tr) {
    return tr.append('<td class="fc-divider"> <div/> </td>');
  };

  return HRowGroup;

})(RowGroup);


/*
A row grouping that renders as a tall multi-cell vertical span in the "spreadsheet" area
 */

VRowGroup = (function(superClass) {
  extend(VRowGroup, superClass);

  function VRowGroup() {
    return VRowGroup.__super__.constructor.apply(this, arguments);
  }

  VRowGroup.prototype.rowspan = 0;

  VRowGroup.prototype.leadingTr = null;

  VRowGroup.prototype.groupTd = null;


  /*
  	Called when a row somewhere within the grouping is shown
   */

  VRowGroup.prototype.rowShown = function(row) {
    this.rowspan += 1;
    this.renderRowspan();
    return VRowGroup.__super__.rowShown.apply(this, arguments);
  };


  /*
  	Called when a row somewhere within the grouping is hidden
   */

  VRowGroup.prototype.rowHidden = function(row) {
    this.rowspan -= 1;
    this.renderRowspan();
    return VRowGroup.__super__.rowHidden.apply(this, arguments);
  };


  /*
  	Makes sure the groupTd has the correct rowspan / place in the DOM.
  	PRECONDITION: in the case of multiple group nesting, a child's renderRowspan()
  	will be called before the parent's renderRowspan().
   */

  VRowGroup.prototype.renderRowspan = function() {
    var leadingTr, theme;
    theme = this.view.calendar.theme;
    if (this.rowspan) {
      if (!this.groupTd) {
        this.groupTd = $('<td class="' + theme.getClass('widgetContent') + '"/>').append(this.renderGroupContentEl());
      }
      this.groupTd.attr('rowspan', this.rowspan);
      leadingTr = this.getLeadingRow().getTr('spreadsheet');
      if (leadingTr !== this.leadingTr) {
        if (leadingTr) {
          leadingTr.prepend(this.groupTd);
        }
        return this.leadingTr = leadingTr;
      }
    } else {
      if (this.groupTd) {
        this.groupTd.remove();
        this.groupTd = null;
      }
      return this.leadingTr = null;
    }
  };

  return VRowGroup;

})(RowGroup);

EventRow = (function(superClass) {
  extend(EventRow, superClass);

  function EventRow() {
    return EventRow.__super__.constructor.apply(this, arguments);
  }

  EventRow.prototype.hasOwnRow = true;

  EventRow.prototype.segContainerEl = null;

  EventRow.prototype.segContainerHeight = null;

  EventRow.prototype.innerEl = null;

  EventRow.prototype.bgSegContainerEl = null;

  EventRow.prototype.isSegsRendered = false;

  EventRow.prototype.isBusinessHourSegsRendered = false;

  EventRow.prototype.businessHourSegs = null;

  EventRow.prototype.bgSegs = null;

  EventRow.prototype.fgSegs = null;

  EventRow.prototype.renderEventContent = function(tr) {
    var theme;
    theme = this.view.calendar.theme;
    tr.html('<td class="' + theme.getClass('widgetContent') + '"> <div> <div class="fc-event-container" /> </div> </td>');
    this.segContainerEl = tr.find('.fc-event-container');
    this.innerEl = this.bgSegContainerEl = tr.find('td > div');
    return this.ensureSegsRendered();
  };

  EventRow.prototype.ensureSegsRendered = function() {
    if (!this.isSegsRendered) {
      this.ensureBusinessHourSegsRendered();
      if (this.bgSegs) {
        this.view.timeGrid.renderFillInContainer('bgEvent', this, this.bgSegs);
      }
      if (this.fgSegs) {
        this.view.timeGrid.renderFgSegsInContainers([[this, this.fgSegs]]);
      }
      return this.isSegsRendered = true;
    }
  };

  EventRow.prototype.ensureBusinessHourSegsRendered = function() {
    if (this.businessHourSegs && !this.isBusinessHourSegsRendered) {
      this.view.timeGrid.renderFillInContainer('businessHours', this, this.businessHourSegs, 'bgevent');
      return this.isBusinessHourSegsRendered = true;
    }
  };

  EventRow.prototype.unrenderEventContent = function() {
    this.clearBusinessHourSegs();
    this.bgSegs = null;
    this.fgSegs = null;
    return this.isSegsRendered = false;
  };

  EventRow.prototype.clearBusinessHourSegs = function() {
    var j, len, ref, seg;
    if (this.businessHourSegs) {
      ref = this.businessHourSegs;
      for (j = 0, len = ref.length; j < len; j++) {
        seg = ref[j];
        if (seg.el) {
          seg.el.remove();
        }
      }
      this.businessHourSegs = null;
    }
    return this.isBusinessHourSegsRendered = false;
  };

  return EventRow;

})(RowParent);


/*
A row that renders information about a particular resource, as well as it events (handled by superclass)
 */

ResourceRow = (function(superClass) {
  extend(ResourceRow, superClass);

  ResourceRow.prototype.resource = null;

  function ResourceRow(view, resource1) {
    this.resource = resource1;
    ResourceRow.__super__.constructor.apply(this, arguments);
  }


  /*
  	Called when a row in the tree has been added
   */

  ResourceRow.prototype.rowAdded = function(row) {
    ResourceRow.__super__.rowAdded.apply(this, arguments);
    if (row !== this && this.isRendered) {
      if (this.children.length === 1) {
        this.enableExpanding();
        if (this.isExpanded) {
          return this.indicateExpanded();
        } else {
          return this.indicateCollapsed();
        }
      }
    }
  };


  /*
  	Called when a row in the tree has been removed
   */

  ResourceRow.prototype.rowRemoved = function(row) {
    ResourceRow.__super__.rowRemoved.apply(this, arguments);
    if (row !== this && this.isRendered) {
      if (!this.children.length) {
        return this.disableExpanding();
      }
    }
  };

  ResourceRow.prototype.render = function() {
    ResourceRow.__super__.render.apply(this, arguments);
    if (this.children.length > 0) {
      this.enableExpanding();
    } else {
      this.disableExpanding();
    }
    return this.view.publiclyTrigger('resourceRender', {
      context: this.resource,
      args: [this.resource, this.getTr('spreadsheet').find('> td'), this.getTr('event').find('> td'), this.view]
    });
  };

  ResourceRow.prototype.renderEventContent = function(tr) {
    ResourceRow.__super__.renderEventContent.apply(this, arguments);
    return tr.attr('data-resource-id', this.resource.id);
  };


  /*
  	Populates the TR with cells containing data about the resource
   */

  ResourceRow.prototype.renderSpreadsheetContent = function(tr) {
    var colSpec, contentEl, input, j, len, ref, resource, td, text, theme;
    theme = this.view.calendar.theme;
    resource = this.resource;
    ref = this.view.colSpecs;
    for (j = 0, len = ref.length; j < len; j++) {
      colSpec = ref[j];
      if (colSpec.group) {
        continue;
      }
      input = colSpec.field ? resource[colSpec.field] || null : resource;
      text = typeof colSpec.text === 'function' ? colSpec.text(resource, input) : input;
      contentEl = $('<div class="fc-cell-content">' + (colSpec.isMain ? this.renderGutterHtml() : '') + '<span class="fc-cell-text">' + (text ? htmlEscape(text) : '&nbsp;') + '</span>' + '</div>');
      if (typeof colSpec.render === 'function') {
        contentEl = colSpec.render(resource, contentEl, input) || contentEl;
      }
      td = $('<td class="' + theme.getClass('widgetContent') + '"/>').append(contentEl);
      if (colSpec.isMain) {
        td.wrapInner('<div/>');
      }
      tr.append(td);
    }
    return tr.attr('data-resource-id', resource.id);
  };


  /*
  	Renders the HTML responsible for the subrow expander area,
  	as well as the space before it (used to align expanders of similar depths)
   */

  ResourceRow.prototype.renderGutterHtml = function() {
    var html, i, j, ref;
    html = '';
    for (i = j = 0, ref = this.depth; j < ref; i = j += 1) {
      html += '<span class="fc-icon"/>';
    }
    html += '<span class="fc-expander-space">' + '<span class="fc-icon"></span>' + '</span>';
    return html;
  };

  return ResourceRow;

})(EventRow);

FC.views.timeline.resourceClass = ResourceTimelineView;

ResourceAgendaView = (function(superClass) {
  extend(ResourceAgendaView, superClass);

  function ResourceAgendaView() {
    return ResourceAgendaView.__super__.constructor.apply(this, arguments);
  }

  ResourceAgendaView.mixin(VertResourceViewMixin);

  ResourceAgendaView.prototype.timeGridClass = ResourceTimeGrid;

  ResourceAgendaView.prototype.dayGridClass = ResourceDayGrid;

  ResourceAgendaView.prototype.renderHead = function() {
    ResourceAgendaView.__super__.renderHead.apply(this, arguments);
    return this.timeGrid.processHeadResourceEls(this.headContainerEl);
  };

  ResourceAgendaView.prototype.setResourcesOnGrids = function(resources) {
    this.timeGrid.setResources(resources);
    if (this.dayGrid) {
      return this.dayGrid.setResources(resources);
    }
  };

  ResourceAgendaView.prototype.unsetResourcesOnGrids = function() {
    this.timeGrid.unsetResources();
    if (this.dayGrid) {
      return this.dayGrid.unsetResources();
    }
  };

  return ResourceAgendaView;

})(FC.AgendaView);

FC.views.agenda.queryResourceClass = function(viewSpec) {
  var ref;
  if ((ref = viewSpec.options.groupByResource || viewSpec.options.groupByDateAndResource) != null ? ref : viewSpec.duration && viewSpec.duration.as('days') === 1) {
    return ResourceAgendaView;
  }
};

ResourceBasicView = (function(superClass) {
  extend(ResourceBasicView, superClass);

  function ResourceBasicView() {
    return ResourceBasicView.__super__.constructor.apply(this, arguments);
  }

  ResourceBasicView.mixin(VertResourceViewMixin);

  ResourceBasicView.prototype.dayGridClass = ResourceDayGrid;

  ResourceBasicView.prototype.renderHead = function() {
    ResourceBasicView.__super__.renderHead.apply(this, arguments);
    return this.dayGrid.processHeadResourceEls(this.headContainerEl);
  };

  ResourceBasicView.prototype.setResourcesOnGrids = function(resources) {
    return this.dayGrid.setResources(resources);
  };

  ResourceBasicView.prototype.unsetResourcesOnGrids = function() {
    return this.dayGrid.unsetResources();
  };

  return ResourceBasicView;

})(FC.BasicView);

ResourceMonthView = (function(superClass) {
  extend(ResourceMonthView, superClass);

  function ResourceMonthView() {
    return ResourceMonthView.__super__.constructor.apply(this, arguments);
  }

  ResourceMonthView.mixin(VertResourceViewMixin);

  ResourceMonthView.prototype.dayGridClass = ResourceDayGrid;

  ResourceMonthView.prototype.renderHead = function() {
    ResourceMonthView.__super__.renderHead.apply(this, arguments);
    return this.dayGrid.processHeadResourceEls(this.headContainerEl);
  };

  ResourceMonthView.prototype.setResourcesOnGrids = function(resources) {
    return this.dayGrid.setResources(resources);
  };

  ResourceMonthView.prototype.unsetResourcesOnGrids = function() {
    return this.dayGrid.unsetResources();
  };

  return ResourceMonthView;

})(FC.MonthView);

FC.views.basic.queryResourceClass = function(viewSpec) {
  var ref;
  if ((ref = viewSpec.options.groupByResource || viewSpec.options.groupByDateAndResource) != null ? ref : viewSpec.duration && viewSpec.duration.as('days') === 1) {
    return ResourceBasicView;
  }
};

FC.views.month.queryResourceClass = function(viewSpec) {
  if (viewSpec.options.groupByResource || viewSpec.options.groupByDateAndResource) {
    return ResourceMonthView;
  }
};

RELEASE_DATE = '2017-08-31';

UPGRADE_WINDOW = {
  years: 1,
  weeks: 1
};

LICENSE_INFO_URL = 'http://fullcalendar.io/scheduler/license/';

PRESET_LICENSE_KEYS = ['GPL-My-Project-Is-Open-Source', 'CC-Attribution-NonCommercial-NoDerivatives'];

processLicenseKey = function(key, containerEl) {
  if (!isImmuneUrl(window.location.href) && !isValidKey(key)) {
    if (!detectWarningInContainer(containerEl)) {
      return renderingWarningInContainer('Please use a valid license key. <a href="' + LICENSE_INFO_URL + '">More Info</a>', containerEl);
    }
  }
};


/*
This decryption is not meant to be bulletproof. Just a way to remind about an upgrade.
 */

isValidKey = function(key) {
  var minPurchaseDate, parts, purchaseDate, releaseDate;
  if ($.inArray(key, PRESET_LICENSE_KEYS) !== -1) {
    return true;
  }
  parts = (key || '').match(/^(\d+)\-fcs\-(\d+)$/);
  if (parts && parts[1].length === 10) {
    purchaseDate = moment.utc(parseInt(parts[2]) * 1000);
    releaseDate = moment.utc(FC.mockSchedulerReleaseDate || RELEASE_DATE);
    if (releaseDate.isValid()) {
      minPurchaseDate = releaseDate.clone().subtract(UPGRADE_WINDOW);
      if (purchaseDate.isAfter(minPurchaseDate)) {
        return true;
      }
    }
  }
  return false;
};

isImmuneUrl = function(url) {
  return Boolean(url.match(/\w+\:\/\/fullcalendar\.io\/|\/demos\/[\w-]+\.html$/));
};

renderingWarningInContainer = function(messageHtml, containerEl) {
  return containerEl.append($('<div class="fc-license-message" />').html(messageHtml));
};

detectWarningInContainer = function(containerEl) {
  return containerEl.find('.fc-license-message').length >= 1;
};

});
