initial commit
This commit is contained in:
236
node_modules/vis/lib/timeline/component/item/BackgroundItem.js
generated
vendored
Normal file
236
node_modules/vis/lib/timeline/component/item/BackgroundItem.js
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
var Item = require('./Item');
|
||||
var BackgroundGroup = require('../BackgroundGroup');
|
||||
var RangeItem = require('./RangeItem');
|
||||
|
||||
/**
|
||||
* @constructor BackgroundItem
|
||||
* @extends Item
|
||||
* @param {Object} data Object containing parameters start, end
|
||||
* content, className.
|
||||
* @param {{toScreen: function, toTime: function}} conversion
|
||||
* Conversion functions from time to screen and vice versa
|
||||
* @param {Object} [options] Configuration options
|
||||
* // TODO: describe options
|
||||
* // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation
|
||||
*/
|
||||
function BackgroundItem (data, conversion, options) {
|
||||
this.props = {
|
||||
content: {
|
||||
width: 0
|
||||
}
|
||||
};
|
||||
this.overflow = false; // if contents can overflow (css styling), this flag is set to true
|
||||
|
||||
// validate data
|
||||
if (data) {
|
||||
if (data.start == undefined) {
|
||||
throw new Error('Property "start" missing in item ' + data.id);
|
||||
}
|
||||
if (data.end == undefined) {
|
||||
throw new Error('Property "end" missing in item ' + data.id);
|
||||
}
|
||||
}
|
||||
|
||||
Item.call(this, data, conversion, options);
|
||||
}
|
||||
|
||||
BackgroundItem.prototype = new Item (null, null, null);
|
||||
|
||||
BackgroundItem.prototype.baseClassName = 'vis-item vis-background';
|
||||
|
||||
BackgroundItem.prototype.stack = false;
|
||||
|
||||
/**
|
||||
* Check whether this item is visible inside given range
|
||||
* @param {vis.Range} range with a timestamp for start and end
|
||||
* @returns {boolean} True if visible
|
||||
*/
|
||||
BackgroundItem.prototype.isVisible = function(range) {
|
||||
// determine visibility
|
||||
return (this.data.start < range.end) && (this.data.end > range.start);
|
||||
};
|
||||
|
||||
BackgroundItem.prototype._createDomElement = function() {
|
||||
if (!this.dom) {
|
||||
// create DOM
|
||||
this.dom = {};
|
||||
|
||||
// background box
|
||||
this.dom.box = document.createElement('div');
|
||||
// className is updated in redraw()
|
||||
|
||||
// frame box (to prevent the item contents from overflowing
|
||||
this.dom.frame = document.createElement('div');
|
||||
this.dom.frame.className = 'vis-item-overflow';
|
||||
this.dom.box.appendChild(this.dom.frame);
|
||||
|
||||
// contents box
|
||||
this.dom.content = document.createElement('div');
|
||||
this.dom.content.className = 'vis-item-content';
|
||||
this.dom.frame.appendChild(this.dom.content);
|
||||
|
||||
// Note: we do NOT attach this item as attribute to the DOM,
|
||||
// such that background items cannot be selected
|
||||
//this.dom.box['timeline-item'] = this;
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundItem.prototype._appendDomElement = function() {
|
||||
if (!this.parent) {
|
||||
throw new Error('Cannot redraw item: no parent attached');
|
||||
}
|
||||
if (!this.dom.box.parentNode) {
|
||||
var background = this.parent.dom.background;
|
||||
if (!background) {
|
||||
throw new Error('Cannot redraw item: parent has no background container element');
|
||||
}
|
||||
background.appendChild(this.dom.box);
|
||||
}
|
||||
this.displayed = true;
|
||||
}
|
||||
|
||||
BackgroundItem.prototype._updateDirtyDomComponents = function() {
|
||||
// update dirty DOM. An item is marked dirty when:
|
||||
// - the item is not yet rendered
|
||||
// - the item's data is changed
|
||||
// - the item is selected/deselected
|
||||
if (this.dirty) {
|
||||
this._updateContents(this.dom.content);
|
||||
this._updateDataAttributes(this.dom.content);
|
||||
this._updateStyle(this.dom.box);
|
||||
|
||||
// update class
|
||||
var className = (this.data.className ? (' ' + this.data.className) : '') +
|
||||
(this.selected ? ' vis-selected' : '');
|
||||
this.dom.box.className = this.baseClassName + className;
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundItem.prototype._getDomComponentsSizes = function() {
|
||||
// determine from css whether this box has overflow
|
||||
this.overflow = window.getComputedStyle(this.dom.content).overflow !== 'hidden';
|
||||
return {
|
||||
content: {
|
||||
width: this.dom.content.offsetWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundItem.prototype._updateDomComponentsSizes = function(sizes) {
|
||||
// recalculate size
|
||||
this.props.content.width = sizes.content.width;
|
||||
this.height = 0; // set height zero, so this item will be ignored when stacking items
|
||||
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
BackgroundItem.prototype._repaintDomAdditionals = function() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaint the item
|
||||
* @param {boolean} [returnQueue=false] return the queue
|
||||
* @return {boolean} the redraw result or the redraw queue if returnQueue=true
|
||||
*/
|
||||
BackgroundItem.prototype.redraw = function(returnQueue) {
|
||||
var sizes
|
||||
var queue = [
|
||||
// create item DOM
|
||||
this._createDomElement.bind(this),
|
||||
|
||||
// append DOM to parent DOM
|
||||
this._appendDomElement.bind(this),
|
||||
|
||||
this._updateDirtyDomComponents.bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
sizes = this._getDomComponentsSizes.bind(this)();
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
this._updateDomComponentsSizes.bind(this)(sizes);
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
// repaint DOM additionals
|
||||
this._repaintDomAdditionals.bind(this)
|
||||
];
|
||||
|
||||
if (returnQueue) {
|
||||
return queue;
|
||||
} else {
|
||||
var result;
|
||||
queue.forEach(function (fn) {
|
||||
result = fn();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the item in the DOM (when not already visible). The items DOM will
|
||||
* be created when needed.
|
||||
*/
|
||||
BackgroundItem.prototype.show = RangeItem.prototype.show;
|
||||
|
||||
/**
|
||||
* Hide the item from the DOM (when visible)
|
||||
* @return {Boolean} changed
|
||||
*/
|
||||
BackgroundItem.prototype.hide = RangeItem.prototype.hide;
|
||||
|
||||
/**
|
||||
* Reposition the item horizontally
|
||||
* @Override
|
||||
*/
|
||||
BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX;
|
||||
|
||||
/**
|
||||
* Reposition the item vertically
|
||||
* @Override
|
||||
*/
|
||||
BackgroundItem.prototype.repositionY = function(margin) { // eslint-disable-line no-unused-vars
|
||||
var height;
|
||||
var orientation = this.options.orientation.item;
|
||||
|
||||
// special positioning for subgroups
|
||||
if (this.data.subgroup !== undefined) {
|
||||
// TODO: instead of calculating the top position of the subgroups here for every BackgroundItem, calculate the top of the subgroup once in Itemset
|
||||
var itemSubgroup = this.data.subgroup;
|
||||
|
||||
this.dom.box.style.height = this.parent.subgroups[itemSubgroup].height + 'px';
|
||||
|
||||
if (orientation == 'top') {
|
||||
this.dom.box.style.top = this.parent.top + this.parent.subgroups[itemSubgroup].top + 'px';
|
||||
} else {
|
||||
this.dom.box.style.top = (this.parent.top + this.parent.height - this.parent.subgroups[itemSubgroup].top - this.parent.subgroups[itemSubgroup].height) + 'px';
|
||||
}
|
||||
this.dom.box.style.bottom = '';
|
||||
}
|
||||
// and in the case of no subgroups:
|
||||
else {
|
||||
// we want backgrounds with groups to only show in groups.
|
||||
if (this.parent instanceof BackgroundGroup) {
|
||||
// if the item is not in a group:
|
||||
height = Math.max(this.parent.height,
|
||||
this.parent.itemSet.body.domProps.center.height,
|
||||
this.parent.itemSet.body.domProps.centerContainer.height);
|
||||
this.dom.box.style.bottom = orientation == 'bottom' ? '0' : '';
|
||||
this.dom.box.style.top = orientation == 'top' ? '0' : '';
|
||||
}
|
||||
else {
|
||||
height = this.parent.height;
|
||||
// same alignment for items when orientation is top or bottom
|
||||
this.dom.box.style.top = this.parent.top + 'px';
|
||||
this.dom.box.style.bottom = '';
|
||||
}
|
||||
}
|
||||
this.dom.box.style.height = height + 'px';
|
||||
};
|
||||
|
||||
module.exports = BackgroundItem;
|
||||
360
node_modules/vis/lib/timeline/component/item/BoxItem.js
generated
vendored
Normal file
360
node_modules/vis/lib/timeline/component/item/BoxItem.js
generated
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
var Item = require('./Item');
|
||||
|
||||
/**
|
||||
* @constructor BoxItem
|
||||
* @extends Item
|
||||
* @param {Object} data Object containing parameters start
|
||||
* content, className.
|
||||
* @param {{toScreen: function, toTime: function}} conversion
|
||||
* Conversion functions from time to screen and vice versa
|
||||
* @param {Object} [options] Configuration options
|
||||
* // TODO: describe available options
|
||||
*/
|
||||
function BoxItem (data, conversion, options) {
|
||||
this.props = {
|
||||
dot: {
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
line: {
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
};
|
||||
this.options = options;
|
||||
// validate data
|
||||
if (data) {
|
||||
if (data.start == undefined) {
|
||||
throw new Error('Property "start" missing in item ' + data);
|
||||
}
|
||||
}
|
||||
|
||||
Item.call(this, data, conversion, options);
|
||||
}
|
||||
|
||||
BoxItem.prototype = new Item (null, null, null);
|
||||
|
||||
/**
|
||||
* Check whether this item is visible inside given range
|
||||
* @param {{start: number, end: number}} range with a timestamp for start and end
|
||||
* @returns {boolean} True if visible
|
||||
*/
|
||||
BoxItem.prototype.isVisible = function(range) {
|
||||
// determine visibility
|
||||
var isVisible;
|
||||
var align = this.options.align;
|
||||
var widthInMs = this.width * range.getMillisecondsPerPixel();
|
||||
|
||||
if (align == 'right') {
|
||||
isVisible = (this.data.start.getTime() > range.start ) && (this.data.start.getTime() - widthInMs < range.end);
|
||||
}
|
||||
else if (align == 'left') {
|
||||
isVisible = (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start.getTime() < range.end);
|
||||
}
|
||||
else {
|
||||
// default or 'center'
|
||||
isVisible = (this.data.start.getTime() + widthInMs/2 > range.start ) && (this.data.start.getTime() - widthInMs/2 < range.end);
|
||||
}
|
||||
return isVisible;
|
||||
};
|
||||
|
||||
BoxItem.prototype._createDomElement = function() {
|
||||
if (!this.dom) {
|
||||
// create DOM
|
||||
this.dom = {};
|
||||
|
||||
// create main box
|
||||
this.dom.box = document.createElement('DIV');
|
||||
|
||||
// contents box (inside the background box). used for making margins
|
||||
this.dom.content = document.createElement('DIV');
|
||||
this.dom.content.className = 'vis-item-content';
|
||||
this.dom.box.appendChild(this.dom.content);
|
||||
|
||||
// line to axis
|
||||
this.dom.line = document.createElement('DIV');
|
||||
this.dom.line.className = 'vis-line';
|
||||
|
||||
// dot on axis
|
||||
this.dom.dot = document.createElement('DIV');
|
||||
this.dom.dot.className = 'vis-dot';
|
||||
|
||||
// attach this item as attribute
|
||||
this.dom.box['timeline-item'] = this;
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
BoxItem.prototype._appendDomElement = function() {
|
||||
if (!this.parent) {
|
||||
throw new Error('Cannot redraw item: no parent attached');
|
||||
}
|
||||
if (!this.dom.box.parentNode) {
|
||||
var foreground = this.parent.dom.foreground;
|
||||
if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element');
|
||||
foreground.appendChild(this.dom.box);
|
||||
}
|
||||
if (!this.dom.line.parentNode) {
|
||||
var background = this.parent.dom.background;
|
||||
if (!background) throw new Error('Cannot redraw item: parent has no background container element');
|
||||
background.appendChild(this.dom.line);
|
||||
}
|
||||
if (!this.dom.dot.parentNode) {
|
||||
var axis = this.parent.dom.axis;
|
||||
if (!background) throw new Error('Cannot redraw item: parent has no axis container element');
|
||||
axis.appendChild(this.dom.dot);
|
||||
}
|
||||
this.displayed = true;
|
||||
}
|
||||
|
||||
BoxItem.prototype._updateDirtyDomComponents = function() {
|
||||
// An item is marked dirty when:
|
||||
// - the item is not yet rendered
|
||||
// - the item's data is changed
|
||||
// - the item is selected/deselected
|
||||
if (this.dirty) {
|
||||
this._updateContents(this.dom.content);
|
||||
this._updateDataAttributes(this.dom.box);
|
||||
this._updateStyle(this.dom.box);
|
||||
|
||||
var editable = (this.editable.updateTime || this.editable.updateGroup);
|
||||
|
||||
// update class
|
||||
var className = (this.data.className? ' ' + this.data.className : '') +
|
||||
(this.selected ? ' vis-selected' : '') +
|
||||
(editable ? ' vis-editable' : ' vis-readonly');
|
||||
this.dom.box.className = 'vis-item vis-box' + className;
|
||||
this.dom.line.className = 'vis-item vis-line' + className;
|
||||
this.dom.dot.className = 'vis-item vis-dot' + className;
|
||||
}
|
||||
}
|
||||
|
||||
BoxItem.prototype._getDomComponentsSizes = function() {
|
||||
return {
|
||||
previous: {
|
||||
right: this.dom.box.style.right,
|
||||
left: this.dom.box.style.left
|
||||
},
|
||||
dot: {
|
||||
height: this.dom.dot.offsetHeight,
|
||||
width: this.dom.dot.offsetWidth
|
||||
},
|
||||
line: {
|
||||
width: this.dom.line.offsetWidth
|
||||
},
|
||||
box: {
|
||||
width: this.dom.box.offsetWidth,
|
||||
height: this.dom.box.offsetHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoxItem.prototype._updateDomComponentsSizes = function(sizes) {
|
||||
if (this.options.rtl) {
|
||||
this.dom.box.style.right = "0px";
|
||||
} else {
|
||||
this.dom.box.style.left = "0px";
|
||||
}
|
||||
|
||||
// recalculate size
|
||||
this.props.dot.height = sizes.dot.height;
|
||||
this.props.dot.width = sizes.dot.width;
|
||||
this.props.line.width = sizes.line.width;
|
||||
this.width = sizes.box.width;
|
||||
this.height = sizes.box.height;
|
||||
|
||||
// restore previous position
|
||||
if (this.options.rtl) {
|
||||
this.dom.box.style.right = sizes.previous.right;
|
||||
} else {
|
||||
this.dom.box.style.left = sizes.previous.left;
|
||||
}
|
||||
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
BoxItem.prototype._repaintDomAdditionals = function() {
|
||||
this._repaintOnItemUpdateTimeTooltip(this.dom.box);
|
||||
this._repaintDragCenter();
|
||||
this._repaintDeleteButton(this.dom.box);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaint the item
|
||||
* @param {boolean} [returnQueue=false] return the queue
|
||||
* @return {boolean} the redraw queue if returnQueue=true
|
||||
*/
|
||||
BoxItem.prototype.redraw = function(returnQueue) {
|
||||
var sizes
|
||||
var queue = [
|
||||
// create item DOM
|
||||
this._createDomElement.bind(this),
|
||||
|
||||
// append DOM to parent DOM
|
||||
this._appendDomElement.bind(this),
|
||||
|
||||
// update dirty DOM
|
||||
this._updateDirtyDomComponents.bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
sizes = this._getDomComponentsSizes();
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
this._updateDomComponentsSizes.bind(this)(sizes);
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
// repaint DOM additionals
|
||||
this._repaintDomAdditionals.bind(this)
|
||||
];
|
||||
|
||||
if (returnQueue) {
|
||||
return queue;
|
||||
} else {
|
||||
var result;
|
||||
queue.forEach(function (fn) {
|
||||
result = fn();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the item in the DOM (when not already displayed). The items DOM will
|
||||
* be created when needed.
|
||||
*/
|
||||
BoxItem.prototype.show = function() {
|
||||
if (!this.displayed) {
|
||||
this.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the item from the DOM (when visible)
|
||||
*/
|
||||
BoxItem.prototype.hide = function() {
|
||||
if (this.displayed) {
|
||||
var dom = this.dom;
|
||||
|
||||
if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box);
|
||||
if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line);
|
||||
if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot);
|
||||
|
||||
this.displayed = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the item horizontally
|
||||
* @Override
|
||||
*/
|
||||
BoxItem.prototype.repositionX = function() {
|
||||
var start = this.conversion.toScreen(this.data.start);
|
||||
var align = this.options.align;
|
||||
|
||||
// calculate left position of the box
|
||||
if (align == 'right') {
|
||||
if (this.options.rtl) {
|
||||
this.right = start - this.width;
|
||||
|
||||
// reposition box, line, and dot
|
||||
this.dom.box.style.right = this.right + 'px';
|
||||
this.dom.line.style.right = (start - this.props.line.width) + 'px';
|
||||
this.dom.dot.style.right = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
|
||||
} else {
|
||||
this.left = start - this.width;
|
||||
|
||||
// reposition box, line, and dot
|
||||
this.dom.box.style.left = this.left + 'px';
|
||||
this.dom.line.style.left = (start - this.props.line.width) + 'px';
|
||||
this.dom.dot.style.left = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
|
||||
}
|
||||
}
|
||||
else if (align == 'left') {
|
||||
if (this.options.rtl) {
|
||||
this.right = start;
|
||||
|
||||
// reposition box, line, and dot
|
||||
this.dom.box.style.right = this.right + 'px';
|
||||
this.dom.line.style.right = start + 'px';
|
||||
this.dom.dot.style.right = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
|
||||
} else {
|
||||
this.left = start;
|
||||
|
||||
// reposition box, line, and dot
|
||||
this.dom.box.style.left = this.left + 'px';
|
||||
this.dom.line.style.left = start + 'px';
|
||||
this.dom.dot.style.left = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
|
||||
}
|
||||
}
|
||||
else {
|
||||
// default or 'center'
|
||||
if (this.options.rtl) {
|
||||
this.right = start - this.width / 2;
|
||||
|
||||
// reposition box, line, and dot
|
||||
this.dom.box.style.right = this.right + 'px';
|
||||
this.dom.line.style.right = (start - this.props.line.width) + 'px';
|
||||
this.dom.dot.style.right = (start - this.props.dot.width / 2) + 'px';
|
||||
} else {
|
||||
this.left = start - this.width / 2;
|
||||
|
||||
// reposition box, line, and dot
|
||||
this.dom.box.style.left = this.left + 'px';
|
||||
this.dom.line.style.left = (start - this.props.line.width / 2) + 'px';
|
||||
this.dom.dot.style.left = (start - this.props.dot.width / 2) + 'px';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the item vertically
|
||||
* @Override
|
||||
*/
|
||||
BoxItem.prototype.repositionY = function() {
|
||||
var orientation = this.options.orientation.item;
|
||||
var box = this.dom.box;
|
||||
var line = this.dom.line;
|
||||
var dot = this.dom.dot;
|
||||
|
||||
if (orientation == 'top') {
|
||||
box.style.top = (this.top || 0) + 'px';
|
||||
|
||||
line.style.top = '0';
|
||||
line.style.height = (this.parent.top + this.top + 1) + 'px';
|
||||
line.style.bottom = '';
|
||||
}
|
||||
else { // orientation 'bottom'
|
||||
var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty
|
||||
var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top;
|
||||
|
||||
box.style.top = (this.parent.height - this.top - this.height || 0) + 'px';
|
||||
line.style.top = (itemSetHeight - lineHeight) + 'px';
|
||||
line.style.bottom = '0';
|
||||
}
|
||||
|
||||
dot.style.top = (-this.props.dot.height / 2) + 'px';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the item left from its start date
|
||||
* @return {number}
|
||||
*/
|
||||
BoxItem.prototype.getWidthLeft = function () {
|
||||
return this.width / 2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the item right from its start date
|
||||
* @return {number}
|
||||
*/
|
||||
BoxItem.prototype.getWidthRight = function () {
|
||||
return this.width / 2;
|
||||
};
|
||||
|
||||
module.exports = BoxItem;
|
||||
519
node_modules/vis/lib/timeline/component/item/Item.js
generated
vendored
Normal file
519
node_modules/vis/lib/timeline/component/item/Item.js
generated
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
var Hammer = require('../../../module/hammer');
|
||||
var util = require('../../../util');
|
||||
var moment = require('../../../module/moment');
|
||||
|
||||
|
||||
/**
|
||||
* @constructor Item
|
||||
* @param {Object} data Object containing (optional) parameters type,
|
||||
* start, end, content, group, className.
|
||||
* @param {{toScreen: function, toTime: function}} conversion
|
||||
* Conversion functions from time to screen and vice versa
|
||||
* @param {Object} options Configuration options
|
||||
* // TODO: describe available options
|
||||
*/
|
||||
function Item (data, conversion, options) {
|
||||
this.id = null;
|
||||
this.parent = null;
|
||||
this.data = data;
|
||||
this.dom = null;
|
||||
this.conversion = conversion || {};
|
||||
this.options = options || {};
|
||||
this.selected = false;
|
||||
this.displayed = false;
|
||||
this.groupShowing = true;
|
||||
this.dirty = true;
|
||||
|
||||
this.top = null;
|
||||
this.right = null;
|
||||
this.left = null;
|
||||
this.width = null;
|
||||
this.height = null;
|
||||
|
||||
this.editable = null;
|
||||
this._updateEditStatus();
|
||||
}
|
||||
|
||||
Item.prototype.stack = true;
|
||||
|
||||
/**
|
||||
* Select current item
|
||||
*/
|
||||
Item.prototype.select = function() {
|
||||
this.selected = true;
|
||||
this.dirty = true;
|
||||
if (this.displayed) this.redraw();
|
||||
};
|
||||
|
||||
/**
|
||||
* Unselect current item
|
||||
*/
|
||||
Item.prototype.unselect = function() {
|
||||
this.selected = false;
|
||||
this.dirty = true;
|
||||
if (this.displayed) this.redraw();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set data for the item. Existing data will be updated. The id should not
|
||||
* be changed. When the item is displayed, it will be redrawn immediately.
|
||||
* @param {Object} data
|
||||
*/
|
||||
Item.prototype.setData = function(data) {
|
||||
var groupChanged = data.group != undefined && this.data.group != data.group;
|
||||
if (groupChanged && this.parent != null) {
|
||||
this.parent.itemSet._moveToGroup(this, data.group);
|
||||
}
|
||||
|
||||
if (this.parent) {
|
||||
this.parent.stackDirty = true;
|
||||
}
|
||||
|
||||
var subGroupChanged = data.subgroup != undefined && this.data.subgroup != data.subgroup;
|
||||
if (subGroupChanged && this.parent != null) {
|
||||
this.parent.changeSubgroup(this, this.data.subgroup, data.subgroup);
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this._updateEditStatus();
|
||||
this.dirty = true;
|
||||
if (this.displayed) this.redraw();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a parent for the item
|
||||
* @param {Group} parent
|
||||
*/
|
||||
Item.prototype.setParent = function(parent) {
|
||||
if (this.displayed) {
|
||||
this.hide();
|
||||
this.parent = parent;
|
||||
if (this.parent) {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.parent = parent;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether this item is visible inside given range
|
||||
* @param {vis.Range} range with a timestamp for start and end
|
||||
* @returns {boolean} True if visible
|
||||
*/
|
||||
Item.prototype.isVisible = function(range) { // eslint-disable-line no-unused-vars
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the Item in the DOM (when not already visible)
|
||||
* @return {Boolean} changed
|
||||
*/
|
||||
Item.prototype.show = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the Item from the DOM (when visible)
|
||||
* @return {Boolean} changed
|
||||
*/
|
||||
Item.prototype.hide = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Repaint the item
|
||||
*/
|
||||
Item.prototype.redraw = function() {
|
||||
// should be implemented by the item
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the Item horizontally
|
||||
*/
|
||||
Item.prototype.repositionX = function() {
|
||||
// should be implemented by the item
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the Item vertically
|
||||
*/
|
||||
Item.prototype.repositionY = function() {
|
||||
// should be implemented by the item
|
||||
};
|
||||
|
||||
/**
|
||||
* Repaint a drag area on the center of the item when the item is selected
|
||||
* @protected
|
||||
*/
|
||||
Item.prototype._repaintDragCenter = function () {
|
||||
if (this.selected && this.options.editable.updateTime && !this.dom.dragCenter) {
|
||||
var me = this;
|
||||
// create and show drag area
|
||||
var dragCenter = document.createElement('div');
|
||||
dragCenter.className = 'vis-drag-center';
|
||||
dragCenter.dragCenterItem = this;
|
||||
var hammer = new Hammer(dragCenter);
|
||||
|
||||
hammer.on('tap', function (event) {
|
||||
me.parent.itemSet.body.emitter.emit('click', {
|
||||
event: event,
|
||||
item: me.id
|
||||
});
|
||||
});
|
||||
hammer.on('doubletap', function (event) {
|
||||
event.stopPropagation();
|
||||
me.parent.itemSet._onUpdateItem(me);
|
||||
me.parent.itemSet.body.emitter.emit('doubleClick', {
|
||||
event: event,
|
||||
item: me.id
|
||||
});
|
||||
});
|
||||
|
||||
if (this.dom.box) {
|
||||
if (this.dom.dragLeft) {
|
||||
this.dom.box.insertBefore(dragCenter, this.dom.dragLeft);
|
||||
}
|
||||
else {
|
||||
this.dom.box.appendChild(dragCenter);
|
||||
}
|
||||
}
|
||||
else if (this.dom.point) {
|
||||
this.dom.point.appendChild(dragCenter);
|
||||
}
|
||||
|
||||
this.dom.dragCenter = dragCenter;
|
||||
}
|
||||
else if (!this.selected && this.dom.dragCenter) {
|
||||
// delete drag area
|
||||
if (this.dom.dragCenter.parentNode) {
|
||||
this.dom.dragCenter.parentNode.removeChild(this.dom.dragCenter);
|
||||
}
|
||||
this.dom.dragCenter = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Repaint a delete button on the top right of the item when the item is selected
|
||||
* @param {HTMLElement} anchor
|
||||
* @protected
|
||||
*/
|
||||
Item.prototype._repaintDeleteButton = function (anchor) {
|
||||
var editable = ((this.options.editable.overrideItems || this.editable == null) && this.options.editable.remove) ||
|
||||
(!this.options.editable.overrideItems && this.editable != null && this.editable.remove);
|
||||
|
||||
if (this.selected && editable && !this.dom.deleteButton) {
|
||||
// create and show button
|
||||
var me = this;
|
||||
|
||||
var deleteButton = document.createElement('div');
|
||||
|
||||
if (this.options.rtl) {
|
||||
deleteButton.className = 'vis-delete-rtl';
|
||||
} else {
|
||||
deleteButton.className = 'vis-delete';
|
||||
}
|
||||
deleteButton.title = 'Delete this item';
|
||||
|
||||
// TODO: be able to destroy the delete button
|
||||
new Hammer(deleteButton).on('tap', function (event) {
|
||||
event.stopPropagation();
|
||||
me.parent.removeFromDataSet(me);
|
||||
});
|
||||
|
||||
anchor.appendChild(deleteButton);
|
||||
this.dom.deleteButton = deleteButton;
|
||||
}
|
||||
else if (!this.selected && this.dom.deleteButton) {
|
||||
// remove button
|
||||
if (this.dom.deleteButton.parentNode) {
|
||||
this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton);
|
||||
}
|
||||
this.dom.deleteButton = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Repaint a onChange tooltip on the top right of the item when the item is selected
|
||||
* @param {HTMLElement} anchor
|
||||
* @protected
|
||||
*/
|
||||
Item.prototype._repaintOnItemUpdateTimeTooltip = function (anchor) {
|
||||
if (!this.options.tooltipOnItemUpdateTime) return;
|
||||
|
||||
var editable = (this.options.editable.updateTime ||
|
||||
this.data.editable === true) &&
|
||||
this.data.editable !== false;
|
||||
|
||||
if (this.selected && editable && !this.dom.onItemUpdateTimeTooltip) {
|
||||
var onItemUpdateTimeTooltip = document.createElement('div');
|
||||
|
||||
onItemUpdateTimeTooltip.className = 'vis-onUpdateTime-tooltip';
|
||||
anchor.appendChild(onItemUpdateTimeTooltip);
|
||||
this.dom.onItemUpdateTimeTooltip = onItemUpdateTimeTooltip;
|
||||
|
||||
} else if (!this.selected && this.dom.onItemUpdateTimeTooltip) {
|
||||
// remove button
|
||||
if (this.dom.onItemUpdateTimeTooltip.parentNode) {
|
||||
this.dom.onItemUpdateTimeTooltip.parentNode.removeChild(this.dom.onItemUpdateTimeTooltip);
|
||||
}
|
||||
this.dom.onItemUpdateTimeTooltip = null;
|
||||
}
|
||||
|
||||
// position onChange tooltip
|
||||
if (this.dom.onItemUpdateTimeTooltip) {
|
||||
|
||||
// only show when editing
|
||||
this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden';
|
||||
|
||||
// position relative to item's content
|
||||
if (this.options.rtl) {
|
||||
this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right;
|
||||
} else {
|
||||
this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left;
|
||||
}
|
||||
|
||||
// position above or below the item depending on the item's position in the window
|
||||
var tooltipOffset = 50; // TODO: should be tooltip height (depends on template)
|
||||
var scrollTop = this.parent.itemSet.body.domProps.scrollTop;
|
||||
|
||||
// TODO: this.top for orientation:true is actually the items distance from the bottom...
|
||||
// (should be this.bottom)
|
||||
var itemDistanceFromTop
|
||||
if (this.options.orientation.item == 'top') {
|
||||
itemDistanceFromTop = this.top;
|
||||
} else {
|
||||
itemDistanceFromTop = (this.parent.height - this.top - this.height)
|
||||
}
|
||||
var isCloseToTop = itemDistanceFromTop + this.parent.top - tooltipOffset < -scrollTop;
|
||||
|
||||
if (isCloseToTop) {
|
||||
this.dom.onItemUpdateTimeTooltip.style.bottom = "";
|
||||
this.dom.onItemUpdateTimeTooltip.style.top = this.height + 2 + "px";
|
||||
} else {
|
||||
this.dom.onItemUpdateTimeTooltip.style.top = "";
|
||||
this.dom.onItemUpdateTimeTooltip.style.bottom = this.height + 2 + "px";
|
||||
}
|
||||
|
||||
// handle tooltip content
|
||||
var content;
|
||||
var templateFunction;
|
||||
|
||||
if (this.options.tooltipOnItemUpdateTime && this.options.tooltipOnItemUpdateTime.template) {
|
||||
templateFunction = this.options.tooltipOnItemUpdateTime.template.bind(this);
|
||||
content = templateFunction(this.data);
|
||||
} else {
|
||||
content = 'start: ' + moment(this.data.start).format('MM/DD/YYYY hh:mm');
|
||||
if (this.data.end) {
|
||||
content += '<br> end: ' + moment(this.data.end).format('MM/DD/YYYY hh:mm');
|
||||
}
|
||||
}
|
||||
this.dom.onItemUpdateTimeTooltip.innerHTML = content;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set HTML contents for the item
|
||||
* @param {Element} element HTML element to fill with the contents
|
||||
* @private
|
||||
*/
|
||||
Item.prototype._updateContents = function (element) {
|
||||
var content;
|
||||
var changed;
|
||||
var templateFunction;
|
||||
var itemVisibleFrameContent;
|
||||
var visibleFrameTemplateFunction;
|
||||
var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset
|
||||
|
||||
var frameElement = this.dom.box || this.dom.point;
|
||||
var itemVisibleFrameContentElement = frameElement.getElementsByClassName('vis-item-visible-frame')[0]
|
||||
|
||||
if (this.options.visibleFrameTemplate) {
|
||||
visibleFrameTemplateFunction = this.options.visibleFrameTemplate.bind(this);
|
||||
itemVisibleFrameContent = visibleFrameTemplateFunction(itemData, frameElement);
|
||||
} else {
|
||||
itemVisibleFrameContent = '';
|
||||
}
|
||||
|
||||
if (itemVisibleFrameContentElement) {
|
||||
if ((itemVisibleFrameContent instanceof Object) && !(itemVisibleFrameContent instanceof Element)) {
|
||||
visibleFrameTemplateFunction(itemData, itemVisibleFrameContentElement)
|
||||
} else {
|
||||
changed = this._contentToString(this.itemVisibleFrameContent) !== this._contentToString(itemVisibleFrameContent);
|
||||
if (changed) {
|
||||
// only replace the content when changed
|
||||
if (itemVisibleFrameContent instanceof Element) {
|
||||
itemVisibleFrameContentElement.innerHTML = '';
|
||||
itemVisibleFrameContentElement.appendChild(itemVisibleFrameContent);
|
||||
}
|
||||
else if (itemVisibleFrameContent != undefined) {
|
||||
itemVisibleFrameContentElement.innerHTML = itemVisibleFrameContent;
|
||||
}
|
||||
else {
|
||||
if (!(this.data.type == 'background' && this.data.content === undefined)) {
|
||||
throw new Error('Property "content" missing in item ' + this.id);
|
||||
}
|
||||
}
|
||||
|
||||
this.itemVisibleFrameContent = itemVisibleFrameContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.template) {
|
||||
templateFunction = this.options.template.bind(this);
|
||||
content = templateFunction(itemData, element, this.data);
|
||||
} else {
|
||||
content = this.data.content;
|
||||
}
|
||||
|
||||
if ((content instanceof Object) && !(content instanceof Element)) {
|
||||
templateFunction(itemData, element)
|
||||
} else {
|
||||
changed = this._contentToString(this.content) !== this._contentToString(content);
|
||||
if (changed) {
|
||||
// only replace the content when changed
|
||||
if (content instanceof Element) {
|
||||
element.innerHTML = '';
|
||||
element.appendChild(content);
|
||||
}
|
||||
else if (content != undefined) {
|
||||
element.innerHTML = content;
|
||||
}
|
||||
else {
|
||||
if (!(this.data.type == 'background' && this.data.content === undefined)) {
|
||||
throw new Error('Property "content" missing in item ' + this.id);
|
||||
}
|
||||
}
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process dataAttributes timeline option and set as data- attributes on dom.content
|
||||
* @param {Element} element HTML element to which the attributes will be attached
|
||||
* @private
|
||||
*/
|
||||
Item.prototype._updateDataAttributes = function(element) {
|
||||
if (this.options.dataAttributes && this.options.dataAttributes.length > 0) {
|
||||
var attributes = [];
|
||||
|
||||
if (Array.isArray(this.options.dataAttributes)) {
|
||||
attributes = this.options.dataAttributes;
|
||||
}
|
||||
else if (this.options.dataAttributes == 'all') {
|
||||
attributes = Object.keys(this.data);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < attributes.length; i++) {
|
||||
var name = attributes[i];
|
||||
var value = this.data[name];
|
||||
|
||||
if (value != null) {
|
||||
element.setAttribute('data-' + name, value);
|
||||
}
|
||||
else {
|
||||
element.removeAttribute('data-' + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update custom styles of the element
|
||||
* @param {Element} element
|
||||
* @private
|
||||
*/
|
||||
Item.prototype._updateStyle = function(element) {
|
||||
// remove old styles
|
||||
if (this.style) {
|
||||
util.removeCssText(element, this.style);
|
||||
this.style = null;
|
||||
}
|
||||
|
||||
// append new styles
|
||||
if (this.data.style) {
|
||||
util.addCssText(element, this.data.style);
|
||||
this.style = this.data.style;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stringify the items contents
|
||||
* @param {string | Element | undefined} content
|
||||
* @returns {string | undefined}
|
||||
* @private
|
||||
*/
|
||||
Item.prototype._contentToString = function (content) {
|
||||
if (typeof content === 'string') return content;
|
||||
if (content && 'outerHTML' in content) return content.outerHTML;
|
||||
return content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the editability of this item.
|
||||
*/
|
||||
Item.prototype._updateEditStatus = function() {
|
||||
if (this.options) {
|
||||
if(typeof this.options.editable === 'boolean') {
|
||||
this.editable = {
|
||||
updateTime: this.options.editable,
|
||||
updateGroup: this.options.editable,
|
||||
remove: this.options.editable
|
||||
};
|
||||
} else if(typeof this.options.editable === 'object') {
|
||||
this.editable = {};
|
||||
util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.options.editable);
|
||||
}
|
||||
}
|
||||
// Item data overrides, except if options.editable.overrideItems is set.
|
||||
if (!this.options || !(this.options.editable) || (this.options.editable.overrideItems !== true)) {
|
||||
if (this.data) {
|
||||
if (typeof this.data.editable === 'boolean') {
|
||||
this.editable = {
|
||||
updateTime: this.data.editable,
|
||||
updateGroup: this.data.editable,
|
||||
remove: this.data.editable
|
||||
}
|
||||
} else if (typeof this.data.editable === 'object') {
|
||||
// TODO: in vis.js 5.0, we should change this to not reset options from the timeline configuration.
|
||||
// Basically just remove the next line...
|
||||
this.editable = {};
|
||||
util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.data.editable);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the item left from its start date
|
||||
* @return {number}
|
||||
*/
|
||||
Item.prototype.getWidthLeft = function () {
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the item right from the max of its start and end date
|
||||
* @return {number}
|
||||
*/
|
||||
Item.prototype.getWidthRight = function () {
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the title of the item
|
||||
* @return {string | undefined}
|
||||
*/
|
||||
Item.prototype.getTitle = function () {
|
||||
return this.data.title;
|
||||
};
|
||||
|
||||
module.exports = Item;
|
||||
281
node_modules/vis/lib/timeline/component/item/PointItem.js
generated
vendored
Normal file
281
node_modules/vis/lib/timeline/component/item/PointItem.js
generated
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
var Item = require('./Item');
|
||||
|
||||
/**
|
||||
* @constructor PointItem
|
||||
* @extends Item
|
||||
* @param {Object} data Object containing parameters start
|
||||
* content, className.
|
||||
* @param {{toScreen: function, toTime: function}} conversion
|
||||
* Conversion functions from time to screen and vice versa
|
||||
* @param {Object} [options] Configuration options
|
||||
* // TODO: describe available options
|
||||
*/
|
||||
function PointItem (data, conversion, options) {
|
||||
this.props = {
|
||||
dot: {
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
content: {
|
||||
height: 0,
|
||||
marginLeft: 0,
|
||||
marginRight: 0
|
||||
}
|
||||
};
|
||||
this.options = options;
|
||||
// validate data
|
||||
if (data) {
|
||||
if (data.start == undefined) {
|
||||
throw new Error('Property "start" missing in item ' + data);
|
||||
}
|
||||
}
|
||||
|
||||
Item.call(this, data, conversion, options);
|
||||
}
|
||||
|
||||
PointItem.prototype = new Item (null, null, null);
|
||||
|
||||
/**
|
||||
* Check whether this item is visible inside given range
|
||||
* @param {{start: number, end: number}} range with a timestamp for start and end
|
||||
* @returns {boolean} True if visible
|
||||
*/
|
||||
PointItem.prototype.isVisible = function(range) {
|
||||
// determine visibility
|
||||
var widthInMs = this.width * range.getMillisecondsPerPixel();
|
||||
|
||||
return (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start < range.end);
|
||||
};
|
||||
|
||||
|
||||
PointItem.prototype._createDomElement = function() {
|
||||
if (!this.dom) {
|
||||
// create DOM
|
||||
this.dom = {};
|
||||
|
||||
// background box
|
||||
this.dom.point = document.createElement('div');
|
||||
// className is updated in redraw()
|
||||
|
||||
// contents box, right from the dot
|
||||
this.dom.content = document.createElement('div');
|
||||
this.dom.content.className = 'vis-item-content';
|
||||
this.dom.point.appendChild(this.dom.content);
|
||||
|
||||
// dot at start
|
||||
this.dom.dot = document.createElement('div');
|
||||
this.dom.point.appendChild(this.dom.dot);
|
||||
|
||||
// attach this item as attribute
|
||||
this.dom.point['timeline-item'] = this;
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
PointItem.prototype._appendDomElement = function() {
|
||||
if (!this.parent) {
|
||||
throw new Error('Cannot redraw item: no parent attached');
|
||||
}
|
||||
if (!this.dom.point.parentNode) {
|
||||
var foreground = this.parent.dom.foreground;
|
||||
if (!foreground) {
|
||||
throw new Error('Cannot redraw item: parent has no foreground container element');
|
||||
}
|
||||
foreground.appendChild(this.dom.point);
|
||||
}
|
||||
this.displayed = true;
|
||||
}
|
||||
|
||||
PointItem.prototype._updateDirtyDomComponents = function() {
|
||||
// An item is marked dirty when:
|
||||
// - the item is not yet rendered
|
||||
// - the item's data is changed
|
||||
// - the item is selected/deselected
|
||||
if (this.dirty) {
|
||||
this._updateContents(this.dom.content);
|
||||
this._updateDataAttributes(this.dom.point);
|
||||
this._updateStyle(this.dom.point);
|
||||
|
||||
var editable = (this.editable.updateTime || this.editable.updateGroup);
|
||||
// update class
|
||||
var className = (this.data.className ? ' ' + this.data.className : '') +
|
||||
(this.selected ? ' vis-selected' : '') +
|
||||
(editable ? ' vis-editable' : ' vis-readonly');
|
||||
this.dom.point.className = 'vis-item vis-point' + className;
|
||||
this.dom.dot.className = 'vis-item vis-dot' + className;
|
||||
}
|
||||
}
|
||||
|
||||
PointItem.prototype._getDomComponentsSizes = function() {
|
||||
return {
|
||||
dot: {
|
||||
width: this.dom.dot.offsetWidth,
|
||||
height: this.dom.dot.offsetHeight
|
||||
},
|
||||
content: {
|
||||
width: this.dom.content.offsetWidth,
|
||||
height: this.dom.content.offsetHeight
|
||||
},
|
||||
point: {
|
||||
width: this.dom.point.offsetWidth,
|
||||
height: this.dom.point.offsetHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PointItem.prototype._updateDomComponentsSizes = function(sizes) {
|
||||
// recalculate size of dot and contents
|
||||
this.props.dot.width = sizes.dot.width;
|
||||
this.props.dot.height = sizes.dot.height;
|
||||
this.props.content.height = sizes.content.height;
|
||||
|
||||
// resize contents
|
||||
if (this.options.rtl) {
|
||||
this.dom.content.style.marginRight = 2 * this.props.dot.width + 'px';
|
||||
} else {
|
||||
this.dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
|
||||
}
|
||||
//this.dom.content.style.marginRight = ... + 'px'; // TODO: margin right
|
||||
|
||||
// recalculate size
|
||||
this.width = sizes.point.width;
|
||||
this.height = sizes.point.height;
|
||||
|
||||
// reposition the dot
|
||||
this.dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px';
|
||||
if (this.options.rtl) {
|
||||
this.dom.dot.style.right = (this.props.dot.width / 2) + 'px';
|
||||
} else {
|
||||
this.dom.dot.style.left = (this.props.dot.width / 2) + 'px';
|
||||
}
|
||||
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
PointItem.prototype._repaintDomAdditionals = function() {
|
||||
this._repaintOnItemUpdateTimeTooltip(this.dom.point);
|
||||
this._repaintDragCenter();
|
||||
this._repaintDeleteButton(this.dom.point);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaint the item
|
||||
* @param {boolean} [returnQueue=false] return the queue
|
||||
* @return {boolean} the redraw queue if returnQueue=true
|
||||
*/
|
||||
PointItem.prototype.redraw = function(returnQueue) {
|
||||
var sizes
|
||||
var queue = [
|
||||
// create item DOM
|
||||
this._createDomElement.bind(this),
|
||||
|
||||
// append DOM to parent DOM
|
||||
this._appendDomElement.bind(this),
|
||||
|
||||
// update dirty DOM
|
||||
this._updateDirtyDomComponents.bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
sizes = this._getDomComponentsSizes();
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
this._updateDomComponentsSizes.bind(this)(sizes);
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
// repaint DOM additionals
|
||||
this._repaintDomAdditionals.bind(this)
|
||||
];
|
||||
|
||||
if (returnQueue) {
|
||||
return queue;
|
||||
} else {
|
||||
var result;
|
||||
queue.forEach(function (fn) {
|
||||
result = fn();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the item in the DOM (when not already visible). The items DOM will
|
||||
* be created when needed.
|
||||
*/
|
||||
PointItem.prototype.show = function() {
|
||||
if (!this.displayed) {
|
||||
this.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the item from the DOM (when visible)
|
||||
*/
|
||||
PointItem.prototype.hide = function() {
|
||||
if (this.displayed) {
|
||||
if (this.dom.point.parentNode) {
|
||||
this.dom.point.parentNode.removeChild(this.dom.point);
|
||||
}
|
||||
|
||||
this.displayed = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the item horizontally
|
||||
* @Override
|
||||
*/
|
||||
PointItem.prototype.repositionX = function() {
|
||||
var start = this.conversion.toScreen(this.data.start);
|
||||
|
||||
if (this.options.rtl) {
|
||||
this.right = start - this.props.dot.width;
|
||||
|
||||
// reposition point
|
||||
this.dom.point.style.right = this.right + 'px';
|
||||
} else {
|
||||
this.left = start - this.props.dot.width;
|
||||
|
||||
// reposition point
|
||||
this.dom.point.style.left = this.left + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the item vertically
|
||||
* @Override
|
||||
*/
|
||||
PointItem.prototype.repositionY = function() {
|
||||
var orientation = this.options.orientation.item;
|
||||
var point = this.dom.point;
|
||||
if (orientation == 'top') {
|
||||
point.style.top = this.top + 'px';
|
||||
}
|
||||
else {
|
||||
point.style.top = (this.parent.height - this.top - this.height) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the item left from its start date
|
||||
* @return {number}
|
||||
*/
|
||||
PointItem.prototype.getWidthLeft = function () {
|
||||
return this.props.dot.width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the width of the item right from its start date
|
||||
* @return {number}
|
||||
*/
|
||||
PointItem.prototype.getWidthRight = function () {
|
||||
return this.props.dot.width;
|
||||
};
|
||||
|
||||
module.exports = PointItem;
|
||||
392
node_modules/vis/lib/timeline/component/item/RangeItem.js
generated
vendored
Normal file
392
node_modules/vis/lib/timeline/component/item/RangeItem.js
generated
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
var Item = require('./Item');
|
||||
|
||||
/**
|
||||
* @constructor RangeItem
|
||||
* @extends Item
|
||||
* @param {Object} data Object containing parameters start, end
|
||||
* content, className.
|
||||
* @param {{toScreen: function, toTime: function}} conversion
|
||||
* Conversion functions from time to screen and vice versa
|
||||
* @param {Object} [options] Configuration options
|
||||
* // TODO: describe options
|
||||
*/
|
||||
function RangeItem (data, conversion, options) {
|
||||
this.props = {
|
||||
content: {
|
||||
width: 0
|
||||
}
|
||||
};
|
||||
this.overflow = false; // if contents can overflow (css styling), this flag is set to true
|
||||
this.options = options;
|
||||
// validate data
|
||||
if (data) {
|
||||
if (data.start == undefined) {
|
||||
throw new Error('Property "start" missing in item ' + data.id);
|
||||
}
|
||||
if (data.end == undefined) {
|
||||
throw new Error('Property "end" missing in item ' + data.id);
|
||||
}
|
||||
}
|
||||
|
||||
Item.call(this, data, conversion, options);
|
||||
}
|
||||
|
||||
RangeItem.prototype = new Item (null, null, null);
|
||||
|
||||
RangeItem.prototype.baseClassName = 'vis-item vis-range';
|
||||
|
||||
/**
|
||||
* Check whether this item is visible inside given range
|
||||
*
|
||||
* @param {vis.Range} range with a timestamp for start and end
|
||||
* @returns {boolean} True if visible
|
||||
*/
|
||||
RangeItem.prototype.isVisible = function(range) {
|
||||
// determine visibility
|
||||
return (this.data.start < range.end) && (this.data.end > range.start);
|
||||
};
|
||||
|
||||
RangeItem.prototype._createDomElement = function() {
|
||||
if (!this.dom) {
|
||||
// create DOM
|
||||
this.dom = {};
|
||||
|
||||
// background box
|
||||
this.dom.box = document.createElement('div');
|
||||
// className is updated in redraw()
|
||||
|
||||
// frame box (to prevent the item contents from overflowing)
|
||||
this.dom.frame = document.createElement('div');
|
||||
this.dom.frame.className = 'vis-item-overflow';
|
||||
this.dom.box.appendChild(this.dom.frame);
|
||||
|
||||
// visible frame box (showing the frame that is always visible)
|
||||
this.dom.visibleFrame = document.createElement('div');
|
||||
this.dom.visibleFrame.className = 'vis-item-visible-frame';
|
||||
this.dom.box.appendChild(this.dom.visibleFrame);
|
||||
|
||||
// contents box
|
||||
this.dom.content = document.createElement('div');
|
||||
this.dom.content.className = 'vis-item-content';
|
||||
this.dom.frame.appendChild(this.dom.content);
|
||||
|
||||
// attach this item as attribute
|
||||
this.dom.box['timeline-item'] = this;
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RangeItem.prototype._appendDomElement = function() {
|
||||
if (!this.parent) {
|
||||
throw new Error('Cannot redraw item: no parent attached');
|
||||
}
|
||||
if (!this.dom.box.parentNode) {
|
||||
var foreground = this.parent.dom.foreground;
|
||||
if (!foreground) {
|
||||
throw new Error('Cannot redraw item: parent has no foreground container element');
|
||||
}
|
||||
foreground.appendChild(this.dom.box);
|
||||
}
|
||||
this.displayed = true;
|
||||
}
|
||||
|
||||
RangeItem.prototype._updateDirtyDomComponents = function() {
|
||||
// update dirty DOM. An item is marked dirty when:
|
||||
// - the item is not yet rendered
|
||||
// - the item's data is changed
|
||||
// - the item is selected/deselected
|
||||
if (this.dirty) {
|
||||
this._updateContents(this.dom.content);
|
||||
this._updateDataAttributes(this.dom.box);
|
||||
this._updateStyle(this.dom.box);
|
||||
|
||||
var editable = (this.editable.updateTime || this.editable.updateGroup);
|
||||
|
||||
// update class
|
||||
var className = (this.data.className ? (' ' + this.data.className) : '') +
|
||||
(this.selected ? ' vis-selected' : '') +
|
||||
(editable ? ' vis-editable' : ' vis-readonly');
|
||||
this.dom.box.className = this.baseClassName + className;
|
||||
|
||||
// turn off max-width to be able to calculate the real width
|
||||
// this causes an extra browser repaint/reflow, but so be it
|
||||
this.dom.content.style.maxWidth = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
RangeItem.prototype._getDomComponentsSizes = function() {
|
||||
// determine from css whether this box has overflow
|
||||
this.overflow = window.getComputedStyle(this.dom.frame).overflow !== 'hidden';
|
||||
return {
|
||||
content: {
|
||||
width: this.dom.content.offsetWidth,
|
||||
},
|
||||
box: {
|
||||
height: this.dom.box.offsetHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RangeItem.prototype._updateDomComponentsSizes = function(sizes) {
|
||||
this.props.content.width = sizes.content.width;
|
||||
this.height = sizes.box.height;
|
||||
this.dom.content.style.maxWidth = '';
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
RangeItem.prototype._repaintDomAdditionals = function() {
|
||||
this._repaintOnItemUpdateTimeTooltip(this.dom.box);
|
||||
this._repaintDeleteButton(this.dom.box);
|
||||
this._repaintDragCenter();
|
||||
this._repaintDragLeft();
|
||||
this._repaintDragRight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaint the item
|
||||
* @param {boolean} [returnQueue=false] return the queue
|
||||
* @return {boolean} the redraw queue if returnQueue=true
|
||||
*/
|
||||
RangeItem.prototype.redraw = function(returnQueue) {
|
||||
var sizes;
|
||||
var queue = [
|
||||
// create item DOM
|
||||
this._createDomElement.bind(this),
|
||||
|
||||
// append DOM to parent DOM
|
||||
this._appendDomElement.bind(this),
|
||||
|
||||
// update dirty DOM
|
||||
this._updateDirtyDomComponents.bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
sizes = this._getDomComponentsSizes.bind(this)();
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
(function() {
|
||||
if (this.dirty) {
|
||||
this._updateDomComponentsSizes.bind(this)(sizes);
|
||||
}
|
||||
}).bind(this),
|
||||
|
||||
// repaint DOM additionals
|
||||
this._repaintDomAdditionals.bind(this)
|
||||
];
|
||||
|
||||
if (returnQueue) {
|
||||
return queue;
|
||||
} else {
|
||||
var result;
|
||||
queue.forEach(function (fn) {
|
||||
result = fn();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the item in the DOM (when not already visible). The items DOM will
|
||||
* be created when needed.
|
||||
*/
|
||||
RangeItem.prototype.show = function() {
|
||||
if (!this.displayed) {
|
||||
this.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the item from the DOM (when visible)
|
||||
*/
|
||||
RangeItem.prototype.hide = function() {
|
||||
if (this.displayed) {
|
||||
var box = this.dom.box;
|
||||
|
||||
if (box.parentNode) {
|
||||
box.parentNode.removeChild(box);
|
||||
}
|
||||
|
||||
this.displayed = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the item horizontally
|
||||
* @param {boolean} [limitSize=true] If true (default), the width of the range
|
||||
* item will be limited, as the browser cannot
|
||||
* display very wide divs. This means though
|
||||
* that the applied left and width may
|
||||
* not correspond to the ranges start and end
|
||||
* @Override
|
||||
*/
|
||||
RangeItem.prototype.repositionX = function(limitSize) {
|
||||
var parentWidth = this.parent.width;
|
||||
var start = this.conversion.toScreen(this.data.start);
|
||||
var end = this.conversion.toScreen(this.data.end);
|
||||
var align = this.data.align === undefined ? this.options.align : this.data.align;
|
||||
var contentStartPosition;
|
||||
var contentWidth;
|
||||
|
||||
// limit the width of the range, as browsers cannot draw very wide divs
|
||||
// unless limitSize: false is explicitly set in item data
|
||||
if (this.data.limitSize !== false && (limitSize === undefined || limitSize === true)) {
|
||||
if (start < -parentWidth) {
|
||||
start = -parentWidth;
|
||||
}
|
||||
if (end > 2 * parentWidth) {
|
||||
end = 2 * parentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// add 0.5 to compensate floating-point values rounding
|
||||
var boxWidth = Math.max(end - start + 0.5, 1);
|
||||
|
||||
if (this.overflow) {
|
||||
if (this.options.rtl) {
|
||||
this.right = start;
|
||||
} else {
|
||||
this.left = start;
|
||||
}
|
||||
this.width = boxWidth + this.props.content.width;
|
||||
contentWidth = this.props.content.width;
|
||||
|
||||
// Note: The calculation of width is an optimistic calculation, giving
|
||||
// a width which will not change when moving the Timeline
|
||||
// So no re-stacking needed, which is nicer for the eye;
|
||||
}
|
||||
else {
|
||||
if (this.options.rtl) {
|
||||
this.right = start;
|
||||
} else {
|
||||
this.left = start;
|
||||
}
|
||||
this.width = boxWidth;
|
||||
contentWidth = Math.min(end - start, this.props.content.width);
|
||||
}
|
||||
|
||||
if (this.options.rtl) {
|
||||
this.dom.box.style.right = this.right + 'px';
|
||||
} else {
|
||||
this.dom.box.style.left = this.left + 'px';
|
||||
}
|
||||
this.dom.box.style.width = boxWidth + 'px';
|
||||
|
||||
switch (align) {
|
||||
case 'left':
|
||||
if (this.options.rtl) {
|
||||
this.dom.content.style.right = '0';
|
||||
} else {
|
||||
this.dom.content.style.left = '0';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'right':
|
||||
if (this.options.rtl) {
|
||||
this.dom.content.style.right = Math.max((boxWidth - contentWidth), 0) + 'px';
|
||||
} else {
|
||||
this.dom.content.style.left = Math.max((boxWidth - contentWidth), 0) + 'px';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'center':
|
||||
if (this.options.rtl) {
|
||||
this.dom.content.style.right = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
|
||||
} else {
|
||||
this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default: // 'auto'
|
||||
// when range exceeds left of the window, position the contents at the left of the visible area
|
||||
if (this.overflow) {
|
||||
if (end > 0) {
|
||||
contentStartPosition = Math.max(-start, 0);
|
||||
}
|
||||
else {
|
||||
contentStartPosition = -contentWidth; // ensure it's not visible anymore
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (start < 0) {
|
||||
contentStartPosition = -start;
|
||||
}
|
||||
else {
|
||||
contentStartPosition = 0;
|
||||
}
|
||||
}
|
||||
if (this.options.rtl) {
|
||||
this.dom.content.style.right = contentStartPosition + 'px';
|
||||
} else {
|
||||
this.dom.content.style.left = contentStartPosition + 'px';
|
||||
this.dom.content.style.width = 'calc(100% - ' + contentStartPosition + 'px)';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition the item vertically
|
||||
* @Override
|
||||
*/
|
||||
RangeItem.prototype.repositionY = function() {
|
||||
var orientation = this.options.orientation.item;
|
||||
var box = this.dom.box;
|
||||
|
||||
if (orientation == 'top') {
|
||||
box.style.top = this.top + 'px';
|
||||
}
|
||||
else {
|
||||
box.style.top = (this.parent.height - this.top - this.height) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Repaint a drag area on the left side of the range when the range is selected
|
||||
* @protected
|
||||
*/
|
||||
RangeItem.prototype._repaintDragLeft = function () {
|
||||
if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragLeft) {
|
||||
// create and show drag area
|
||||
var dragLeft = document.createElement('div');
|
||||
dragLeft.className = 'vis-drag-left';
|
||||
dragLeft.dragLeftItem = this;
|
||||
|
||||
this.dom.box.appendChild(dragLeft);
|
||||
this.dom.dragLeft = dragLeft;
|
||||
}
|
||||
else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragLeft) {
|
||||
// delete drag area
|
||||
if (this.dom.dragLeft.parentNode) {
|
||||
this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft);
|
||||
}
|
||||
this.dom.dragLeft = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Repaint a drag area on the right side of the range when the range is selected
|
||||
* @protected
|
||||
*/
|
||||
RangeItem.prototype._repaintDragRight = function () {
|
||||
if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragRight) {
|
||||
// create and show drag area
|
||||
var dragRight = document.createElement('div');
|
||||
dragRight.className = 'vis-drag-right';
|
||||
dragRight.dragRightItem = this;
|
||||
|
||||
this.dom.box.appendChild(dragRight);
|
||||
this.dom.dragRight = dragRight;
|
||||
}
|
||||
else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragRight) {
|
||||
// delete drag area
|
||||
if (this.dom.dragRight.parentNode) {
|
||||
this.dom.dragRight.parentNode.removeChild(this.dom.dragRight);
|
||||
}
|
||||
this.dom.dragRight = null;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = RangeItem;
|
||||
Reference in New Issue
Block a user