All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scout.planner.Planner.js Maven / Gradle / Ivy

There is a newer version: 25.1.0-beta.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2014-2015 BSI Business Systems Integration AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     BSI Business Systems Integration AG - initial API and implementation
 ******************************************************************************/
scout.Planner = function() {
  scout.Planner.parent.call(this);
  this.resourceMap = [];
  this.activityMap = [];

  // visual
  this._resourceTitleWidth = 20;

  // main elements
  this.$container;
  this.$range;
  this.$modes;
  this.$grid;

  // scale calculator
  this.transformLeft = function(t) {
    return t;
  };
  this.transformWidth = function(t) {
    return t;
  };

  this.yearPanelVisible = false;
  this._addAdapterProperties(['menus']);
};
scout.inherits(scout.Planner, scout.ModelAdapter);

scout.Planner.Direction = {
  BACKWARD: -1,
  FORWARD: 1
};

/**
 * Enum providing display-modes for planner (extends calendar).
 * @see IPlannerDisplayMode.java
 */
scout.Planner.DisplayMode = $.extend({
  CALENDAR_WEEK: 5,
  YEAR: 6
}, scout.Calendar.DisplayMode);


scout.Planner.SelectionMode = {
  NONE: 0,
  ACTIVITY: 1,
  SINGLE_RANGE: 2,
  MULTI_RANGE: 3
};

scout.Planner.prototype._init = function(model) {
  scout.Planner.parent.prototype._init.call(this, model);
  this._yearPanel = scout.create('YearPanel', {
    parent: this,
    alwaysSelectFirstDay: true
  });
  this._yearPanel.on('dateSelect', this._onYearPanelDateSelect.bind(this));
  this._header = scout.create('PlannerHeader', {
    parent: this
  });
  this._header.on('todayClick', this._onTodayClick.bind(this));
  this._header.on('yearClick', this._onYearClick.bind(this));
  this._header.on('previousClick', this._onPreviousClick.bind(this));
  this._header.on('nextClick', this._onNextClick.bind(this));
  this._header.on('displayModeClick', this._onDisplayModeClick.bind(this));
  this.menuBar = scout.create('MenuBar', {
    parent: this,
    menuOrder: new scout.PlannerMenuItemsOrder(this.session, 'Planner')
  });
  this.menuBar.bottom();
  for (var i = 0; i < this.resources.length; i++) {
    this._initResource(this.resources[i]);
  }
  this._syncDisplayMode(this.displayMode);
  this._syncAvailableDisplayModes(this.availableDisplayModes);
  this._syncViewRange(this.viewRange);
  this._syncSelectedResources(this.selectedResources);
  this._syncSelectionRange(this.selectionRange);
  this._syncMenus(this.menus);

  this._tooltipSupport = new scout.TooltipSupport({
    parent: this,
    arrowPosition: 50
  });

};

scout.Planner.prototype._initResource = function(resource) {
  scout.defaultValues.applyTo(resource, 'Resource');
  resource.activities.forEach(function(activity) {
    this._initActivity(activity);
  }, this);
  this.resourceMap[resource.id] = resource;
};

scout.Planner.prototype._initActivity = function(activity) {
  activity.beginTime = scout.dates.parseJsonDate(activity.beginTime);
  activity.endTime = scout.dates.parseJsonDate(activity.endTime);
  scout.defaultValues.applyTo(activity, 'Activity');
  this.activityMap[activity.id] = activity;
};

scout.Planner.prototype._render = function($parent) {
  // basics, layout etc.
  this.$container = $parent.appendDiv('planner');
  var layout = new scout.PlannerLayout(this);
  this.htmlComp = new scout.HtmlComponent(this.$container, this.session);
  this.htmlComp.setLayout(layout);
  this.htmlComp.pixelBasedSizing = false;

  // main elements
  this._header.render(this.$container);
  this._yearPanel.render(this.$container);
  this.$grid = this.$container.appendDiv('planner-grid')
    .on('mousedown', '.resource-cells', this._onCellMousedown.bind(this))
    .on('mousedown', '.resource-title', this._onResourceTitleMousedown.bind(this))
    .on('contextmenu', '.resource-title', this._onResourceTitleContextMenu.bind(this))
    .on('contextmenu', '.planner-activity', this._onActivityContextMenu.bind(this));
  this.$scale = this.$container.appendDiv('planner-scale');
  this.menuBar.render(this.$container);

  scout.tooltips.install(this.$grid, {
    parent: this,
    selector: '.planner-activity',
    text: function($comp) {
      if (this._activityById($comp.attr('data-id'))) {
        return this._activityById($comp.attr('data-id')).tooltipText;
      } else {
        return undefined;
      }
    }.bind(this)
  });

  scout.scrollbars.install(this.$grid, {
    parent: this
  });
  this._gridScrollHandler = this._onGridScroll.bind(this);
  this.$grid.on('scroll', this._gridScrollHandler);
};

scout.Planner.prototype._renderProperties = function() {
  scout.Planner.parent.prototype._renderProperties.call(this);

  this._renderViewRange();
  this._renderHeaderVisible();
  this._renderYearPanelVisible(false);
  this._renderResources();
  this._renderSelectedResources();
  // render with setTimeout because the planner needs to be layouted first
  setTimeout(this._renderSelectionRange.bind(this));
};

