';
var title = titleTemplate.replace(/{count}/, clusterItems.length);
var clusterOptions = assign$4({}, options, this.itemSet.options);
var data = {
'content': clusterContent,
'title': title,
'group': group,
'uiItems': clusterItems,
'eventEmitter': this.itemSet.body.emitter,
'range': this.itemSet.body.range
};
cluster = this.createClusterItem(data, conversion, clusterOptions);
if (group) {
group.add(cluster);
cluster.group = group;
}
cluster.attach();
return cluster;
}
/**
* Drop cache
* @private
*/
}, {
key: "_dropLevelsCache",
value: function _dropLevelsCache() {
this.cache = {};
this.cacheLevel = -1;
this.cache[this.cacheLevel] = [];
}
}]);
return ClusterGenerator;
}();
var UNGROUPED$2 = '__ungrouped__'; // reserved group id for ungrouped items
var BACKGROUND$2 = '__background__'; // reserved group id for background items without group
var ItemSet =
/*#__PURE__*/
function (_Component) {
inherits$1(ItemSet, _Component);
/**
* @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
* @param {Object} [options] See ItemSet.setOptions for the available options.
* @constructor ItemSet
* @extends Component
*/
function ItemSet(body, options) {
var _this;
classCallCheck$1(this, ItemSet);
_this = possibleConstructorReturn$1(this, getPrototypeOf$4(ItemSet).call(this));
_this.body = body;
_this.defaultOptions = {
type: null,
// 'box', 'point', 'range', 'background'
orientation: {
item: 'bottom' // item orientation: 'top' or 'bottom'
},
align: 'auto',
// alignment of box items
stack: true,
stackSubgroups: true,
groupOrderSwap: function groupOrderSwap(fromGroup, toGroup, groups) {
// eslint-disable-line no-unused-vars
var targetOrder = toGroup.order;
toGroup.order = fromGroup.order;
fromGroup.order = targetOrder;
},
groupOrder: 'order',
selectable: true,
multiselect: false,
itemsAlwaysDraggable: {
item: false,
range: false
},
editable: {
updateTime: false,
updateGroup: false,
add: false,
remove: false,
overrideItems: false
},
groupEditable: {
order: false,
add: false,
remove: false
},
snap: TimeStep.snap,
// Only called when `objectData.target === 'item'.
onDropObjectOnItem: function onDropObjectOnItem(objectData, item, callback) {
callback(item);
},
onAdd: function onAdd(item, callback) {
callback(item);
},
onUpdate: function onUpdate(item, callback) {
callback(item);
},
onMove: function onMove(item, callback) {
callback(item);
},
onRemove: function onRemove(item, callback) {
callback(item);
},
onMoving: function onMoving(item, callback) {
callback(item);
},
onAddGroup: function onAddGroup(item, callback) {
callback(item);
},
onMoveGroup: function onMoveGroup(item, callback) {
callback(item);
},
onRemoveGroup: function onRemoveGroup(item, callback) {
callback(item);
},
margin: {
item: {
horizontal: 10,
vertical: 10
},
axis: 20
},
showTooltips: true,
tooltip: {
followMouse: false,
overflowMethod: 'flip',
delay: 500
},
tooltipOnItemUpdateTime: false
}; // options is shared by this ItemSet and all its items
_this.options = util$2.extend({}, _this.defaultOptions);
_this.options.rtl = options.rtl;
_this.options.onTimeout = options.onTimeout; // options for getting items from the DataSet with the correct type
_this.itemOptions = {
type: {
start: 'Date',
end: 'Date'
}
};
_this.conversion = {
toScreen: body.util.toScreen,
toTime: body.util.toTime
};
_this.dom = {};
_this.props = {};
_this.hammer = null;
var me = assertThisInitialized$1(_this);
_this.itemsData = null; // DataSet
_this.groupsData = null; // DataSet
_this.itemsSettingTime = null;
_this.initialItemSetDrawn = false;
_this.userContinueNotBail = null;
_this.sequentialSelection = false; // listeners for the DataSet of the items
_this.itemListeners = {
'add': function add(event, params, senderId) {
// eslint-disable-line no-unused-vars
me._onAdd(params.items);
if (me.options.cluster) {
me.clusterGenerator.setItems(me.items, {
applyOnChangedLevel: false
});
}
me.redraw();
},
'update': function update(event, params, senderId) {
// eslint-disable-line no-unused-vars
me._onUpdate(params.items);
if (me.options.cluster) {
me.clusterGenerator.setItems(me.items, {
applyOnChangedLevel: false
});
}
me.redraw();
},
'remove': function remove(event, params, senderId) {
// eslint-disable-line no-unused-vars
me._onRemove(params.items);
if (me.options.cluster) {
me.clusterGenerator.setItems(me.items, {
applyOnChangedLevel: false
});
}
me.redraw();
}
}; // listeners for the DataSet of the groups
_this.groupListeners = {
'add': function add(event, params, senderId) {
// eslint-disable-line no-unused-vars
me._onAddGroups(params.items);
if (me.groupsData && me.groupsData.length > 0) {
var _context;
var groupsData = me.groupsData.getDataSet();
forEach$3(_context = groupsData.get()).call(_context, function (groupData) {
if (groupData.nestedGroups) {
var _context2;
if (groupData.showNested != false) {
groupData.showNested = true;
}
var updatedGroups = [];
forEach$3(_context2 = groupData.nestedGroups).call(_context2, function (nestedGroupId) {
var updatedNestedGroup = groupsData.get(nestedGroupId);
if (!updatedNestedGroup) {
return;
}
updatedNestedGroup.nestedInGroup = groupData.id;
if (groupData.showNested == false) {
updatedNestedGroup.visible = false;
}
updatedGroups = concat$2(updatedGroups).call(updatedGroups, updatedNestedGroup);
});
groupsData.update(updatedGroups, senderId);
}
});
}
},
'update': function update(event, params, senderId) {
// eslint-disable-line no-unused-vars
me._onUpdateGroups(params.items);
},
'remove': function remove(event, params, senderId) {
// eslint-disable-line no-unused-vars
me._onRemoveGroups(params.items);
}
};
_this.items = {}; // object with an Item for every data item
_this.groups = {}; // Group object for every group
_this.groupIds = [];
_this.selection = []; // list with the ids of all selected nodes
_this.popup = null;
_this.popupTimer = null;
_this.touchParams = {}; // stores properties while dragging
_this.groupTouchParams = {
group: null,
isDragging: false
}; // create the HTML DOM
_this._create();
_this.setOptions(options);
_this.clusters = [];
return _this;
}
/**
* Create the HTML DOM for the ItemSet
*/
createClass$1(ItemSet, [{
key: "_create",
value: function _create() {
var _this2 = this,
_context3,
_context4,
_context5,
_context6,
_context7,
_context8,
_context9,
_context10,
_context11,
_context12,
_context13,
_context14,
_context15,
_context16,
_context17;
var frame = document.createElement('div');
frame.className = 'vis-itemset';
frame['vis-itemset'] = this;
this.dom.frame = frame; // create background panel
var background = document.createElement('div');
background.className = 'vis-background';
frame.appendChild(background);
this.dom.background = background; // create foreground panel
var foreground = document.createElement('div');
foreground.className = 'vis-foreground';
frame.appendChild(foreground);
this.dom.foreground = foreground; // create axis panel
var axis = document.createElement('div');
axis.className = 'vis-axis';
this.dom.axis = axis; // create labelset
var labelSet = document.createElement('div');
labelSet.className = 'vis-labelset';
this.dom.labelSet = labelSet; // create ungrouped Group
this._updateUngrouped(); // create background Group
var backgroundGroup = new BackgroundGroup(BACKGROUND$2, null, this);
backgroundGroup.show();
this.groups[BACKGROUND$2] = backgroundGroup; // attach event listeners
// Note: we bind to the centerContainer for the case where the height
// of the center container is larger than of the ItemSet, so we
// can click in the empty area to create a new item or deselect an item.
this.hammer = new Hammer$1(this.body.dom.centerContainer); // drag items when selected
this.hammer.on('hammer.input', function (event) {
if (event.isFirst) {
_this2._onTouch(event);
}
});
this.hammer.on('panstart', bind$2(_context3 = this._onDragStart).call(_context3, this));
this.hammer.on('panmove', bind$2(_context4 = this._onDrag).call(_context4, this));
this.hammer.on('panend', bind$2(_context5 = this._onDragEnd).call(_context5, this));
this.hammer.get('pan').set({
threshold: 5,
direction: Hammer$1.ALL
}); // single select (or unselect) when tapping an item
this.hammer.on('tap', bind$2(_context6 = this._onSelectItem).call(_context6, this)); // multi select when holding mouse/touch, or on ctrl+click
this.hammer.on('press', bind$2(_context7 = this._onMultiSelectItem).call(_context7, this)); // add item on doubletap
this.hammer.on('doubletap', bind$2(_context8 = this._onAddItem).call(_context8, this));
if (this.options.rtl) {
this.groupHammer = new Hammer$1(this.body.dom.rightContainer);
} else {
this.groupHammer = new Hammer$1(this.body.dom.leftContainer);
}
this.groupHammer.on('tap', bind$2(_context9 = this._onGroupClick).call(_context9, this));
this.groupHammer.on('panstart', bind$2(_context10 = this._onGroupDragStart).call(_context10, this));
this.groupHammer.on('panmove', bind$2(_context11 = this._onGroupDrag).call(_context11, this));
this.groupHammer.on('panend', bind$2(_context12 = this._onGroupDragEnd).call(_context12, this));
this.groupHammer.get('pan').set({
threshold: 5,
direction: Hammer$1.DIRECTION_VERTICAL
});
this.body.dom.centerContainer.addEventListener('mouseover', bind$2(_context13 = this._onMouseOver).call(_context13, this));
this.body.dom.centerContainer.addEventListener('mouseout', bind$2(_context14 = this._onMouseOut).call(_context14, this));
this.body.dom.centerContainer.addEventListener('mousemove', bind$2(_context15 = this._onMouseMove).call(_context15, this)); // right-click on timeline
this.body.dom.centerContainer.addEventListener('contextmenu', bind$2(_context16 = this._onDragEnd).call(_context16, this));
this.body.dom.centerContainer.addEventListener('mousewheel', bind$2(_context17 = this._onMouseWheel).call(_context17, this)); // attach to the DOM
this.show();
}
/**
* Set options for the ItemSet. Existing options will be extended/overwritten.
* @param {Object} [options] The following options are available:
* {string} type
* Default type for the items. Choose from 'box'
* (default), 'point', 'range', or 'background'.
* The default style can be overwritten by
* individual items.
* {string} align
* Alignment for the items, only applicable for
* BoxItem. Choose 'center' (default), 'left', or
* 'right'.
* {string} orientation.item
* Orientation of the item set. Choose 'top' or
* 'bottom' (default).
* {Function} groupOrder
* A sorting function for ordering groups
* {boolean} stack
* If true (default), items will be stacked on
* top of each other.
* {number} margin.axis
* Margin between the axis and the items in pixels.
* Default is 20.
* {number} margin.item.horizontal
* Horizontal margin between items in pixels.
* Default is 10.
* {number} margin.item.vertical
* Vertical Margin between items in pixels.
* Default is 10.
* {number} margin.item
* Margin between items in pixels in both horizontal
* and vertical direction. Default is 10.
* {number} margin
* Set margin for both axis and items in pixels.
* {boolean} selectable
* If true (default), items can be selected.
* {boolean} multiselect
* If true, multiple items can be selected.
* False by default.
* {boolean} editable
* Set all editable options to true or false
* {boolean} editable.updateTime
* Allow dragging an item to an other moment in time
* {boolean} editable.updateGroup
* Allow dragging an item to an other group
* {boolean} editable.add
* Allow creating new items on double tap
* {boolean} editable.remove
* Allow removing items by clicking the delete button
* top right of a selected item.
* {Function(item: Item, callback: Function)} onAdd
* Callback function triggered when an item is about to be added:
* when the user double taps an empty space in the Timeline.
* {Function(item: Item, callback: Function)} onUpdate
* Callback function fired when an item is about to be updated.
* This function typically has to show a dialog where the user
* change the item. If not implemented, nothing happens.
* {Function(item: Item, callback: Function)} onMove
* Fired when an item has been moved. If not implemented,
* the move action will be accepted.
* {Function(item: Item, callback: Function)} onRemove
* Fired when an item is about to be deleted.
* If not implemented, the item will be always removed.
*/
}, {
key: "setOptions",
value: function setOptions(options) {
var _this3 = this;
if (options) {
var _context18, _context20;
// copy all options that we know
var fields = ['type', 'rtl', 'align', 'order', 'stack', 'stackSubgroups', 'selectable', 'multiselect', 'sequentialSelection', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'visibleFrameTemplate', 'hide', 'snap', 'groupOrderSwap', 'showTooltips', 'tooltip', 'tooltipOnItemUpdateTime', 'groupHeightMode', 'onTimeout'];
util$2.selectiveExtend(fields, this.options, options);
if ('itemsAlwaysDraggable' in options) {
if (typeof options.itemsAlwaysDraggable === 'boolean') {
this.options.itemsAlwaysDraggable.item = options.itemsAlwaysDraggable;
this.options.itemsAlwaysDraggable.range = false;
} else if (_typeof_1$2(options.itemsAlwaysDraggable) === 'object') {
util$2.selectiveExtend(['item', 'range'], this.options.itemsAlwaysDraggable, options.itemsAlwaysDraggable); // only allow range always draggable when item is always draggable as well
if (!this.options.itemsAlwaysDraggable.item) {
this.options.itemsAlwaysDraggable.range = false;
}
}
}
if ('sequentialSelection' in options) {
if (typeof options.sequentialSelection === 'boolean') {
this.options.sequentialSelection = options.sequentialSelection;
}
}
if ('orientation' in options) {
if (typeof options.orientation === 'string') {
this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom';
} else if (_typeof_1$2(options.orientation) === 'object' && 'item' in options.orientation) {
this.options.orientation.item = options.orientation.item;
}
}
if ('margin' in options) {
if (typeof options.margin === 'number') {
this.options.margin.axis = options.margin;
this.options.margin.item.horizontal = options.margin;
this.options.margin.item.vertical = options.margin;
} else if (_typeof_1$2(options.margin) === 'object') {
util$2.selectiveExtend(['axis'], this.options.margin, options.margin);
if ('item' in options.margin) {
if (typeof options.margin.item === 'number') {
this.options.margin.item.horizontal = options.margin.item;
this.options.margin.item.vertical = options.margin.item;
} else if (_typeof_1$2(options.margin.item) === 'object') {
util$2.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item);
}
}
}
}
forEach$3(_context18 = ['locale', 'locales']).call(_context18, function (key) {
if (key in options) {
_this3.options[key] = options[key];
}
});
if ('editable' in options) {
if (typeof options.editable === 'boolean') {
this.options.editable.updateTime = options.editable;
this.options.editable.updateGroup = options.editable;
this.options.editable.add = options.editable;
this.options.editable.remove = options.editable;
this.options.editable.overrideItems = false;
} else if (_typeof_1$2(options.editable) === 'object') {
util$2.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove', 'overrideItems'], this.options.editable, options.editable);
}
}
if ('groupEditable' in options) {
if (typeof options.groupEditable === 'boolean') {
this.options.groupEditable.order = options.groupEditable;
this.options.groupEditable.add = options.groupEditable;
this.options.groupEditable.remove = options.groupEditable;
} else if (_typeof_1$2(options.groupEditable) === 'object') {
util$2.selectiveExtend(['order', 'add', 'remove'], this.options.groupEditable, options.groupEditable);
}
} // callback functions
var addCallback = function addCallback(name) {
var fn = options[name];
if (fn) {
if (!(typeof fn === 'function')) {
var _context19;
throw new Error(concat$2(_context19 = "option ".concat(name, " must be a function ")).call(_context19, name, "(item, callback)"));
}
_this3.options[name] = fn;
}
};
forEach$3(_context20 = ['onDropObjectOnItem', 'onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving', 'onAddGroup', 'onMoveGroup', 'onRemoveGroup']).call(_context20, addCallback);
if (options.cluster) {
assign$4(this.options, {
cluster: options.cluster
});
if (!this.clusterGenerator) {
this.clusterGenerator = new ClusterGenerator(this);
}
this.clusterGenerator.setItems(this.items, {
applyOnChangedLevel: false
});
this.markDirty({
refreshItems: true,
restackGroups: true
});
this.redraw();
} else if (this.clusterGenerator) {
this._detachAllClusters();
this.clusters = [];
this.clusterGenerator = null;
this.options.cluster = undefined;
this.markDirty({
refreshItems: true,
restackGroups: true
});
this.redraw();
} else {
// force the itemSet to refresh: options like orientation and margins may be changed
this.markDirty();
}
}
}
/**
* Mark the ItemSet dirty so it will refresh everything with next redraw.
* Optionally, all items can be marked as dirty and be refreshed.
* @param {{refreshItems: boolean}} [options]
*/
}, {
key: "markDirty",
value: function markDirty(options) {
this.groupIds = [];
if (options) {
if (options.refreshItems) {
forEach$3(util$2).call(util$2, this.items, function (item) {
item.dirty = true;
if (item.displayed) item.redraw();
});
}
if (options.restackGroups) {
forEach$3(util$2).call(util$2, this.groups, function (group, key) {
if (key === BACKGROUND$2) return;
group.stackDirty = true;
});
}
}
}
/**
* Destroy the ItemSet
*/
}, {
key: "destroy",
value: function destroy() {
this.clearPopupTimer();
this.hide();
this.setItems(null);
this.setGroups(null);
this.hammer && this.hammer.destroy();
this.groupHammer && this.groupHammer.destroy();
this.hammer = null;
this.body = null;
this.conversion = null;
}
/**
* Hide the component from the DOM
*/
}, {
key: "hide",
value: function hide() {
// remove the frame containing the items
if (this.dom.frame.parentNode) {
this.dom.frame.parentNode.removeChild(this.dom.frame);
} // remove the axis with dots
if (this.dom.axis.parentNode) {
this.dom.axis.parentNode.removeChild(this.dom.axis);
} // remove the labelset containing all group labels
if (this.dom.labelSet.parentNode) {
this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
}
}
/**
* Show the component in the DOM (when not already visible).
*/
}, {
key: "show",
value: function show() {
// show frame containing the items
if (!this.dom.frame.parentNode) {
this.body.dom.center.appendChild(this.dom.frame);
} // show axis with dots
if (!this.dom.axis.parentNode) {
this.body.dom.backgroundVertical.appendChild(this.dom.axis);
} // show labelset containing labels
if (!this.dom.labelSet.parentNode) {
if (this.options.rtl) {
this.body.dom.right.appendChild(this.dom.labelSet);
} else {
this.body.dom.left.appendChild(this.dom.labelSet);
}
}
}
/**
* Activates the popup timer to show the given popup after a fixed time.
* @param {Popup} popup
*/
}, {
key: "setPopupTimer",
value: function setPopupTimer(popup) {
this.clearPopupTimer();
if (popup) {
var delay = this.options.tooltip.delay || typeof this.options.tooltip.delay === 'number' ? this.options.tooltip.delay : 500;
this.popupTimer = setTimeout$2(function () {
popup.show();
}, delay);
}
}
/**
* Clears the popup timer for the tooltip.
*/
}, {
key: "clearPopupTimer",
value: function clearPopupTimer() {
if (this.popupTimer != null) {
clearTimeout(this.popupTimer);
this.popupTimer = null;
}
}
/**
* Set selected items by their id. Replaces the current selection
* Unknown id's are silently ignored.
* @param {string[] | string} [ids] An array with zero or more id's of the items to be
* selected, or a single item id. If ids is undefined
* or an empty array, all items will be unselected.
*/
}, {
key: "setSelection",
value: function setSelection(ids) {
var _context21;
if (ids == undefined) {
ids = [];
}
if (!isArray$3(ids)) {
ids = [ids];
}
var idsToDeselect = filter$2(_context21 = this.selection).call(_context21, function (id) {
return indexOf$3(ids).call(ids, id) === -1;
}); // unselect currently selected items
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = getIterator$2(idsToDeselect), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var selectedId = _step.value;
var item = this.getItemById(selectedId);
if (item) {
item.unselect();
}
} // select items
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
this.selection = toConsumableArray$2(ids);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = getIterator$2(ids), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var id = _step2.value;
var _item2 = this.getItemById(id);
if (_item2) {
_item2.select();
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
/**
* Get the selected items by their id
* @return {Array} ids The ids of the selected items
*/
}, {
key: "getSelection",
value: function getSelection() {
var _context22;
return concat$2(_context22 = this.selection).call(_context22, []);
}
/**
* Get the id's of the currently visible items.
* @returns {Array} The ids of the visible items
*/
}, {
key: "getVisibleItems",
value: function getVisibleItems() {
var range = this.body.range.getRange();
var right;
var left;
if (this.options.rtl) {
right = this.body.util.toScreen(range.start);
left = this.body.util.toScreen(range.end);
} else {
left = this.body.util.toScreen(range.start);
right = this.body.util.toScreen(range.end);
}
var ids = [];
for (var groupId in this.groups) {
if (this.groups.hasOwnProperty(groupId)) {
var group = this.groups[groupId];
var rawVisibleItems = group.isVisible ? group.visibleItems : []; // filter the "raw" set with visibleItems into a set which is really
// visible by pixels
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = getIterator$2(rawVisibleItems), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var item = _step3.value;
// TODO: also check whether visible vertically
if (this.options.rtl) {
if (item.right < left && item.right + item.width > right) {
ids.push(item.id);
}
} else {
if (item.left < right && item.left + item.width > left) {
ids.push(item.id);
}
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
}
return ids;
}
/**
* Get the id's of the currently visible groups.
* @returns {Array} The ids of the visible groups
*/
}, {
key: "getVisibleGroups",
value: function getVisibleGroups() {
var ids = [];
for (var groupId in this.groups) {
if (this.groups.hasOwnProperty(groupId)) {
var group = this.groups[groupId];
if (group.isVisible) {
ids.push(groupId);
}
}
}
return ids;
}
/**
* get item by id
* @param {string} id
* @return {object} item
*/
}, {
key: "getItemById",
value: function getItemById(id) {
var _context23;
return this.items[id] || find$2(_context23 = this.clusters).call(_context23, function (cluster) {
return cluster.id === id;
});
}
/**
* Deselect a selected item
* @param {string | number} id
* @private
*/
}, {
key: "_deselect",
value: function _deselect(id) {
var selection = this.selection;
for (var i = 0, ii = selection.length; i < ii; i++) {
if (selection[i] == id) {
// non-strict comparison!
splice$2(selection).call(selection, i, 1);
break;
}
}
}
/**
* Repaint the component
* @return {boolean} Returns true if the component is resized
*/
}, {
key: "redraw",
value: function redraw() {
var _this4 = this;
var margin = this.options.margin;
var range = this.body.range;
var asSize = util$2.option.asSize;
var options = this.options;
var orientation = options.orientation.item;
var resized = false;
var frame = this.dom.frame; // recalculate absolute position (before redrawing groups)
this.props.top = this.body.domProps.top.height + this.body.domProps.border.top;
if (this.options.rtl) {
this.props.right = this.body.domProps.right.width + this.body.domProps.border.right;
} else {
this.props.left = this.body.domProps.left.width + this.body.domProps.border.left;
} // update class name
frame.className = 'vis-itemset';
if (this.options.cluster) {
this._clusterItems();
} // reorder the groups (if needed)
resized = this._orderGroups() || resized; // check whether zoomed (in that case we need to re-stack everything)
// TODO: would be nicer to get this as a trigger from Range
var visibleInterval = range.end - range.start;
var zoomed = visibleInterval != this.lastVisibleInterval || this.props.width != this.props.lastWidth;
var scrolled = range.start != this.lastRangeStart;
var changedStackOption = options.stack != this.lastStack;
var changedStackSubgroupsOption = options.stackSubgroups != this.lastStackSubgroups;
var forceRestack = zoomed || scrolled || changedStackOption || changedStackSubgroupsOption;
this.lastVisibleInterval = visibleInterval;
this.lastRangeStart = range.start;
this.lastStack = options.stack;
this.lastStackSubgroups = options.stackSubgroups;
this.props.lastWidth = this.props.width;
var firstGroup = this._firstGroup();
var firstMargin = {
item: margin.item,
axis: margin.axis
};
var nonFirstMargin = {
item: margin.item,
axis: margin.item.vertical / 2
};
var height = 0;
var minHeight = margin.axis + margin.item.vertical; // redraw the background group
this.groups[BACKGROUND$2].redraw(range, nonFirstMargin, forceRestack);
var redrawQueue = {};
var redrawQueueLength = 0; // collect redraw functions
forEach$3(util$2).call(util$2, this.groups, function (group, key) {
if (key === BACKGROUND$2) return;
var groupMargin = group == firstGroup ? firstMargin : nonFirstMargin;
var returnQueue = true;
redrawQueue[key] = group.redraw(range, groupMargin, forceRestack, returnQueue);
redrawQueueLength = redrawQueue[key].length;
});
var needRedraw = redrawQueueLength > 0;
if (needRedraw) {
(function () {
var redrawResults = {};
var _loop = function _loop(i) {
forEach$3(util$2).call(util$2, redrawQueue, function (fns, key) {
redrawResults[key] = fns[i]();
});
};
for (var i = 0; i < redrawQueueLength; i++) {
_loop(i);
} // redraw all regular groups
forEach$3(util$2).call(util$2, _this4.groups, function (group, key) {
if (key === BACKGROUND$2) return;
var groupResized = redrawResults[key];
resized = groupResized || resized;
height += group.height;
});
height = Math.max(height, minHeight);
})();
}
height = Math.max(height, minHeight); // update frame height
frame.style.height = asSize(height); // calculate actual size
this.props.width = frame.offsetWidth;
this.props.height = height; // reposition axis
this.dom.axis.style.top = asSize(orientation == 'top' ? this.body.domProps.top.height + this.body.domProps.border.top : this.body.domProps.top.height + this.body.domProps.centerContainer.height);
if (this.options.rtl) {
this.dom.axis.style.right = '0';
} else {
this.dom.axis.style.left = '0';
}
this.initialItemSetDrawn = true; // check if this component is resized
resized = this._isResized() || resized;
return resized;
}
/**
* Get the first group, aligned with the axis
* @return {Group | null} firstGroup
* @private
*/
}, {
key: "_firstGroup",
value: function _firstGroup() {
var firstGroupIndex = this.options.orientation.item == 'top' ? 0 : this.groupIds.length - 1;
var firstGroupId = this.groupIds[firstGroupIndex];
var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED$2];
return firstGroup || null;
}
/**
* Create or delete the group holding all ungrouped items. This group is used when
* there are no groups specified.
* @protected
*/
}, {
key: "_updateUngrouped",
value: function _updateUngrouped() {
var ungrouped = this.groups[UNGROUPED$2];
var item;
var itemId;
if (this.groupsData) {
// remove the group holding all ungrouped items
if (ungrouped) {
ungrouped.hide();
delete this.groups[UNGROUPED$2];
for (itemId in this.items) {
if (this.items.hasOwnProperty(itemId)) {
item = this.items[itemId];
item.parent && item.parent.remove(item);
var groupId = this.getGroupId(item.data);
var group = this.groups[groupId];
group && group.add(item) || item.hide();
}
}
}
} else {
// create a group holding all (unfiltered) items
if (!ungrouped) {
var id = null;
var data = null;
ungrouped = new Group(id, data, this);
this.groups[UNGROUPED$2] = ungrouped;
for (itemId in this.items) {
if (this.items.hasOwnProperty(itemId)) {
item = this.items[itemId];
ungrouped.add(item);
}
}
ungrouped.show();
}
}
}
/**
* Get the element for the labelset
* @return {HTMLElement} labelSet
*/
}, {
key: "getLabelSet",
value: function getLabelSet() {
return this.dom.labelSet;
}
/**
* Set items
* @param {vis.DataSet | null} items
*/
}, {
key: "setItems",
value: function setItems(items) {
this.itemsSettingTime = new Date();
var me = this;
var ids;
var oldItemsData = this.itemsData; // replace the dataset
if (!items) {
this.itemsData = null;
} else if (items instanceof DataSet || items instanceof DataView) {
this.itemsData = items;
} else {
throw new TypeError('Data must be an instance of DataSet or DataView');
}
if (oldItemsData) {
// unsubscribe from old dataset
forEach$3(util$2).call(util$2, this.itemListeners, function (callback, event) {
oldItemsData.off(event, callback);
}); // remove all drawn items
ids = oldItemsData.getIds();
this._onRemove(ids);
}
if (this.itemsData) {
// subscribe to new dataset
var id = this.id;
forEach$3(util$2).call(util$2, this.itemListeners, function (callback, event) {
me.itemsData.on(event, callback, id);
}); // add all new items
ids = this.itemsData.getIds();
this._onAdd(ids); // update the group holding all ungrouped items
this._updateUngrouped();
}
this.body.emitter.emit('_change', {
queue: true
});
}
/**
* Get the current items
* @returns {vis.DataSet | null}
*/
}, {
key: "getItems",
value: function getItems() {
return this.itemsData;
}
/**
* Set groups
* @param {vis.DataSet} groups
*/
}, {
key: "setGroups",
value: function setGroups(groups) {
var me = this;
var ids; // unsubscribe from current dataset
if (this.groupsData) {
forEach$3(util$2).call(util$2, this.groupListeners, function (callback, event) {
me.groupsData.off(event, callback);
}); // remove all drawn groups
ids = this.groupsData.getIds();
this.groupsData = null;
this._onRemoveGroups(ids); // note: this will cause a redraw
} // replace the dataset
if (!groups) {
this.groupsData = null;
} else if (groups instanceof DataSet || groups instanceof DataView) {
this.groupsData = groups;
} else {
throw new TypeError('Data must be an instance of DataSet or DataView');
}
if (this.groupsData) {
var _context24;
// go over all groups nesting
var groupsData = this.groupsData;
if (this.groupsData instanceof DataView) {
groupsData = this.groupsData.getDataSet();
}
forEach$3(_context24 = groupsData.get()).call(_context24, function (group) {
if (group.nestedGroups) {
var _context25;
forEach$3(_context25 = group.nestedGroups).call(_context25, function (nestedGroupId) {
var updatedNestedGroup = groupsData.get(nestedGroupId);
updatedNestedGroup.nestedInGroup = group.id;
if (group.showNested == false) {
updatedNestedGroup.visible = false;
}
groupsData.update(updatedNestedGroup);
});
}
}); // subscribe to new dataset
var id = this.id;
forEach$3(util$2).call(util$2, this.groupListeners, function (callback, event) {
me.groupsData.on(event, callback, id);
}); // draw all ms
ids = this.groupsData.getIds();
this._onAddGroups(ids);
} // update the group holding all ungrouped items
this._updateUngrouped(); // update the order of all items in each group
this._order();
if (this.options.cluster) {
this.clusterGenerator.updateData();
this._clusterItems();
this.markDirty({
refreshItems: true,
restackGroups: true
});
}
this.body.emitter.emit('_change', {
queue: true
});
}
/**
* Get the current groups
* @returns {vis.DataSet | null} groups
*/
}, {
key: "getGroups",
value: function getGroups() {
return this.groupsData;
}
/**
* Remove an item by its id
* @param {string | number} id
*/
}, {
key: "removeItem",
value: function removeItem(id) {
var item = this.itemsData.get(id);
var dataset = this.itemsData.getDataSet();
if (item) {
// confirm deletion
this.options.onRemove(item, function (item) {
if (item) {
// remove by id here, it is possible that an item has no id defined
// itself, so better not delete by the item itself
dataset.remove(id);
}
});
}
}
/**
* Get the time of an item based on it's data and options.type
* @param {Object} itemData
* @returns {string} Returns the type
* @private
*/
}, {
key: "_getType",
value: function _getType(itemData) {
return itemData.type || this.options.type || (itemData.end ? 'range' : 'box');
}
/**
* Get the group id for an item
* @param {Object} itemData
* @returns {string} Returns the groupId
* @private
*/
}, {
key: "getGroupId",
value: function getGroupId(itemData) {
var type = this._getType(itemData);
if (type == 'background' && itemData.group == undefined) {
return BACKGROUND$2;
} else {
return this.groupsData ? itemData.group : UNGROUPED$2;
}
}
/**
* Handle updated items
* @param {number[]} ids
* @protected
*/
}, {
key: "_onUpdate",
value: function _onUpdate(ids) {
var _this5 = this;
var me = this;
forEach$3(ids).call(ids, function (id) {
var itemData = me.itemsData.get(id, me.itemOptions);
var item = me.items[id];
var type = itemData ? me._getType(itemData) : null;
var constructor = ItemSet.types[type];
var selected;
if (item) {
// update item
if (!constructor || !(item instanceof constructor)) {
// item type has changed, delete the item and recreate it
selected = item.selected; // preserve selection of this item
me._removeItem(item);
item = null;
} else {
me._updateItem(item, itemData);
}
}
if (!item && itemData) {
// create item
if (constructor) {
item = new constructor(itemData, me.conversion, me.options);
item.id = id; // TODO: not so nice setting id afterwards
me._addItem(item);
if (selected) {
_this5.selection.push(id);
item.select();
}
} else {
throw new TypeError("Unknown item type \"".concat(type, "\""));
}
}
});
this._order();
if (this.options.cluster) {
this.clusterGenerator.setItems(this.items, {
applyOnChangedLevel: false
});
this._clusterItems();
}
this.body.emitter.emit('_change', {
queue: true
});
}
/**
* Handle removed items
* @param {number[]} ids
* @protected
*/
}, {
key: "_onRemove",
value: function _onRemove(ids) {
var count = 0;
var me = this;
forEach$3(ids).call(ids, function (id) {
var item = me.items[id];
if (item) {
count++;
me._removeItem(item);
}
});
if (count) {
// update order
this._order();
this.body.emitter.emit('_change', {
queue: true
});
}
}
/**
* Update the order of item in all groups
* @private
*/
}, {
key: "_order",
value: function _order() {
// reorder the items in all groups
// TODO: optimization: only reorder groups affected by the changed items
forEach$3(util$2).call(util$2, this.groups, function (group) {
group.order();
});
}
/**
* Handle updated groups
* @param {number[]} ids
* @private
*/
}, {
key: "_onUpdateGroups",
value: function _onUpdateGroups(ids) {
this._onAddGroups(ids);
}
/**
* Handle changed groups (added or updated)
* @param {number[]} ids
* @private
*/
}, {
key: "_onAddGroups",
value: function _onAddGroups(ids) {
var me = this;
forEach$3(ids).call(ids, function (id) {
var groupData = me.groupsData.get(id);
var group = me.groups[id];
if (!group) {
// check for reserved ids
if (id == UNGROUPED$2 || id == BACKGROUND$2) {
throw new Error("Illegal group id. ".concat(id, " is a reserved id."));
}
var groupOptions = create$4(me.options);
util$2.extend(groupOptions, {
height: null
});
group = new Group(id, groupData, me);
me.groups[id] = group; // add items with this groupId to the new group
for (var itemId in me.items) {
if (me.items.hasOwnProperty(itemId)) {
var item = me.items[itemId];
if (item.data.group == id) {
group.add(item);
}
}
}
group.order();
group.show();
} else {
// update group
group.setData(groupData);
}
});
this.body.emitter.emit('_change', {
queue: true
});
}
/**
* Handle removed groups
* @param {number[]} ids
* @private
*/
}, {
key: "_onRemoveGroups",
value: function _onRemoveGroups(ids) {
var groups = this.groups;
forEach$3(ids).call(ids, function (id) {
var group = groups[id];
if (group) {
group.hide();
delete groups[id];
}
});
if (this.options.cluster) {
this.clusterGenerator.updateData();
this._clusterItems();
}
this.markDirty({
restackGroups: !!this.options.cluster
});
this.body.emitter.emit('_change', {
queue: true
});
}
/**
* Reorder the groups if needed
* @return {boolean} changed
* @private
*/
}, {
key: "_orderGroups",
value: function _orderGroups() {
if (this.groupsData) {
// reorder the groups
var groupIds = this.groupsData.getIds({
order: this.options.groupOrder
});
groupIds = this._orderNestedGroups(groupIds);
var changed = !util$2.equalArray(groupIds, this.groupIds);
if (changed) {
// hide all groups, removes them from the DOM
var groups = this.groups;
forEach$3(groupIds).call(groupIds, function (groupId) {
groups[groupId].hide();
}); // show the groups again, attach them to the DOM in correct order
forEach$3(groupIds).call(groupIds, function (groupId) {
groups[groupId].show();
});
this.groupIds = groupIds;
}
return changed;
} else {
return false;
}
}
/**
* Reorder the nested groups
*
* @param {Array.} groupIds
* @returns {Array.}
* @private
*/
}, {
key: "_orderNestedGroups",
value: function _orderNestedGroups(groupIds) {
var _this6 = this;
/**
* Recursively order nested groups
*
* @param {ItemSet} t
* @param {Array.} groupIds
* @returns {Array.}
* @private
*/
function getOrderedNestedGroups(t, groupIds) {
var result = [];
forEach$3(groupIds).call(groupIds, function (groupId) {
result.push(groupId);
var groupData = t.groupsData.get(groupId);
if (groupData.nestedGroups) {
var _context26;
var nestedGroupIds = map$2(_context26 = t.groupsData.get({
filter: function filter(nestedGroup) {
return nestedGroup.nestedInGroup == groupId;
},
order: t.options.groupOrder
})).call(_context26, function (nestedGroup) {
return nestedGroup.id;
});
result = concat$2(result).call(result, getOrderedNestedGroups(t, nestedGroupIds));
}
});
return result;
}
var topGroupIds = filter$2(groupIds).call(groupIds, function (groupId) {
return !_this6.groupsData.get(groupId).nestedInGroup;
});
return getOrderedNestedGroups(this, topGroupIds);
}
/**
* Add a new item
* @param {Item} item
* @private
*/
}, {
key: "_addItem",
value: function _addItem(item) {
this.items[item.id] = item; // add to group
var groupId = this.getGroupId(item.data);
var group = this.groups[groupId];
if (!group) {
item.groupShowing = false;
} else if (group && group.data && group.data.showNested) {
item.groupShowing = true;
}
if (group) group.add(item);
}
/**
* Update an existing item
* @param {Item} item
* @param {Object} itemData
* @private
*/
}, {
key: "_updateItem",
value: function _updateItem(item, itemData) {
// update the items data (will redraw the item when displayed)
item.setData(itemData);
var groupId = this.getGroupId(item.data);
var group = this.groups[groupId];
if (!group) {
item.groupShowing = false;
} else if (group && group.data && group.data.showNested) {
item.groupShowing = true;
}
}
/**
* Delete an item from the ItemSet: remove it from the DOM, from the map
* with items, and from the map with visible items, and from the selection
* @param {Item} item
* @private
*/
}, {
key: "_removeItem",
value: function _removeItem(item) {
var _context27, _context28;
// remove from DOM
item.hide(); // remove from items
delete this.items[item.id]; // remove from selection
var index = indexOf$3(_context27 = this.selection).call(_context27, item.id);
if (index != -1) splice$2(_context28 = this.selection).call(_context28, index, 1); // remove from group
item.parent && item.parent.remove(item); // remove Tooltip from DOM
if (this.popup != null) {
this.popup.hide();
}
}
/**
* Create an array containing all items being a range (having an end date)
* @param {Array.