Browse Source

Merge branch 'develop' of https://github.com/almende/vis into template-fix

codeClimate
Yotam Berkowitz 8 years ago
parent
commit
5af1b893fe
13 changed files with 1166 additions and 621 deletions
  1. +17
    -0
      docs/timeline/index.html
  2. +16
    -9
      examples/graph3d/11_tooltips.html
  3. +77
    -0
      examples/timeline/other/horizontalScroll.html
  4. +92
    -0
      examples/timeline/other/verticalScroll.html
  5. +327
    -562
      lib/graph3d/Graph3d.js
  6. +436
    -0
      lib/graph3d/Settings.js
  7. +112
    -31
      lib/timeline/Core.js
  8. +25
    -13
      lib/timeline/Range.js
  9. +1
    -1
      lib/timeline/Timeline.js
  10. +13
    -4
      lib/timeline/component/ItemSet.js
  11. +22
    -1
      lib/timeline/component/css/panel.css
  12. +2
    -0
      lib/timeline/optionsTimeline.js
  13. +26
    -0
      lib/util.js

+ 17
- 0
docs/timeline/index.html View File

@ -675,6 +675,15 @@ function (option, path) {
</td> </td>
</tr> </tr>
<tr>
<td>horizontalScroll</td>
<td>Boolean</td>
<td>false</td>
<td>This option allows you to scroll horizontally to move backwards and forwards in the time range.
Only applicable when option <code>zoomCtrl</code> is defined or <code>zoomable</code> is <code>false</code>.
</td>
</tr>
<tr> <tr>
<td>itemsAlwaysDraggable</td> <td>itemsAlwaysDraggable</td>
<td>boolean</td> <td>boolean</td>
@ -1015,6 +1024,14 @@ function (option, path) {
</td> </td>
</tr> </tr>
<tr>
<td>verticalScroll</td>
<td>Boolean</td>
<td>false</td>
<td> Show a vertical scroll on the side of the group list.
</td>
</tr>
<tr> <tr>
<td>width</td> <td>width</td>
<td>String or Number</td> <td>String or Number</td>

+ 16
- 9
examples/graph3d/11_tooltips.html View File

@ -5,6 +5,12 @@
<style> <style>
body {font: 10pt arial;} body {font: 10pt arial;}
div#info {
width : 600px;
text-align: center;
margin-top: 2em;
font-size : 1.2em;
}
</style> </style>
<script type="text/javascript" src="../../dist/vis.js"></script> <script type="text/javascript" src="../../dist/vis.js"></script>
@ -24,11 +30,11 @@
// Create and populate a data table. // Create and populate a data table.
data = new vis.DataSet(); data = new vis.DataSet();
var extra_content = [
'Arbitrary information',
'You can access data from the point source object',
'Tooltip example content',
];
var extra_content = [
'Arbitrary information',
'You can access data from the point source object',
'Tooltip example content',
];
// create some nice looking data with sin/cos // create some nice looking data with sin/cos
var steps = 5; // number of datapoints will be steps*steps var steps = 5; // number of datapoints will be steps*steps
@ -42,7 +48,7 @@
data.add({x:x, y:y, z: z, style:value, extra: extra_content[(x*y) % extra_content.length]}); data.add({x:x, y:y, z: z, style:value, extra: extra_content[(x*y) % extra_content.length]});
} }
else { else {
data.add({x:x, y:y, z: z, extra: extra_content[(x*y) % extra_content.length]});
data.add({x:x, y:y, z: z, extra: extra_content[(x*y) % extra_content.length]});
} }
} }
} }
@ -61,8 +67,8 @@
//tooltip: true, //tooltip: true,
tooltip: function (point) { tooltip: function (point) {
// parameter point contains properties x, y, z, and data // parameter point contains properties x, y, z, and data
// data is the original object passed to the point constructor
return 'value: <b>' + point.z + '</b><br>' + point.data.extra;
// data is the original object passed to the point constructor
return 'value: <b>' + point.z + '</b><br>' + point.data.extra;
}, },
keepAspectRatio: true, keepAspectRatio: true,
@ -106,6 +112,7 @@
<div id="mygraph"></div> <div id="mygraph"></div>
<div id="info"></div>
<div id="info">Hover the mouse cursor over the graph to see tooltips.</div>
</body> </body>
</html> </html>