scout.Planner.prototype._remove = function() {
  scout.scrollbars.uninstall(this.$grid, this.session);
  scout.Planner.parent.prototype._remove.call(this);
};

/* -- basics, events -------------------------------------------- */

scout.Planner.prototype._onPreviousClick = function(event) {
  this._navigateDate(scout.Planner.Direction.BACKWARD);
};

scout.Planner.prototype._onNextClick = function(event) {
  this._navigateDate(scout.Planner.Direction.FORWARD);
};

scout.Planner.prototype._navigateDate = function(direction) {
  var viewRange = new scout.DateRange(this.viewRange.from, this.viewRange.to),
    displayMode = scout.Planner.DisplayMode;

  if (this.displayMode === displayMode.DAY) {
    viewRange.from = scout.dates.shift(this.viewRange.from, 0, 0, direction);
    viewRange.to = scout.dates.shift(this.viewRange.to, 0, 0, direction);
  } else if (scout.isOneOf(this.displayMode, displayMode.WEEK, displayMode.WORK_WEEK)) {
    viewRange.from = scout.dates.shift(this.viewRange.from, 0, 0, direction * 7);
    viewRange.from = scout.dates.ensureMonday(viewRange.from, -1 * direction);
    viewRange.to = scout.dates.shift(this.viewRange.to, 0, 0, direction * 7);
  } else if (this.displayMode === displayMode.MONTH) {
    viewRange.from = scout.dates.shift(this.viewRange.from, 0, direction, 0);
    viewRange.from = scout.dates.ensureMonday(viewRange.from, -1 * direction);
    viewRange.to = scout.dates.shift(this.viewRange.to, 0, direction, 0);
  } else if (this.displayMode === displayMode.CALENDAR_WEEK) {
    viewRange.from = scout.dates.shift(this.viewRange.from, 0, direction, 0);
    viewRange.from = scout.dates.ensureMonday(viewRange.from, -1 * direction);
    viewRange.to = scout.dates.shift(this.viewRange.to, 0, direction, 0);
  } else if (this.displayMode === displayMode.YEAR) {
    viewRange.from = scout.dates.shift(this.viewRange.from, 0, 3 * direction, 0);
    viewRange.to = scout.dates.shift(this.viewRange.to, 0, 3 * direction, 0);
  }

  this.setViewRange(viewRange);
};

scout.Planner.prototype._onTodayClick = function(event) {
  var today = new Date(),
    year = today.getFullYear(),
    month = today.getMonth(),
    date = today.getDate(),
    day = (today.getDay() + 6) % 7,
    displayMode = scout.Planner.DisplayMode;

  if (this.displayMode === displayMode.DAY) {
    today = new Date(year, month, date);
  } else if (this.displayMode === displayMode.YEAR) {
    today = new Date(year, month, 1);
  } else {
    today = new Date(year, month, date - day);
  }

  this.setViewRangeFrom(today);
};

scout.Planner.prototype._onDisplayModeClick = function(event) {
  var displayMode = event.displayMode;
  this.setDisplayMode(displayMode);
};

scout.Planner.prototype._onYearClick = function(event) {
  this.setYearPanelVisible(!this.yearPanelVisible);
};

scout.Planner.prototype._onYearPanelDateSelect = function(event) {
  this.setViewRangeFrom(event.date);
};

scout.Planner.prototype._onResourceTitleMousedown = function(event) {
  var $resource = $(event.target).parent();
  if ($resource.isSelected()) {
    if (event.which === 3 || event.which === 1 && event.ctrlKey) {
      // Right click on an already selected resource must not clear the selection -> context menu will be opened
      return;
    }
  }
  this.startRow = $resource.data('resource');
  this.lastRow = this.startRow;
  this._select();
};

scout.Planner.prototype._onResourceTitleContextMenu = function(event) {
  this._showContextMenu(event, 'Planner.Resource');
};

scout.Planner.prototype._onRangeSelectorContextMenu = function(event) {
  this._showContextMenu(event, 'Planner.Range');
};

scout.Planner.prototype._onActivityContextMenu = function(event) {
  this._showContextMenu(event, 'Planner.Activity');
};

scout.Planner.prototype._showContextMenu = function(event, allowedType) {
  event.preventDefault();
  event.stopPropagation();
  var func = function func(event, allowedType) {
    var filteredMenus = this._filterMenus([allowedType]),
      $part = $(event.currentTarget);
    if (filteredMenus.length === 0) {
      return; // at least one menu item must be visible
    }
    var popup = scout.create('ContextMenuPopup', {
      parent: this,
      menuItems: filteredMenus,
      location: {
        x: event.pageX,
        y: event.pageY
      },
      $anchor: $part
    });
    popup.open();
  }.bind(this);

  scout.menus.showContextMenuWithWait(this.session, func, event, allowedType);
};

scout.Planner.prototype._onGridScroll = function() {
  this._reconcileScrollPos();
};

scout.Planner.prototype._reconcileScrollPos = function() {
  // When scrolling horizontally scroll scale as well
  var scrollLeft = this.$grid.scrollLeft();
  this.$scale.scrollLeft(scrollLeft);
};

