Features

  • Adjusts time range selection to skip non-business cells during drag and drop (custom implementation)
  • Adjusts resizing shadow to skip non-business cells during drag and drop (custom implementation)
  • Adjusts moving shadow to skip non-business (config)
  • Includes a trial version of DayPilot Pro for JavaScript (see License below)

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.

Live Demo

Skipping Non-Business Cells during Event Moving

javascript-scheduler-skip-non-business-event-moving.png

The JavaScript Scheduler component includes built-in support for taking non-business cells into account when moving events. It will automatically extend the event duration to skip non-business cells so the original business duration will remain the same.

You can enable it using eventMovingSkipNonBusiness property:

var dp = new DayPilot.Scheduler("dp", {
  // ...
  eventMoveSkipNonBusiness: true
});

The Scheduler will respect non-business hours that are hidden from the timeline and also custom business hours that you set for individual rows using onBeforeCellRender.

Creating Events

javascript-scheduler-skip-non-business-event-creating.png

When creating events the Scheduler doesn't force the non-business cell skipping. However, you can implement it easily using onTimeRangeSelecting event handler. This way you can decide what to do if the selected time range start and/or ends on non-business cells - the selection can be extended to include the next business cell or shortened. Our implementation will always extend the selection:

var dp = new DayPilot.Scheduler("dp", {
  
  // ...
  
  onTimeRangeSelecting: function(args) {
    var cells = args.row.cells.forRange(args.start, args.end);
    var first = cells[0];
    var last = cells.last();

    while (first && !first.properties.business) {
      var minusDuration = new DayPilot.Duration(first.end, first.start);
      args.start = args.start.addTime(minusDuration);
      first = DayPilot.list(args.row.cells.forRange(args.start, args.start.addSeconds(1))).first();
    }
    if (!first) {
      args.start = dp.visibleStart();
      args.allowed = false;
      args.right.enabled = true;
      args.right.html = "Out of range";
    }

    while (last && !last.properties.business) {
      var duration = new DayPilot.Duration(last.start, last.end);
      args.end = args.end.addTime(duration);
      last = DayPilot.list(args.row.cells.forRange(args.end.addSeconds(-1), args.end)).first();
    }
    if (!last) {
      args.end = dp.visibleEnd();
      args.allowed = false;
      args.left.enabled = true;
      args.left.html = "Out of range";
    }
  },
  
  // ...

});

Resizing Events

javascript-scheduler-skip-non-business-event-resizing.png

The same logic can be used for event resizing:

var dp = new DayPilot.Scheduler("dp", {
  
  // ...
  
  onEventResizing: function(args) {
    var cells = args.row.cells.forRange(args.start, args.end);
    var first = cells[0];
    var last = cells.last();

    while (first && !first.properties.business) {
      var minusDuration = new DayPilot.Duration(first.end, first.start);
      args.start = args.start.addTime(minusDuration);
      first = DayPilot.list(args.row.cells.forRange(args.start, args.start.addSeconds(1))).first();
    }
    if (!first) {
      args.start = dp.visibleStart();
      args.allowed = false;
      args.right.enabled = true;
      args.right.html = "Out of range";
    }

    while (last && !last.properties.business) {
      var duration = new DayPilot.Duration(last.start, last.end);
      args.end = args.end.addTime(duration);
      last = DayPilot.list(args.row.cells.forRange(args.end.addSeconds(-1), args.end)).first();
    }
    if (!last) {
      args.end = dp.visibleEnd();
      args.allowed = false;
      args.left.enabled = true;
      args.left.html = "Out of range";
    }
  },
  
  // ...

});

Reusing the Real-Time Adjusting Logic

As both real-time event handlers (onTimeRangeSelecting and onEventResizing) use the same implementation we can consolidate the code and extract the logic into a special function. This is how the implementation looks like now:

Config:

var dp = new DayPilot.Scheduler("dp", {
  // ...
  onTimeRangeSelecting: function(args) {
    adjustForNonBusiness(args);
  },
  onEventResizing: function(args) {
    adjustForNonBusiness(args);
  },
  eventMoveSkipNonBusiness: true
});

Adjusting function:

function adjustForNonBusiness(args) {
  var cells = args.row.cells.forRange(args.start, args.end);
  var first = cells[0];
  var last = cells.last();

  while (first && !first.properties.business) {
    var minusDuration = new DayPilot.Duration(first.end, first.start);
    args.start = args.start.addTime(minusDuration);
    first = DayPilot.list(args.row.cells.forRange(args.start, args.start.addSeconds(1))).first();
  }
  if (!first) {
    args.start = dp.visibleStart();
    args.allowed = false;
    args.right.enabled = true;
    args.right.html = "Out of range";
  }

  while (last && !last.properties.business) {
    var duration = new DayPilot.Duration(last.start, last.end);
    args.end = args.end.addTime(duration);
    last = DayPilot.list(args.row.cells.forRange(args.end.addSeconds(-1), args.end)).first();
  }
  if (!last) {
    args.end = dp.visibleEnd();
    args.allowed = false;
    args.left.enabled = true;
    args.left.html = "Out of range";
  }
}