+ 77
- 0
examples/timeline/other/horizontalScroll.html View File

@ -0,0 +1,77 @@
<html>
<head>
<title>Timeline | Horizontal Scroll Option</title>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<h1>Timeline horizontal scroll option</h1>
<div id="mytimeline"></div>
<script>
// create groups
var numberOfGroups = 25;
var groups = new vis.DataSet()
for (var i = 0; i < numberOfGroups; i++) {
groups.add({
id: i,
content: 'Truck&nbsp;' + i
})
}
// create items
var numberOfItems = 1000;
var items = new vis.DataSet();
var itemsPerGroup = Math.round(numberOfItems/numberOfGroups);
for (var truck = 0; truck < numberOfGroups; truck++) {
var date = new Date();
for (var order = 0; order < itemsPerGroup; order++) {
date.setHours(date.getHours() + 4 * (Math.random() < 0.2));
var start = new Date(date);
date.setHours(date.getHours() + 2 + Math.floor(Math.random()*4));
var end = new Date(date);
items.add({
id: order + itemsPerGroup * truck,
group: truck,
start: start,
end: end,
content: 'Order ' + order
});
}
}
// specify options
var options = {
stack: true,
horizontalScroll: true,
zoomKey: 'ctrlKey',
maxHeight: 400,
start: new Date(),
end: new Date(1000*60*60*24 + (new Date()).valueOf()),
editable: true,
margin: {
item: 10, // minimal margin between items
axis: 5 // minimal margin between items and the axis
},
orientation: 'top'
};
// create a Timeline
var container = document.getElementById('mytimeline');
timeline = new vis.Timeline(container, items, groups, options);
</script>
</body>
</html>

+ 92
- 0
examples/timeline/other/verticalScroll.html View File

@ -0,0 +1,92 @@
<html>
<head>
<title>Timeline | Vertical Scroll Option</title>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<h1>Timeline vertical scroll option</h1>
<h2>With <code>
verticalScroll: true,
zoomKey: 'ctrlKey'</code>
</h2>
<div id="mytimeline1"></div>
<h2>With <code>
horizontalScroll: true,
verticalScroll: true,
zoomKey: 'ctrlKey'</code>
</h2>
<div id="mytimeline2"></div>
<script>
// create groups
var numberOfGroups = 25;
var groups = new vis.DataSet()
for (var i = 0; i < numberOfGroups; i++) {
groups.add({
id: i,
content: 'Truck&nbsp;' + i
})
}
// create items
var numberOfItems = 1000;
var items = new vis.DataSet();
var itemsPerGroup = Math.round(numberOfItems/numberOfGroups);
for (var truck = 0; truck < numberOfGroups; truck++) {
var date = new Date();
for (var order = 0; order < itemsPerGroup; order++) {
date.setHours(date.getHours() + 4 * (Math.random() < 0.2));
var start = new Date(date);
date.setHours(date.getHours() + 2 + Math.floor(Math.random()*4));
var end = new Date(date);
items.add({
id: order + itemsPerGroup * truck,
group: truck,
start: start,
end: end,
content: 'Order ' + order
});
}
}
// specify options
var options = {
stack: true,
verticalScroll: true,
zoomKey: 'ctrlKey',
maxHeight: 200,
start: new Date(),
end: new Date(1000*60*60*24 + (new Date()).valueOf()),
editable: true,
margin: {
item: 10, // minimal margin between items
axis: 5 // minimal margin between items and the axis
},
orientation: 'top'
};
// create a Timeline
options1 = Object.assign({}, options)
var container1 = document.getElementById('mytimeline1');
timeline1 = new vis.Timeline(container1, items, groups, options1);
options2 = Object.assign({horizontalScroll: true}, options)
var container2 = document.getElementById('mytimeline2');
timeline2 = new vis.Timeline(container2, items, groups, options2);
</script>
</body>
</html>

+ 327
- 562
lib/graph3d/Graph3d.js
File diff suppressed because it is too large
View File


+ 436
- 0
lib/graph3d/Settings.js View File