scout.Planner.prototype._renderRange = function() {
  if (!this.viewRange.from || !this.viewRange.to) {
    return;
  }
  var text,
    toDate = new Date(this.viewRange.to.valueOf() - 1),
    toText = this.session.text('ui.to'),
    displayMode = scout.Planner.DisplayMode;

  // find range text
  if (scout.dates.isSameDay(this.viewRange.from, toDate)) {
    text = this._dateFormat(this.viewRange.from, 'd. MMMM yyyy');
  } else if (this.viewRange.from.getMonth() === toDate.getMonth() && this.viewRange.from.getFullYear() === toDate.getFullYear()) {
    text = scout.strings.join(' ', this._dateFormat(this.viewRange.from, 'd.'), toText, this._dateFormat(toDate, 'd. MMMM yyyy'));
  } else if (this.viewRange.from.getFullYear() === toDate.getFullYear()) {
    if (this.displayMode === displayMode.YEAR) {
      text = scout.strings.join(' ', this._dateFormat(this.viewRange.from, 'MMMM'), toText, this._dateFormat(toDate, 'MMMM yyyy'));
    } else {
      text = scout.strings.join(' ', this._dateFormat(this.viewRange.from, 'd.  MMMM'), toText, this._dateFormat(toDate, 'd. MMMM yyyy'));
    }
  } else {
    if (this.displayMode === displayMode.YEAR) {
      text = scout.strings.join(' ', this._dateFormat(this.viewRange.from, 'MMMM yyyy'), toText, this._dateFormat(toDate, 'MMMM yyyy'));
    } else {
      text = scout.strings.join(' ', this._dateFormat(this.viewRange.from, 'd.  MMMM yyyy'), toText, this._dateFormat(toDate, 'd. MMMM yyyy'));
    }
  }

  // set text
  $('.planner-select', this._header.$range).text(text);
};

scout.Planner.prototype._renderScale = function() {
  if (!this.viewRange.from || !this.viewRange.to) {
    return;
  }
  var $timeline, $timelineLarge, $timelineSmall, loop, $divLarge, $divSmall, width, newLargeGroup,
    first = true,
    that = this,
    displayMode = scout.Planner.DisplayMode;

  // empty scale
  this.$scale.empty();
  this.$grid.children('.planner-small-scale-item-line').remove();
  this.$grid.children('.planner-large-scale-item-line').remove();

  // append main elements
  this.$scaleTitle = this.$scale.appendDiv('planner-scale-title');
  this._renderLabel();
  this.$timeline = this.$scale.appendDiv('timeline');
  this.$timelineLarge = this.$timeline.appendDiv('timeline-large');
  this.$timelineSmall = this.$timeline.appendDiv('timeline-small');
  $timeline = this.$timeline;
  $timelineLarge = this.$timelineLarge;
  $timelineSmall = this.$timelineSmall;

  // fill timeline large depending on mode
  if (this.displayMode === displayMode.DAY) {
    loop = new Date(this.viewRange.from.valueOf());

    // from start to end
    while (loop < this.viewRange.to) {
      newLargeGroup = false;
      if ((loop.getMinutes() === 0) || first) {
        $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'HH')).data('count', 0);
        newLargeGroup = true;
      }

      $divSmall = $timelineSmall
        .appendDiv('scale-item', this._dateFormat(loop, ':mm'))
        .data('date-from', new Date(loop.valueOf()));

      loop.setMinutes(loop.getMinutes() + 30);
      $divSmall.data('date-to', new Date(loop.valueOf()))
        .data('first', newLargeGroup);

      $divLarge.data('count', $divLarge.data('count') + 1);
      first = false;
    }
  } else if (scout.isOneOf(this.displayMode, displayMode.WORK_WEEK, displayMode.WEEK)) {
    loop = new Date(this.viewRange.from.valueOf());

    // from start to end
    while (loop < this.viewRange.to) {
      newLargeGroup = false;
      if ((loop.getHours() === 0) || first) {
        if ((loop.getMonth() === 0) || first) {
          $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'd. MMMM yyyy')).data('count', 0);
        } else if (loop.getDate() === 1) {
          $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'd. MMMM')).data('count', 0);
        } else {
          $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'd.')).data('count', 0);
        }
        newLargeGroup = true;
      }

      $divSmall = $timelineSmall
        .appendDiv('scale-item', this._dateFormat(loop, 'HH:mm'))
        .data('date-from', new Date(loop.valueOf()));

      loop.setHours(loop.getHours() + 6);
      $divSmall.data('date-to', new Date(loop.valueOf()))
        .data('first', newLargeGroup);

      $divLarge.data('count', $divLarge.data('count') + 1);
      first = false;
    }

  } else if (this.displayMode === displayMode.MONTH) {
    loop = new Date(this.viewRange.from.valueOf());

    // from start to end
    while (loop < this.viewRange.to) {
      newLargeGroup = false;
      if ((loop.getDate() === 1) || first) {
        if ((loop.getMonth() === 0) || first) {
          $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'MMMM yyyy')).data('count', 0);
        } else {
          $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'MMMM')).data('count', 0);
        }
        newLargeGroup = true;
      }

      $divSmall = $timelineSmall
        .appendDiv('scale-item', this._dateFormat(loop, 'dd'))
        .data('date-from', new Date(loop.valueOf()));

      if (loop.getDate() % 2 === 1) {
        $divSmall.addClass('label-invisible');
      }

      loop = scout.dates.shift(loop, 0, 0, 1);
      $divSmall.data('date-to', new Date(loop.valueOf()))
        .data('first', newLargeGroup);

      $divLarge.data('count', $divLarge.data('count') + 1);
      first = false;
    }

  } else if (this.displayMode === displayMode.CALENDAR_WEEK) {
    loop = new Date(this.viewRange.from.valueOf());

    // from start to end
    while (loop < this.viewRange.to) {
      newLargeGroup = false;
      if ((loop.getDate() < 8) || first === true) {
        if ((loop.getMonth() === 0) || first === true) {
          if (loop.getDate() > 11) {
            $divLarge = $timelineLarge.appendDiv('scale-item').html(' ').data('count', 0);
            first = 2;
          } else {
            $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'MMMM yyyy')).data('count', 0);
            first = false;
          }
        } else {
          if (first === 2) {
            $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'MMMM yyyy')).data('count', 0);
            first = false;
          } else {
            $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'MMMM')).data('count', 0);
          }
        }
        newLargeGroup = true;
      }

      $divSmall = $timelineSmall
        .appendDiv('scale-item', scout.dates.weekInYear(loop))
        .data('date-from', new Date(loop.valueOf()))
        .data('tooltipText', this._scaleTooltipText.bind(this));
      this._tooltipSupport.install($divSmall);

      loop.setDate(loop.getDate() + 7);
      $divSmall.data('date-to', new Date(loop.valueOf()))
        .data('first', newLargeGroup);

      $divLarge.data('count', $divLarge.data('count') + 1);
    }

  } else if (this.displayMode === displayMode.YEAR) {
    loop = new Date(this.viewRange.from.valueOf());

    // from start to end
    while (loop < this.viewRange.to) {
      newLargeGroup = false;
      if ((loop.getMonth() === 0) || first) {
        $divLarge = $timelineLarge.appendDiv('scale-item', this._dateFormat(loop, 'yyyy')).data('count', 0);
        newLargeGroup = true;
      }

      $divSmall = $timelineSmall
        .appendDiv('scale-item', this._dateFormat(loop, 'MMMM'))
        .data('date-from', new Date(loop.valueOf()));

      loop = scout.dates.shift(loop, 0, 1, 0);
      $divSmall.data('date-to', new Date(loop.valueOf()))
        .data('first', newLargeGroup);

      $divLarge.data('count', $divLarge.data('count') + 1);
      first = false;
    }
  }

  // set sizes and append scale lines
  var $smallScaleItems = $timelineSmall.children('.scale-item');
  var $largeScaleItems = $timelineLarge.children('.scale-item');
  width = 100 / $smallScaleItems.length;
  $largeScaleItems.each(function() {
    var $scaleItem = $(this);
    $scaleItem.css('width', $scaleItem.data('count') * width + '%')
      .data('scale-item-line', that.$grid.appendDiv('planner-large-scale-item-line'));
    $scaleItem.appendDiv('planner-large-scale-item-line')
      .css('left', 0);
  });
  $smallScaleItems.each(function(index) {
    var $scaleItem = $(this);
    $scaleItem.css('width', width + '%');
    if (!$scaleItem.data('first')) {
      $scaleItem.data('scale-item-line', that.$grid.appendDiv('planner-small-scale-item-line'));
      $scaleItem.appendDiv('planner-small-scale-item-line')
        .css('left', 0);
    }
  });

  // find transfer function
  this.beginScale = $timelineSmall.children().first().data('date-from').valueOf();
  this.endScale = $timelineSmall.children().last().data('date-to').valueOf();

  this.transformLeft = function(begin, end) {
    return function(t) {
      return (t - begin) / (end - begin) * 100;
    };
  }(this.beginScale, this.endScale);

  this.transformWidth = function(begin, end) {
    return function(t) {
      return t / (end - begin) * 100;
    };
  }(this.beginScale, this.endScale);
};

/* -- scale events --------------------------------------------------- */

scout.Planner.prototype._scaleTooltipText = function($scale) {
  var toText = ' ' + this.session.text('ui.to') + ' ',
    from = new Date($scale.data('date-from').valueOf()),
    to = new Date($scale.data('date-to').valueOf() - 1);

  if (from.getMonth() === to.getMonth()) {
    return this._dateFormat(from, 'd.') + toText + this._dateFormat(to, 'd. MMMM yyyy');
  } else if (from.getFullYear() === to.getFullYear()) {
    return this._dateFormat(from, 'd. MMMM') + toText + this._dateFormat(to, 'd. MMMM yyyy');
  } else {
    return this._dateFormat(from, 'd. MMMM yyyy') + toText + this._dateFormat(to, 'd. MMMM yyyy');
  }
};

/* --  render resources, activities --------------------------------- */

scout.Planner.prototype._removeAllResources = function() {
  this.resources.forEach(function(resource) {
    resource.$resource.remove();
  });
};

scout.Planner.prototype._renderResources = function(resources) {
  var i, $resource, resource,
    resourcesHtml = '';

  resources = resources || this.resources;
  for (i = 0; i < resources.length; i++) {
    resource = resources[i];
    resourcesHtml += this._buildResourceHtml(resource, this.$grid);
  }

  // Append resources to grid
  $(resourcesHtml).appendTo(this.$grid);

  // Match resources
  this.$grid.children('.planner-resource').each(function(index, element) {
    var $element = $(element);
    resource = this._resourceById($element.attr('data-id'));
    this._linkResource($element, resource);
  }.bind(this));
};