@ -0,0 +1,436 @@
////////////////////////////////////////////////////////////////////////////////
// This modules handles the options for Graph3d.
//
////////////////////////////////////////////////////////////////////////////////
var Camera = require('./Camera');
var Point3d = require('./Point3d');
// enumerate the available styles
var STYLE = {
BAR : 0,
BARCOLOR: 1,
BARSIZE : 2,
DOT : 3,
DOTLINE : 4,
DOTCOLOR: 5,
DOTSIZE : 6,
GRID : 7,
LINE : 8,
SURFACE : 9
};
// The string representations of the styles
var STYLENAME = {
'dot' : STYLE.DOT,
'dot-line' : STYLE.DOTLINE,
'dot-color': STYLE.DOTCOLOR,
'dot-size' : STYLE.DOTSIZE,
'line' : STYLE.LINE,
'grid' : STYLE.GRID,
'surface' : STYLE.SURFACE,
'bar' : STYLE.BAR,
'bar-color': STYLE.BARCOLOR,
'bar-size' : STYLE.BARSIZE
};
/**
* Field names in the options hash which are of relevance to the user.
*
* Specifically, these are the fields which require no special handling,
* and can be directly copied over.
*/
var OPTIONKEYS = [
'width',
'height',
'filterLabel',
'legendLabel',
'xLabel',
'yLabel',
'zLabel',
'xValueLabel',
'yValueLabel',
'zValueLabel',
'showGrid',
'showPerspective',
'showShadow',
'keepAspectRatio',
'verticalRatio',
'dotSizeRatio',
'showAnimationControls',
'animationInterval',
'animationPreload',
'animationAutoStart',
'axisColor',
'gridColor',
'xCenter',
'yCenter'
];
/**
* Field names in the options hash which are of relevance to the user.
*
* Same as OPTIONKEYS, but internally these fields are stored with
* prefix 'default' in the name.
*/
var PREFIXEDOPTIONKEYS = [
'xBarWidth',
'yBarWidth',
'valueMin',
'valueMax',
'xMin',
'xMax',
'xStep',
'yMin',
'yMax',
'yStep',
'zMin',
'zMax',
'zStep'
];
// Placeholder for DEFAULTS reference
var DEFAULTS = undefined;
/**
* Check if given hash is empty.
*
* Source: http://stackoverflow.com/a/679937
*/
function isEmpty(obj) {
for(var prop in obj) {
if (obj.hasOwnProperty(prop))
return false;
}
return true;
}
/**
* Make first letter of parameter upper case.
*
* Source: http://stackoverflow.com/a/1026087
*/
function capitalize(str) {
if (str === undefined || str === "") {
return str;
}
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* Add a prefix to a field name, taking style guide into account
*/
function prefixFieldName(prefix, fieldName) {
if (prefix === undefined || prefix === "") {
return fieldName;
}
return prefix + capitalize(fieldName);
}
/**
* Forcibly copy fields from src to dst in a controlled manner.
*
* A given field in dst will always be overwitten. If this field
* is undefined or not present in src, the field in dst will
* be explicitly set to undefined.
*
* The intention here is to be able to reset all option fields.
*
* Only the fields mentioned in array 'fields' will be handled.
*
* @param fields array with names of fields to copy
* @param prefix optional; prefix to use for the target fields.
*/
function forceCopy(src, dst, fields, prefix) {
var srcKey;
var dstKey;
for (var i in fields) {
srcKey = fields[i];
dstKey = prefixFieldName(prefix, srcKey);
dst[dstKey] = src[srcKey];
}
}
/**
* Copy fields from src to dst in a safe and controlled manner.
*
* Only the fields mentioned in array 'fields' will be copied over,
* and only if these are actually defined.
*
* @param fields array with names of fields to copy
* @param prefix optional; prefix to use for the target fields.
*/
function safeCopy(src, dst, fields, prefix) {
var srcKey;
var dstKey;
for (var i in fields) {
srcKey = fields[i];
if (src[srcKey] === undefined) continue;
dstKey = prefixFieldName(prefix, srcKey);
dst[dstKey] = src[srcKey];
}
}
/**
* Initialize dst with the values in src.
*
* src is the hash with the default values.
* A reference DEFAULTS to this hash is stored locally for
* further handling.
*
* For now, dst is assumed to be a Graph3d instance.
*/
function setDefaults(src, dst) {
if (src === undefined || isEmpty(src)) {
throw new Error('No DEFAULTS passed');
}
if (dst === undefined) {
throw new Error('No dst passed');
}
// Remember defaults for future reference
DEFAULTS = src;
// Handle the defaults which can be simply copied over
forceCopy(src, dst, OPTIONKEYS);
forceCopy(src, dst, PREFIXEDOPTIONKEYS, 'default');
// Handle the more complex ('special') fields
setSpecialSettings(src, dst);
// Following are internal fields, not part of the user settings
dst.margin = 10; // px
dst.showGrayBottom = false; // TODO: this does not work correctly
dst.showTooltip = false;
dst.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
}
function setOptions(options, dst) {
if (options === undefined) {
return;
}
if (dst === undefined) {
throw new Error('No dst passed');
}
if (DEFAULTS === undefined || isEmpty(DEFAULTS)) {
throw new Error('DEFAULTS not set for module Settings');
}
// Handle the parameters which can be simply copied over
safeCopy(options, dst, OPTIONKEYS);
safeCopy(options, dst, PREFIXEDOPTIONKEYS, 'default');
// Handle the more complex ('special') fields
setSpecialSettings(options, dst);
}
/**
* Special handling for certain parameters
*
* 'Special' here means: setting requires more than a simple copy
*/
function setSpecialSettings(src, dst) {
if (src.backgroundColor !== undefined) {
setBackgroundColor(src.backgroundColor, dst);
}
setDataColor(src.dataColor, dst);
setStyle(src.style, dst);
setShowLegend(src.showLegend, dst);
setCameraPosition(src.cameraPosition, dst);
// As special fields go, this is an easy one; just a translation of the name.
// Can't use this.tooltip directly, because that field exists internally
if (src.tooltip !== undefined) {
dst.showTooltip = src.tooltip;
}
}
/**
* Set the value of setting 'showLegend'
*
* This depends on the value of the style fields, so it must be called
* after the style field has been initialized.
*/
function setShowLegend(showLegend, dst) {
if (showLegend === undefined) {
// If the default was auto, make a choice for this field
var isAutoByDefault = (DEFAULTS.showLegend === undefined);
if (isAutoByDefault) {
// these styles default to having legends
var isLegendGraphStyle = dst.style === STYLE.DOTCOLOR
|| dst.style === STYLE.DOTSIZE;
dst.showLegend = isLegendGraphStyle;
} else {
// Leave current value as is
}
} else {
dst.showLegend = showLegend;
}
}
/**
* Retrieve the style index from given styleName
* @param {string} styleName Style name such as 'dot', 'grid', 'dot-line'
* @return {Number} styleNumber Enumeration value representing the style, or -1
* when not found
*/
function getStyleNumberByName(styleName) {
var number = STYLENAME[styleName];
if (number === undefined) {
return -1;
}
return number;
}
/**
* Check if given number is a valid style number.
*
* @return true if valid, false otherwise
*/
function checkStyleNumber(style) {
var valid = false;
for (var n in STYLE) {
if (STYLE[n] === style) {
valid = true;
break;
}
}
return valid;
}
function setStyle(style, dst) {
if (style === undefined) {
return; // Nothing to do
}
var styleNumber;
if (typeof style === 'string') {
styleNumber = getStyleNumberByName(style);
if (styleNumber === -1 ) {
throw new Error('Style \'' + style + '\' is invalid');
}
} else {
// Do a pedantic check on style number value
if (!checkStyleNumber(style)) {
throw new Error('Style \'' + style + '\' is invalid');
}
styleNumber = style;
}
dst.style = styleNumber;
}
/**
* Set the background styling for the graph
* @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor
*/
function setBackgroundColor(backgroundColor, dst) {
var fill = 'white';
var stroke = 'gray';
var strokeWidth = 1;
if (typeof(backgroundColor) === 'string') {
fill = backgroundColor;
stroke = 'none';
strokeWidth = 0;
}
else if (typeof(backgroundColor) === 'object') {
if (backgroundColor.fill !== undefined) fill = backgroundColor.fill;
if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke;
if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth;
}
else {
throw new Error('Unsupported type of backgroundColor');
}
dst.frame.style.backgroundColor = fill;
dst.frame.style.borderColor = stroke;
dst.frame.style.borderWidth = strokeWidth + 'px';
dst.frame.style.borderStyle = 'solid';
}
function setDataColor(dataColor, dst) {
if (dataColor === undefined) {
return; // Nothing to do
}
if (dst.dataColor === undefined) {
dst.dataColor = {};
}
if (typeof dataColor === 'string') {
dst.dataColor.fill = dataColor;
dst.dataColor.stroke = dataColor;
}
else {
if (dataColor.fill) {
dst.dataColor.fill = dataColor.fill;
}
if (dataColor.stroke) {
dst.dataColor.stroke = dataColor.stroke;
}
if (dataColor.strokeWidth !== undefined) {
dst.dataColor.strokeWidth = dataColor.strokeWidth;
}
}
}
function setCameraPosition(cameraPosition, dst) {
var camPos = cameraPosition;
if (camPos === undefined) {
return;
}
if (dst.camera === undefined) {
dst.camera = new Camera();
}
dst.camera.setArmRotation(camPos.horizontal, camPos.vertical);
dst.camera.setArmLength(camPos.distance);
}
module.exports.STYLE = STYLE;
module.exports.setDefaults = setDefaults;
module.exports.setOptions = setOptions;
module.exports.setCameraPosition = setCameraPosition;

+ 112
- 31
lib/timeline/Core.js View File

@ -82,7 +82,6 @@ Core.prototype._create = function (container) {
this.dom.centerContainer.appendChild(this.dom.center); this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left); this.dom.leftContainer.appendChild(this.dom.left);
this.dom.rightContainer.appendChild(this.dom.right); this.dom.rightContainer.appendChild(this.dom.right);
this.dom.centerContainer.appendChild(this.dom.shadowTop); this.dom.centerContainer.appendChild(this.dom.shadowTop);
this.dom.centerContainer.appendChild(this.dom.shadowBottom); this.dom.centerContainer.appendChild(this.dom.shadowBottom);
this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
@ -90,9 +89,26 @@ Core.prototype._create = function (container) {
this.dom.rightContainer.appendChild(this.dom.shadowTopRight); this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
// size properties of each of the panels
this.props = {
root: {},
background: {},
centerContainer: {},
leftContainer: {},
rightContainer: {},
center: {},
left: {},
right: {},
top: {},
bottom: {},
border: {},
scrollTop: 0,
scrollTopMin: 0
};
this.on('rangechange', function () { this.on('rangechange', function () {
if (this.initialDrawDone === true) { if (this.initialDrawDone === true) {
this._redraw(); // this allows overriding the _redraw method
this._redraw();
} }
}.bind(this)); }.bind(this));
this.on('touch', this._onTouch.bind(this)); this.on('touch', this._onTouch.bind(this));
@ -154,29 +170,61 @@ Core.prototype._create = function (container) {
}.bind(this)); }.bind(this));
function onMouseWheel(event) { function onMouseWheel(event) {
if (this.isActive()) {
this.emit('mousewheel', event);
}
// prevent scrolling when zoomKey defined or activated
if (!this.options.zoomKey || event[this.options.zoomKey]) return
// prevent scrolling vertically when horizontalScroll is true
if (this.options.horizontalScroll) return
var delta = 0;
if (event.wheelDelta) { /* IE/Opera. */
delta = event.wheelDelta / 120;
} else if (event.detail) { /* Mozilla case. */
// In Mozilla, sign of delta is different than in IE.
// Also, delta is multiple of 3.
delta = -event.detail / 3;
}
var current = this.props.scrollTop;
var adjusted = current + delta * 120;
if (this.isActive()) {
this._setScrollTop(adjusted);
this._redraw();
this.emit('scroll', event);
}
// Prevent default actions caused by mouse wheel
// (else the page and timeline both scroll)
event.preventDefault();
}
if (this.dom.root.addEventListener) {
// IE9, Chrome, Safari, Opera
this.dom.root.addEventListener("mousewheel", onMouseWheel.bind(this), false);
// Firefox
this.dom.root.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false);
} else {
// IE 6/7/8
this.dom.root.attachEvent("onmousewheel", onMouseWheel.bind(this));
}
function onMouseScrollSide(event) {
var current = this.scrollTop;
var adjusted = -current;
if (me.isActive()) { if (me.isActive()) {
me.emit('mousewheel', event);
me._setScrollTop(adjusted);
me._redraw();
me.emit('scroll', event);
} }
} }
this.dom.root.addEventListener('mousewheel', onMouseWheel);
this.dom.root.addEventListener('DOMMouseScroll', onMouseWheel);
// size properties of each of the panels
this.props = {
root: {},
background: {},
centerContainer: {},
leftContainer: {},
rightContainer: {},
center: {},
left: {},
right: {},
top: {},
bottom: {},
border: {},
scrollTop: 0,
scrollTopMin: 0
};
this.dom.left.parentNode.addEventListener('scroll', onMouseScrollSide);
this.dom.right.parentNode.addEventListener('scroll', onMouseScrollSide);
this.customTimes = []; this.customTimes = [];
@ -222,17 +270,23 @@ Core.prototype.setOptions = function (options) {
var fields = [ var fields = [
'width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'width', 'height', 'minHeight', 'maxHeight', 'autoResize',
'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates',
'locale', 'locales', 'moment', 'rtl'
'locale', 'locales', 'moment', 'rtl', 'zoomKey', 'horizontalScroll', 'verticalScroll'
]; ];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
if (this.options.rtl) { if (this.options.rtl) {
var contentContainer = this.dom.leftContainer;
this.dom.leftContainer = this.dom.rightContainer;
this.dom.rightContainer = contentContainer;
this.dom.container.style.direction = "rtl"; this.dom.container.style.direction = "rtl";
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; }
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl';
}
if (this.options.verticalScroll) {
if (this.options.rtl) {
this.dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
} else {
this.dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
}
}
this.options.orientation = {item:undefined,axis:undefined}; this.options.orientation = {item:undefined,axis:undefined};
if ('orientation' in options) { if ('orientation' in options) {
@ -705,9 +759,25 @@ Core.prototype._redraw = function() {
// calculate the widths of the panels // calculate the widths of the panels
props.root.width = dom.root.offsetWidth; props.root.width = dom.root.offsetWidth;
props.background.width = props.root.width - borderRootWidth; props.background.width = props.root.width - borderRootWidth;
props.left.width = dom.leftContainer.clientWidth || -props.border.left;
if (!this.initialDrawDone) {
props.scrollbarWidth = util.getScrollBarWidth();
}
if (this.options.verticalScroll) {
if (this.options.rtl) {
props.left.width = dom.leftContainer.clientWidth || -props.border.left;
props.right.width = dom.rightContainer.clientWidth + props.scrollbarWidth || -props.border.right;
} else {
props.left.width = dom.leftContainer.clientWidth + props.scrollbarWidth || -props.border.left;
props.right.width = dom.rightContainer.clientWidth || -props.border.right;
}
} else {
props.left.width = dom.leftContainer.clientWidth || -props.border.left;
props.right.width = dom.rightContainer.clientWidth || -props.border.right;
}
props.leftContainer.width = props.left.width; props.leftContainer.width = props.left.width;
props.right.width = dom.rightContainer.clientWidth || -props.border.right;
props.rightContainer.width = props.right.width; props.rightContainer.width = props.right.width;
var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth;
props.center.width = centerWidth; props.center.width = centerWidth;
@ -761,10 +831,8 @@ Core.prototype._redraw = function() {
dom.center.style.left = '0'; dom.center.style.left = '0';
dom.center.style.top = offset + 'px'; dom.center.style.top = offset + 'px';
dom.left.style.left = '0'; dom.left.style.left = '0';
dom.left.style.top = offset + 'px';
dom.right.style.left = '0'; dom.right.style.left = '0';
dom.right.style.top = offset + 'px';
// show shadows when vertical scrolling is available // show shadows when vertical scrolling is available
var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : '';
var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : '';
@ -775,6 +843,18 @@ Core.prototype._redraw = function() {
dom.shadowTopRight.style.visibility = visibilityTop; dom.shadowTopRight.style.visibility = visibilityTop;
dom.shadowBottomRight.style.visibility = visibilityBottom; dom.shadowBottomRight.style.visibility = visibilityBottom;
if (this.options.verticalScroll) {
this.dom.shadowTopRight.style.visibility = "hidden";
this.dom.shadowBottomRight.style.visibility = "hidden";
this.dom.shadowTopLeft.style.visibility = "hidden";
this.dom.shadowBottomLeft.style.visibility = "hidden";
document.getElementsByClassName('vis-left')[0].scrollTop = -offset;
document.getElementsByClassName('vis-right')[0].scrollTop = -offset;
} else {
dom.left.style.top = offset + 'px';
dom.right.style.top = offset + 'px';
}
// enable/disable vertical panning // enable/disable vertical panning
var contentsOverflow = this.props.center.height > this.props.centerContainer.height; var contentsOverflow = this.props.center.height > this.props.centerContainer.height;
this.hammer.get('pan').set({ this.hammer.get('pan').set({
@ -797,6 +877,7 @@ Core.prototype._redraw = function() {
} else { } else {
this.redrawCount = 0; this.redrawCount = 0;
} }
this.initialDrawDone = true; this.initialDrawDone = true;
//Emit public 'changed' event for UI updates, see issue #1592 //Emit public 'changed' event for UI updates, see issue #1592

+ 25
- 13
lib/timeline/Range.js View File

@ -81,7 +81,7 @@ Range.prototype.setOptions = function (options) {
// copy the options that we know // copy the options that we know
var fields = [ var fields = [
'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable',
'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl'
'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl', 'horizontalScroll'
]; ];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
@ -483,15 +483,10 @@ Range.prototype._onDragEnd = function (event) {
* @private * @private
*/ */
Range.prototype._onMouseWheel = function(event) { Range.prototype._onMouseWheel = function(event) {
// only allow zooming when configured as zoomable and moveable
if (!(this.options.zoomable && this.options.moveable)) return;
// only zoom when the mouse is inside the current range
if (!this._isInsideRange(event)) return;
// Prevent default actions caused by mouse wheel
// (else the page and timeline both zoom and scroll)
event.preventDefault();
// only zoom when the according key is pressed and the zoomKey option is set
if (this.options.zoomKey && !event[this.options.zoomKey]) return;
// retrieve delta // retrieve delta
var delta = 0; var delta = 0;
if (event.wheelDelta) { /* IE/Opera. */ if (event.wheelDelta) { /* IE/Opera. */
@ -502,6 +497,27 @@ Range.prototype._onMouseWheel = function(event) {
delta = -event.detail / 3; delta = -event.detail / 3;
} }
// don't allow zoom when the according key is pressed and the zoomKey option or not zoomable but movable
if ((this.options.zoomKey && !event[this.options.zoomKey] && this.options.zoomable)
|| (!this.options.zoomable && this.options.moveable)) {
if (this.options.horizontalScroll) {
// calculate a single scroll jump relative to the range scale
var diff = delta * (this.end - this.start) / 20;
// calculate new start and end
var newStart = this.start - diff;
var newEnd = this.end - diff;
this.setRange(newStart, newEnd);
}
return;
}
// only allow zooming when configured as zoomable and moveable
if (!(this.options.zoomable && this.options.moveable)) return;
// only zoom when the mouse is inside the current range
if (!this._isInsideRange(event)) return;
// If delta is nonzero, handle it. // If delta is nonzero, handle it.
// Basically, delta is now positive if wheel was scrolled up, // Basically, delta is now positive if wheel was scrolled up,
// and negative, if wheel was scrolled down. // and negative, if wheel was scrolled down.
@ -524,10 +540,6 @@ Range.prototype._onMouseWheel = function(event) {
this.zoom(scale, pointerDate, delta); this.zoom(scale, pointerDate, delta);
} }
// Prevent default actions caused by mouse wheel
// (else the page and timeline both zoom and scroll)
event.preventDefault();
}; };
/** /**

+ 1
- 1
lib/timeline/Timeline.js View File

@ -52,7 +52,6 @@ function Timeline (container, items, groups, options) {
axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both' axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
item: 'bottom' // not relevant item: 'bottom' // not relevant
}, },
rtl: false,
moment: moment, moment: moment,
width: null, width: null,
@ -61,6 +60,7 @@ function Timeline (container, items, groups, options) {
minHeight: null minHeight: null
}; };
this.options = util.deepExtend({}, this.defaultOptions); this.options = util.deepExtend({}, this.defaultOptions);
this.options.rtl = options.rtl;
// Create the DOM, props, and emitter // Create the DOM, props, and emitter
this._create(container); this._create(container);

+ 13
- 4
lib/timeline/component/ItemSet.js View File

@ -27,7 +27,6 @@ var BACKGROUND = '__background__'; // reserved group id for background items wit
function ItemSet(body, options) { function ItemSet(body, options) {
this.body = body; this.body = body;
this.defaultOptions = { this.defaultOptions = {
rtl: false,
type: null, // 'box', 'point', 'range', 'background' type: null, // 'box', 'point', 'range', 'background'
orientation: { orientation: {
item: 'bottom' // item orientation: 'top' or 'bottom' item: 'bottom' // item orientation: 'top' or 'bottom'
@ -96,7 +95,8 @@ function ItemSet(body, options) {
// options is shared by this ItemSet and all its items // options is shared by this ItemSet and all its items
this.options = util.extend({}, this.defaultOptions); this.options = util.extend({}, this.defaultOptions);
this.options.rtl = options.rtl;
// options for getting items from the DataSet with the correct type // options for getting items from the DataSet with the correct type
this.itemOptions = { this.itemOptions = {
type: {start: 'Date', end: 'Date'} type: {start: 'Date', end: 'Date'}
@ -230,7 +230,12 @@ ItemSet.prototype._create = function(){
// add item on doubletap // add item on doubletap
this.hammer.on('doubletap', this._onAddItem.bind(this)); this.hammer.on('doubletap', this._onAddItem.bind(this));
this.groupHammer = new Hammer(this.body.dom.leftContainer);
if (this.options.rtl) {
this.groupHammer = new Hammer(this.body.dom.rightContainer);
} else {
this.groupHammer = new Hammer(this.body.dom.leftContainer);
}
this.groupHammer.on('panstart', this._onGroupDragStart.bind(this)); this.groupHammer.on('panstart', this._onGroupDragStart.bind(this));
this.groupHammer.on('panmove', this._onGroupDrag.bind(this)); this.groupHammer.on('panmove', this._onGroupDrag.bind(this));
@ -451,7 +456,11 @@ ItemSet.prototype.show = function() {
// show labelset containing labels // show labelset containing labels
if (!this.dom.labelSet.parentNode) { if (!this.dom.labelSet.parentNode) {
this.body.dom.left.appendChild(this.dom.labelSet);
if (this.options.rtl) {
this.body.dom.right.appendChild(this.dom.labelSet);
} else {
this.body.dom.left.appendChild(this.dom.labelSet);
}
} }
}; };

+ 22
- 1
lib/timeline/component/css/panel.css View File

@ -1,4 +1,3 @@
.vis-panel { .vis-panel {
position: absolute; position: absolute;
@ -24,6 +23,28 @@
overflow: hidden; overflow: hidden;
} }
.vis-left.vis-panel.vis-vertical-scroll, .vis-right.vis-panel.vis-vertical-scroll {
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
}
.vis-left.vis-panel.vis-vertical-scroll {
direction: rtl;
}
.vis-left.vis-panel.vis-vertical-scroll .vis-content {
direction: ltr;
}
.vis-right.vis-panel.vis-vertical-scroll {
direction: ltr;
}
.vis-right.vis-panel.vis-vertical-scroll .vis-content {
direction: rtl;
}
.vis-panel.vis-center, .vis-panel.vis-center,
.vis-panel.vis-top, .vis-panel.vis-top,
.vis-panel.vis-bottom { .vis-panel.vis-bottom {

+ 2
- 0
lib/timeline/optionsTimeline.js View File

@ -26,6 +26,8 @@ let allOptions = {
//globals : //globals :
align: {string}, align: {string},
rtl: {boolean, 'undefined': 'undefined'}, rtl: {boolean, 'undefined': 'undefined'},
verticalScroll: {boolean, 'undefined': 'undefined'},
horizontalScroll: {boolean, 'undefined': 'undefined'},
autoResize: {boolean}, autoResize: {boolean},
clickToUse: {boolean}, clickToUse: {boolean},
dataAttributes: {string, array}, dataAttributes: {string, array},

+ 26
- 0
lib/util.js View File

@ -1452,3 +1452,29 @@ exports.easingFunctions = {
return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t
} }
}; };
exports.getScrollBarWidth = function () {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild (inner);
document.body.appendChild (outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild (outer);
return (w1 - w2);
};

Loading…
Cancel
Save