scout.Planner.prototype._linkResource = function($resource, resource) {
  $resource.data('resource', resource);
  resource.$resource = $resource;
  resource.$cells = $resource.children('.resource-cells');
};

scout.Planner.prototype._rerenderActivities = function(resources) {
  resources = resources || this.resources;
  resources.forEach(function(resource) {
    this._removeActivititesForResource(resource);
    this._renderActivititesForResource(resource);
  }, this);
};

scout.Planner.prototype._buildResourceHtml = function(resource) {
  var resourceHtml = '
'; resourceHtml += '
' + scout.strings.encode(resource.resourceCell.text || '') + '
'; resourceHtml += '
' + this._buildActivitiesHtml(resource) + '
'; resourceHtml += '
'; return resourceHtml; }; scout.Planner.prototype._renderActivititesForResource = function(resource) { resource.$cells.html(this._buildActivitiesHtml(resource)); }; scout.Planner.prototype._buildActivitiesHtml = function(resource) { var activitiesHtml = ''; resource.activities.forEach(function(activity) { if (activity.beginTime.valueOf() >= this.endScale || activity.endTime.valueOf() <= this.beginScale) { // don't add activities which are not in the view range return; } activitiesHtml += this._buildActivityHtml(activity); }, this); return activitiesHtml; }; scout.Planner.prototype._removeActivititesForResource = function(resource) { resource.activities.forEach(function(activity) { if (activity.$activity) { activity.$activity.remove(); activity.$activity = null; } }, this); }; scout.Planner.prototype._buildActivityHtml = function(activity) { var i, level = 100 - Math.min(activity.level * 100, 100), levelColor = scout.styles.modelToCssColor(activity.levelColor), begin = activity.beginTime.valueOf(), end = activity.endTime.valueOf(); // Make sure activity fits into scale begin = Math.max(begin, this.beginScale); end = Math.min(end, this.endScale); var activityCssClass = 'planner-activity' + (activity.cssClass ? (' ' + activity.cssClass) : ''); var activityStyle = 'left: ' + 'calc(' + this.transformLeft(begin) + '% + 2px);'; activityStyle += ' width: ' + 'calc(' + this.transformWidth(end - begin) + '% - 4px);'; if (levelColor) { activityStyle += ' background-color: ' + levelColor + ';'; activityStyle += ' border-color: ' + levelColor + ';'; } // the background-color represents the fill level and not the image. This makes it easier to change the color using a css class activityStyle += ' background-image: ' + 'linear-gradient(to bottom, #fff 0%, #fff ' + level + '%, transparent ' + level + '%, transparent 100% );'; var activityHtml = ''; return activityHtml; }; /* -- selector -------------------------------------------------- */ scout.Planner.prototype._onCellMousedown = function(event) { var $activity, $resource, $target = $(event.target), selectionMode = scout.Planner.SelectionMode; if (this.selectionMode === selectionMode.NONE) { return; } if (this.selectionMode === selectionMode.ACTIVITY) { $activity = this._$elementFromPoint(event.pageX, event.pageY); if ($activity.hasClass('planner-activity')) { $('.selected', this.$grid).removeClass('selected'); $activity.addClass('selected'); $resource = $activity.parent().parent(); this.selectResources([$resource.data('resource')]); } } else { if ($target.hasClass('selector')) { if (event.which === 3 || event.which === 1 && event.ctrlKey) { // Right click on the selector must not clear the selection -> context menu will be opened return; } } // init selector this.startRow = this._findRow(event.pageY); this.lastRow = this.startRow; // find range on scale this.startRange = this._findScale(event.pageX); this.lastRange = this.startRange; // draw this._select(true); // event this._cellMousemoveHandler = this._onCellMousemove.bind(this); $target.document() .on('mousemove', this._cellMousemoveHandler) .one('mouseup', this._onDocumentMouseup.bind(this)); } }; scout.Planner.prototype._onResizeMousedown = function(event) { var swap, $target = $(event.target); // find range on scale if (($target.hasClass('selector-resize-right') && this.startRange.to > this.lastRange.to) || ($target.hasClass('selector-resize-left') && this.startRange.to < this.lastRange.to)) { swap = this.startRange; this.startRange = this.lastRange; this.lastRange = swap; } $target.body().addClass('col-resize'); this._resizeMousemoveHandler = this._onResizeMousemove.bind(this); $target.document() .on('mousemove', this._resizeMousemoveHandler) .one('mouseup', this._onDocumentMouseup.bind(this)); return false; }; scout.Planner.prototype._onCellMousemove = function(event) { var lastRow = this._findRow(event.pageY); if (lastRow) { this.lastRow = lastRow; } var lastRange = this._findScale(event.pageX); if (lastRange) { this.lastRange = lastRange; } this._select(true); }; scout.Planner.prototype._onResizeMousemove = function(event) { var lastRange = this._findScale(event.pageX); if (lastRange) { this.lastRange = lastRange; } this._select(true); }; scout.Planner.prototype._onDocumentMouseup = function(event) { this._select(); var $target = $(event.target); $target.body().removeClass('col-resize'); if (this._cellMousemoveHandler) { $target.document().off('mousemove', this._documentMousemoveHandler); this._cellMousemoveHandler = null; } if (this._resizeMousemoveHandler) { $target.document().off('mousemove', this._resizeMousemoveHandler); this._resizeMousemoveHandler = null; } }; scout.Planner.prototype._select = function(whileSelecting) { if (!this.startRow || !this.lastRow) { return; } // If startRange or lastRange are not given, use the existing range selection // Happens if the user clicks a resource instead of making a range selection if (!this.startRange && !this.lastRange) { if (this.selectionRange.from) { this.startRange = {}; this.startRange.from = this.selectionRange.from.getTime(); this.startRange.to = this.startRange.from; } if (this.selectionRange.to) { this.lastRange = {}; this.lastRange.from = this.selectionRange.to.getTime(); this.lastRange.to = this.lastRange.from; } } var rangeSelected = !!(this.startRange && this.lastRange); var $startRow = this.startRow.$resource, $lastRow = this.lastRow.$resource; // in case of single selection if (this.selectionMode === scout.Planner.SelectionMode.SINGLE_RANGE) { this.lastRow = this.startRow; $lastRow = this.startRow.$resource; } // select rows var $upperRow = ($startRow[0].offsetTop <= $lastRow[0].offsetTop) ? $startRow : $lastRow, $lowerRow = ($startRow[0].offsetTop > $lastRow[0].offsetTop) ? $startRow : $lastRow, resources = $('.planner-resource', this.$grid).toArray(), top = $upperRow[0].offsetTop, low = $lowerRow[0].offsetTop; for (var r = resources.length - 1; r >= 0; r--) { var row = resources[r]; if ((row.offsetTop < top && row.offsetTop < low) || (row.offsetTop > top && row.offsetTop > low)) { resources.splice(r, 1); } } this.selectResources(resources.map(function(i) { return $(i).data('resource'); }), !whileSelecting); if (rangeSelected) { // left and width var from = Math.min(this.lastRange.from, this.startRange.from), to = Math.max(this.lastRange.to, this.startRange.to); var selectionRange = { from: new Date(from), to: new Date(to) }; this.selectRange(selectionRange, !whileSelecting); } }; scout.Planner.prototype._findRow = function(y) { var x = this.$grid.offset().left + 10, $row = this._$elementFromPoint(x, y).parent(); if ($row.hasClass('planner-resource')) { return $row.data('resource'); } else { return null; } }; scout.Planner.prototype._findScale = function(x) { var y = this.$scale.offset().top + this.$scale.height() * 0.75, $scale = this._$elementFromPoint(x, y); if ($scale.data('date-from') !== undefined) { return { from: $scale.data('date-from').valueOf(), to: $scale.data('date-to').valueOf() }; } else { return null; } }; /* -- helper ---------------------------------------------------- */ scout.Planner.prototype._$elementFromPoint = function(x, y) { return $(this.$container.document(true).elementFromPoint(x, y)); }; scout.Planner.prototype._dateFormat = function(date, pattern) { var d = new Date(date.valueOf()), dateFormat = new scout.DateFormat(this.session.locale, pattern); return dateFormat.format(d); }; scout.Planner.prototype._renderViewRange = function() { this._renderRange(); this._renderScale(); this.invalidateLayoutTree(); }; scout.Planner.prototype._renderHeaderVisible = function() { this._header.setVisible(this.headerVisible); this.invalidateLayoutTree(); }; scout.Planner.prototype._renderYearPanelVisible = function(animated) { var yearPanelWidth; if (this.yearPanelVisible) { this._yearPanel.renderContent(); } // show or hide year panel $('.calendar-toggle-year', this.$modes).select(this.yearPanelVisible); if (this.yearPanelVisible) { yearPanelWidth = 210; } else { yearPanelWidth = 0; } this._yearPanel.$container.animate({ width: yearPanelWidth }, { duration: animated ? 500 : 0, progress: this._onYearPanelWidthChange.bind(this), complete: this._afterYearPanelWidthChange.bind(this) }); }; scout.Planner.prototype._onYearPanelWidthChange = function() { if (!this._yearPanel.$container) { // If container has been removed in the meantime (e.g. user navigates away while animation is in progress) return; } var yearPanelWidth = this._yearPanel.$container.outerWidth(); this.$grid.css('width', 'calc(100% - ' + yearPanelWidth + 'px)'); this.$scale.css('width', 'calc(100% - ' + yearPanelWidth + 'px)'); this.revalidateLayout(); }; scout.Planner.prototype._afterYearPanelWidthChange = function() { if (!this.yearPanelVisible) { this._yearPanel.removeContent(); } }; scout.Planner.prototype._syncMenus = function(menus, oldMenus) { this.updateKeyStrokes(menus, oldMenus); this.menus = menus; this._updateMenuBar(); }; scout.Planner.prototype._updateMenuBar = function() { var menuItems = this._filterMenus(['Planner.EmptySpace', 'Planner.Resource', 'Planner.Activity', 'Planner.Range'], true); this.menuBar.setMenuItems(menuItems); }; scout.Planner.prototype._renderMenus = function() { // NOP }; scout.Planner.prototype._removeMenus = function() { // menubar takes care about removal }; scout.Planner.prototype._filterMenus = function(allowedTypes, enableDisableKeyStroke) { allowedTypes = allowedTypes || []; if (allowedTypes.indexOf('Planner.Resource') > -1 && this.selectedResources.length === 0) { scout.arrays.remove(allowedTypes, 'Planner.Resource'); } if (allowedTypes.indexOf('Planner.Activity') > -1 && !this.selectedActivity) { scout.arrays.remove(allowedTypes, 'Planner.Activity'); } if (allowedTypes.indexOf('Planner.Range') > -1 && !this.selectionRange.from && !this.selectionRange.to) { scout.arrays.remove(allowedTypes, 'Planner.Range'); } return scout.menus.filter(this.menus, allowedTypes, true, enableDisableKeyStroke); }; scout.Planner.prototype._renderWorkDayCount = function() {}; scout.Planner.prototype._renderWorkDaysOnly = function() {}; scout.Planner.prototype._renderFirstHourOfDay = function() {}; scout.Planner.prototype._renderLastHourOfDay = function() {}; scout.Planner.prototype._renderAvailableDisplayModes = function() { // done by PlannerHeader.js }; scout.Planner.prototype._renderDisplayMode = function() { // done by PlannerHeader.js }; scout.Planner.prototype._syncViewRange = function(viewRange) { this.viewRange = new scout.DateRange( scout.dates.parseJsonDate(viewRange.from), scout.dates.parseJsonDate(viewRange.to) ); this._yearPanel.setViewRange(this.viewRange); this._yearPanel.selectDate(this.viewRange.from); }; scout.Planner.prototype._syncDisplayMode = function(displayMode) { this.displayMode = displayMode; this._yearPanel.setDisplayMode(this.displayMode); this._header.setDisplayMode(this.displayMode); }; scout.Planner.prototype._syncAvailableDisplayModes = function(availableDisplayModes) { this.availableDisplayModes = availableDisplayModes; this._header.setAvailableDisplayModes(this.availableDisplayModes); }; scout.Planner.prototype._syncSelectionRange = function(selectionRange) { this.selectionRange = { from: scout.dates.parseJsonDate(selectionRange.from), to: scout.dates.parseJsonDate(selectionRange.to) }; this._updateMenuBar(); }; scout.Planner.prototype._syncSelectedResources = function(selectedResources) { this.selectedResources = this._resourcesByIds(selectedResources); this._updateMenuBar(); }; scout.Planner.prototype._renderSelectedResources = function(newIds, oldSelectedResources) { if (oldSelectedResources) { oldSelectedResources.forEach(function(resource) { resource.$resource.select(false); }); } this.selectedResources.forEach(function(resource) { resource.$resource.select(true); }); }; scout.Planner.prototype._renderSelectionRange = function() { var $startRow, $lastRow, from = this.selectionRange.from, to = this.selectionRange.to, startRow = this.selectedResources[0], lastRow = this.selectedResources[this.selectedResources.length - 1]; // remove old selector if (this.$selector) { this.$selector.remove(); $('.selected', this.$scale).select(false); } if (!startRow || !lastRow || !this.selectionRange.from || !this.selectionRange.to) { return; } $startRow = startRow.$resource; $lastRow = lastRow.$resource; // top and height var $parent = ($startRow[0].offsetTop <= $lastRow[0].offsetTop) ? $startRow : $lastRow; this.$selector = $parent.children('.resource-cells').appendDiv('selector'); this.$selector.css('height', $startRow.outerHeight() + Math.abs($lastRow[0].offsetTop - $startRow[0].offsetTop)); this.$selector.appendDiv('selector-resize-left').mousedown(this._onResizeMousedown.bind(this)); this.$selector.appendDiv('selector-resize-right').mousedown(this._onResizeMousedown.bind(this)); this.$selector .css('left', 'calc(' + this.transformLeft(from) + '% - 6px)') .css('width', 'calc(' + this.transformWidth(to - from) + '% + 12px)') .on('contextmenu', this._onRangeSelectorContextMenu.bind(this)); // colorize scale var $scaleItems = $('.timeline-small', this.$scale).children(); for (var i = 0; i < $scaleItems.length; i++) { var $item = $scaleItems.eq(i); if ($item.data('date-from') >= from && $item.data('date-to') <= to) { $item.addClass('selected'); } } }; scout.Planner.prototype._renderSelectedActivity = function() { this._updateMenuBar(); }; scout.Planner.prototype._renderLabel = function() { var label = this.label || ''; if (this.$scaleTitle) { this.$scaleTitle.text(label); } }; scout.Planner.prototype._resourcesByIds = function(ids) { return ids.map(this._resourceById.bind(this)); }; scout.Planner.prototype._activityById = function(id) { return this.activityMap[id]; }; scout.Planner.prototype._resourceById = function(id) { return this.resourceMap[id]; }; scout.Planner.prototype.setDisplayMode = function(displayMode) { this.displayMode = displayMode; this._yearPanel.setDisplayMode(displayMode); this._sendSetDisplayMode(displayMode); if (this.rendered) { this._renderDisplayMode(); } this.startRange = null; this.lastRange = null; }; scout.Planner.prototype.layoutYearPanel = function() { if (this.yearPanelVisible) { scout.scrollbars.update(this._yearPanel.$yearList); this._yearPanel._scrollYear(); } }; scout.Planner.prototype.setYearPanelVisible = function(visible) { if (this.yearPanelVisible === visible) { return; } this.yearPanelVisible = visible; if (this.rendered) { this._renderYearPanelVisible(true); } }; scout.Planner.prototype.setViewRangeFrom = function(date) { var diff = this.viewRange.to.getTime() - this.viewRange.from.getTime(), viewRange = new scout.DateRange(this.viewRange.from, this.viewRange.to); viewRange.from = date; viewRange.to = new Date(date.getTime() + diff); this.setViewRange(viewRange); }; scout.Planner.prototype.setViewRange = function(viewRange) { this.viewRange = viewRange; this._yearPanel.setViewRange(viewRange); this._yearPanel.selectDate(this.viewRange.from); this._sendSetViewRange(viewRange); if (this.rendered) { this._renderViewRange(); this._rerenderActivities(); this.validateLayoutTree(); } }; scout.Planner.prototype.selectRange = function(range, notifyServer) { notifyServer = notifyServer !== undefined ? notifyServer : true; this.selectionRange = range; if (notifyServer) { this._sendSetSelection(); } if (this.rendered) { this._renderSelectionRange(); } this._updateMenuBar(); }; scout.Planner.prototype.selectResources = function(resources, notifyServer) { var oldSelection = this.selectedResources; notifyServer = notifyServer !== undefined ? notifyServer : true; resources = scout.arrays.ensure(resources); // Make a copy so that original array stays untouched this.selectedResources = resources.slice(); if (notifyServer) { this._sendSetSelection(); } if (this.rendered) { this._renderSelectedResources('', oldSelection); } this._updateMenuBar(); }; /** * Returns true if a deselection happened. False if the given resources were not selected at all. */ scout.Planner.prototype.deselectResources = function(resources, notifyServer) { var deselected = false; resources = scout.arrays.ensure(resources); notifyServer = notifyServer !== undefined ? notifyServer : true; var selectedResources = this.selectedResources.slice(); // copy if (scout.arrays.removeAll(selectedResources, resources)) { this.selectResources(selectedResources, notifyServer); deselected = true; } return deselected; }; scout.Planner.prototype.insertResources = function(resources) { // Update model resources.forEach(function(resource) { this._initResource(resource); // Always insert new rows at the end, if the order is wrong a rowOrderChange event will follow this.resources.push(resource); }.bind(this)); // Update HTML if (this.rendered) { this._renderResources(resources); this.invalidateLayoutTree(); } }; scout.Planner.prototype.deleteResources = function(resources) { if (this.deselectResources(resources, false)) { this.selectRange({}, false); } resources.forEach(function(resource) { // Update model scout.arrays.remove(this.resources, resource); delete this.resourceMap[resource.id]; resource.activities.forEach(function(activity) { delete this.activityMap[activity.id]; }.bind(this)); // Update HTML if (this.rendered) { resource.$resource.remove(); delete resource.$resource; } }.bind(this)); this.invalidateLayoutTree(); }; scout.Planner.prototype.deleteAllResources = function() { // Update HTML if (this.rendered) { this._removeAllResources(); this.invalidateLayoutTree(); } // Update model this.resources = []; this.resourceMap = {}; this.activityMap = {}; this.selectResources([], false); this.selectRange({}, false); }; scout.Planner.prototype._updateResources = function(resources) { resources.forEach(function(updatedResource) { var oldResource = this.resourceMap[updatedResource.id]; if (!oldResource) { throw new Error('Update event received for non existing resource. ResourceId: ' + updatedResource.id); } // Replace old resource this._initResource(updatedResource); scout.arrays.replace(this.resources, oldResource, updatedResource); scout.arrays.replace(this.selectedResources, oldResource, updatedResource); // Replace old $resource if (this.rendered && oldResource.$resource) { var $updatedResource = $(this._buildResourceHtml(updatedResource)); oldResource.$resource.replaceWith($updatedResource); this._linkResource($updatedResource, updatedResource); } }.bind(this)); }; scout.Planner.prototype._sendSetDisplayMode = function(displayMode) { this._send('setDisplayMode', { displayMode: displayMode }); }; scout.Planner.prototype._sendSetViewRange = function(viewRange) { this._send('setViewRange', { viewRange: scout.dates.toJsonDateRange(viewRange) }); }; scout.Planner.prototype._sendSetSelection = function() { var selectionRange = scout.dates.toJsonDateRange(this.selectionRange), resourceIds = this.selectedResources.map(function(r) { return r.id; }); this._send('setSelection', { resourceIds: resourceIds, selectionRange: selectionRange }); }; scout.Planner.prototype._onResourcesInserted = function(resources) { this.insertResources(resources); }; scout.Planner.prototype._onResourcesDeleted = function(resourceIds) { var resources = this._resourcesByIds(resourceIds); this.deleteResources(resources); }; scout.Planner.prototype._onResourcesSelected = function(resourceIds) { var resources = this._resourcesByIds(resourceIds); this.selectResources(resources, false); }; scout.Planner.prototype._onAllResourcesDeleted = function() { this.deleteAllResources(); }; scout.Planner.prototype._onResourcesUpdated = function(resources) { this._updateResources(resources); }; scout.Planner.prototype.onModelAction = function(event) { if (event.type === 'resourcesInserted') { this._onResourcesInserted(event.resources); } else if (event.type === 'resourcesDeleted') { this._onResourcesDeleted(event.resourceIds); } else if (event.type === 'resourcesSelected') { this._onResourcesSelected(event.resourceIds); } else if (event.type === 'allResourcesDeleted') { this._onAllResourcesDeleted(); } else if (event.type === 'resourcesUpdated') { this._onResourcesUpdated(event.resources); } else { scout.Planner.parent.prototype.onModelAction.call(this, event); } };




© 2015 - 2025 Weber Informatics LLC | Privacy Policy