Browse Source

Implemented events `click`, `doubleClick`, and `contextMenu`. Implemented method `getEventProperties(event)`.

v3_develop
jos 9 years ago
parent
commit
36cb8193d1
12 changed files with 478 additions and 281 deletions
  1. +2
    -0
      HISTORY.md
  2. +287
    -226
      dist/vis.js
  3. +1
    -1
      dist/vis.map
  4. +14
    -14
      dist/vis.min.js
  5. +6
    -6
      docs/network.html
  6. +63
    -10
      docs/timeline.html
  7. +6
    -6
      lib/timeline/Core.js
  8. +53
    -0
      lib/timeline/Timeline.js
  9. +6
    -16
      lib/timeline/component/ItemSet.js
  10. +18
    -0
      lib/util.js
  11. +10
    -2
      test/timeline.html
  12. +12
    -0
      test/timeline_groups.html

+ 2
- 0
HISTORY.md View File

@ -26,6 +26,8 @@ http://visjs.org
ctrl key down.
- Implemented configuration option `order: function` to define a custom ordering
for the items (see #538, #234).
- Implemented events `click`, `doubleClick`, and `contextMenu`.
- Implemented method `getEventProperties(event)`.
- Fixed not property initializing with a DataView for groups.
- Merged add custom timebar functionality, thanks @aytech!
- Fixed #664: end of item not restored when canceling a move event.

+ 287
- 226
dist/vis.js View File

@ -827,6 +827,24 @@ return /******/ (function(modules) { // webpackBootstrap
return target;
};
/**
* Check if given element contains given parent somewhere in the DOM tree
* @param {Element} element
* @param {Element} parent
*/
exports.hasParent = function (element, parent) {
var e = element;
while (e) {
if (e === parent) {
return true;
}
e = e.parentNode;
}
return false;
};
exports.option = {};
/**
@ -6576,6 +6594,16 @@ return /******/ (function(modules) { // webpackBootstrap
this.itemsData = null; // DataSet
this.groupsData = null; // DataSet
this.on('tap', function (event) {
me.emit('click', me.getEventProperties(event))
});
this.on('doubletap', function (event) {
me.emit('doubleClick', me.getEventProperties(event))
});
this.dom.root.oncontextmenu = function (event) {
me.emit('contextmenu', me.getEventProperties(event))
};
// apply options
if (options) {
this.setOptions(options);
@ -6800,6 +6828,49 @@ return /******/ (function(modules) { // webpackBootstrap
};
};
/**
* Generate Timeline related information from an event
* @param {Event} event
* @return {Object} An object with related information, like on which area
* The event happened, whether clicked on an item, etc.
*/
Timeline.prototype.getEventProperties = function (event) {
var item = this.itemSet.itemFromTarget(event);
var group = this.itemSet.groupFromTarget(event);
var pageX = event.gesture ? event.gesture.center.pageX : event.pageX;
var pageY = event.gesture ? event.gesture.center.pageY : event.pageY;
var x = pageX - util.getAbsoluteLeft(this.dom.centerContainer);
var y = pageY - util.getAbsoluteTop(this.dom.centerContainer);
var snap = this.itemSet.options.snap || null;
var scale = this.body.util.getScale();
var step = this.body.util.getStep();
var time = this._toTime(x);
var snappedTime = snap ? snap(time, scale, step) : time;
var element = util.getTarget(event);
var what = null;
if (item != null) {what = 'item';}
else if (util.hasParent(element, this.dom.center)) {what = 'background';}
else if (util.hasParent(element, this.timeAxis.dom.foreground)) {what = 'axis';}
else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {what = 'axis';}
else if (util.hasParent(element, this.itemSet.dom.labelSet)) {what = 'group-label';}
else if (util.hasParent(element, this.customTime.bar)) {what = 'custom-time';}
else if (util.hasParent(element, this.currentTime.bar)) {what = 'current-time';}
return {
event: event,
item: item ? item.id : null,
group: group ? group.groupId : null,
what: what,
pageX: pageX,
pageY: pageY,
x: x,
y: y,
time: time,
snappedTime: snappedTime
}
};
module.exports = Timeline;
@ -13466,7 +13537,7 @@ return /******/ (function(modules) { // webpackBootstrap
*/
ItemSet.prototype._onTouch = function (event) {
// store the touched item, used in _onDragStart
this.touchParams.item = ItemSet.itemFromTarget(event);
this.touchParams.item = this.itemFromTarget(event);
};
/**
@ -13791,7 +13862,7 @@ return /******/ (function(modules) { // webpackBootstrap
var oldSelection = this.getSelection();
var item = ItemSet.itemFromTarget(event);
var item = this.itemFromTarget(event);
var selection = item ? [item.id] : [];
this.setSelection(selection);
@ -13817,7 +13888,7 @@ return /******/ (function(modules) { // webpackBootstrap
var me = this,
snap = this.options.snap || null,
item = ItemSet.itemFromTarget(event);
item = this.itemFromTarget(event);
if (item) {
// update item
@ -13875,7 +13946,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (!this.options.selectable) return;
var selection,
item = ItemSet.itemFromTarget(event);
item = this.itemFromTarget(event);
if (item) {
// multi select items
@ -13963,7 +14034,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @param {Event} event
* @return {Item | null} item
*/
ItemSet.itemFromTarget = function(event) {
ItemSet.prototype.itemFromTarget = function(event) {
var target = event.target;
while (target) {
if (target.hasOwnProperty('timeline-item')) {
@ -13982,17 +14053,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @return {Group | null} group
*/
ItemSet.prototype.groupFromTarget = function(event) {
// TODO: cleanup when the new solution is stable (also on mobile)
//var target = event.target;
//while (target) {
// if (target.hasOwnProperty('timeline-group')) {
// return target['timeline-group'];
// }
// target = target.parentNode;
//}
//
var clientY = event.gesture.center.clientY;
var clientY = event.gesture ? event.gesture.center.clientY : event.clientY;
for (var i = 0; i < this.groupIds.length; i++) {
var groupId = this.groupIds[i];
var group = this.groups[groupId];
@ -15705,7 +15766,7 @@ return /******/ (function(modules) { // webpackBootstrap
var Emitter = __webpack_require__(56);
var Hammer = __webpack_require__(45);
var keycharm = __webpack_require__(57);
var keycharm = __webpack_require__(59);
var util = __webpack_require__(1);
var hammerUtil = __webpack_require__(47);
var DataSet = __webpack_require__(3);
@ -22577,7 +22638,7 @@ return /******/ (function(modules) { // webpackBootstrap
// first check if moment.js is already loaded in the browser window, if so,
// use this instance. Else, load via commonjs.
module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(58);
module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(57);
/***/ },
@ -22587,7 +22648,7 @@ return /******/ (function(modules) { // webpackBootstrap
// Only load hammer.js when in a browser environment
// (loading hammer.js in a node.js environment gives errors)
if (typeof window !== 'undefined') {
module.exports = window['Hammer'] || __webpack_require__(59);
module.exports = window['Hammer'] || __webpack_require__(58);
}
else {
module.exports = function () {
@ -22629,7 +22690,7 @@ return /******/ (function(modules) { // webpackBootstrap
* top, bottom, content, and background panel.
* @param {Element} container The container element where the Core will
* be attached.
* @private
* @protected
*/
Core.prototype._create = function (container) {
this.dom = {};
@ -22657,7 +22718,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.dom.background.className = 'vispanel background';
this.dom.backgroundVertical.className = 'vispanel background vertical';
this.dom.backgroundHorizontal.className = 'vispanel background horizontal';
this.dom.centerContainer.className = 'vispanel center';
this.dom.centerContainer.className = 'vispanel center jooo';
this.dom.leftContainer.className = 'vispanel left';
this.dom.rightContainer.className = 'vispanel right';
this.dom.top.className = 'vispanel top';
@ -23389,7 +23450,7 @@ return /******/ (function(modules) { // webpackBootstrap
* Convert a position on screen (pixels) to a datetime
* @param {int} x Position on the screen in pixels
* @return {Date} time The datetime the corresponds with given position x
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toTime = function(x) {
@ -23400,7 +23461,7 @@ return /******/ (function(modules) { // webpackBootstrap
* Convert a position on the global screen (pixels) to a datetime
* @param {int} x Position on the screen in pixels
* @return {Date} time The datetime the corresponds with given position x
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toGlobalTime = function(x) {
@ -23414,7 +23475,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @param {Date} time A date
* @return {int} x The position on the screen in pixels which corresponds
* with the given date.
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toScreen = function(time) {
@ -23429,7 +23490,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @param {Date} time A date
* @return {int} x The position on root in pixels which corresponds
* with the given date.
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toGlobalScreen = function(time) {
@ -24371,7 +24432,7 @@ return /******/ (function(modules) { // webpackBootstrap
/* 53 */
/***/ function(module, exports, __webpack_require__) {
var keycharm = __webpack_require__(57);
var keycharm = __webpack_require__(59);
var Emitter = __webpack_require__(56);
var Hammer = __webpack_require__(45);
var util = __webpack_require__(1);
@ -24968,205 +25029,6 @@ return /******/ (function(modules) { // webpackBootstrap
/***/ },
/* 57 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict";
/**
* Created by Alex on 11/6/2014.
*/
// https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60
// if the module has no dependencies, the above pattern can be simplified to
(function (root, factory) {
if (true) {
// AMD. Register as an anonymous module.
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.keycharm = factory();
}
}(this, function () {
function keycharm(options) {
var preventDefault = options && options.preventDefault || false;
var container = options && options.container || window;
var _exportFunctions = {};
var _bound = {keydown:{}, keyup:{}};
var _keys = {};
var i;
// a - z
for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};}
// A - Z
for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};}
// 0 - 9
for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};}
// F1 - F12
for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};}
// num0 - num9
for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};}
// numpad misc
_keys['num*'] = {code:106, shift: false};
_keys['num+'] = {code:107, shift: false};
_keys['num-'] = {code:109, shift: false};
_keys['num/'] = {code:111, shift: false};
_keys['num.'] = {code:110, shift: false};
// arrows
_keys['left'] = {code:37, shift: false};
_keys['up'] = {code:38, shift: false};
_keys['right'] = {code:39, shift: false};
_keys['down'] = {code:40, shift: false};
// extra keys
_keys['space'] = {code:32, shift: false};
_keys['enter'] = {code:13, shift: false};
_keys['shift'] = {code:16, shift: undefined};
_keys['esc'] = {code:27, shift: false};
_keys['backspace'] = {code:8, shift: false};
_keys['tab'] = {code:9, shift: false};
_keys['ctrl'] = {code:17, shift: false};
_keys['alt'] = {code:18, shift: false};
_keys['delete'] = {code:46, shift: false};
_keys['pageup'] = {code:33, shift: false};
_keys['pagedown'] = {code:34, shift: false};
// symbols
_keys['='] = {code:187, shift: false};
_keys['-'] = {code:189, shift: false};
_keys[']'] = {code:221, shift: false};
_keys['['] = {code:219, shift: false};
var down = function(event) {handleEvent(event,'keydown');};
var up = function(event) {handleEvent(event,'keyup');};
// handle the actualy bound key with the event
var handleEvent = function(event,type) {
if (_bound[type][event.keyCode] !== undefined) {
var bound = _bound[type][event.keyCode];
for (var i = 0; i < bound.length; i++) {
if (bound[i].shift === undefined) {
bound[i].fn(event);
}
else if (bound[i].shift == true && event.shiftKey == true) {
bound[i].fn(event);
}
else if (bound[i].shift == false && event.shiftKey == false) {
bound[i].fn(event);
}
}
if (preventDefault == true) {
event.preventDefault();
}
}
};
// bind a key to a callback
_exportFunctions.bind = function(key, callback, type) {
if (type === undefined) {
type = 'keydown';
}
if (_keys[key] === undefined) {
throw new Error("unsupported key: " + key);
}
if (_bound[type][_keys[key].code] === undefined) {
_bound[type][_keys[key].code] = [];
}
_bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift});
};
// bind all keys to a call back (demo purposes)
_exportFunctions.bindAll = function(callback, type) {
if (type === undefined) {
type = 'keydown';
}
for (var key in _keys) {
if (_keys.hasOwnProperty(key)) {
_exportFunctions.bind(key,callback,type);
}
}
};
// get the key label from an event
_exportFunctions.getKey = function(event) {
for (var key in _keys) {
if (_keys.hasOwnProperty(key)) {
if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) {
return key;
}
else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) {
return key;
}
else if (event.keyCode == _keys[key].code && key == 'shift') {
return key;
}
}
}
return "unknown key, currently not supported";
};
// unbind either a specific callback from a key or all of them (by leaving callback undefined)
_exportFunctions.unbind = function(key, callback, type) {
if (type === undefined) {
type = 'keydown';
}
if (_keys[key] === undefined) {
throw new Error("unsupported key: " + key);
}
if (callback !== undefined) {
var newBindings = [];
var bound = _bound[type][_keys[key].code];
if (bound !== undefined) {
for (var i = 0; i < bound.length; i++) {
if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) {
newBindings.push(_bound[type][_keys[key].code][i]);
}
}
}
_bound[type][_keys[key].code] = newBindings;
}
else {
_bound[type][_keys[key].code] = [];
}
};
// reset all bound variables.
_exportFunctions.reset = function() {
_bound = {keydown:{}, keyup:{}};
};
// unbind all listeners and reset all variables.
_exportFunctions.destroy = function() {
_bound = {keydown:{}, keyup:{}};
container.removeEventListener('keydown', down, true);
container.removeEventListener('keyup', up, true);
};
// create listeners.
container.addEventListener('keydown',down,true);
container.addEventListener('keyup',up,true);
// return the public functions.
return _exportFunctions;
}
return keycharm;
}));
/***/ },
/* 58 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js
@ -28216,7 +28078,7 @@ return /******/ (function(modules) { // webpackBootstrap
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(71)(module)))
/***/ },
/* 59 */
/* 58 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20
@ -30382,6 +30244,205 @@ return /******/ (function(modules) { // webpackBootstrap
})(window);
/***/ },
/* 59 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict";
/**
* Created by Alex on 11/6/2014.
*/
// https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60
// if the module has no dependencies, the above pattern can be simplified to
(function (root, factory) {
if (true) {
// AMD. Register as an anonymous module.
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.keycharm = factory();
}
}(this, function () {
function keycharm(options) {
var preventDefault = options && options.preventDefault || false;
var container = options && options.container || window;
var _exportFunctions = {};
var _bound = {keydown:{}, keyup:{}};
var _keys = {};
var i;
// a - z
for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};}
// A - Z
for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};}
// 0 - 9
for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};}
// F1 - F12
for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};}
// num0 - num9
for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};}
// numpad misc
_keys['num*'] = {code:106, shift: false};
_keys['num+'] = {code:107, shift: false};
_keys['num-'] = {code:109, shift: false};
_keys['num/'] = {code:111, shift: false};
_keys['num.'] = {code:110, shift: false};
// arrows
_keys['left'] = {code:37, shift: false};
_keys['up'] = {code:38, shift: false};
_keys['right'] = {code:39, shift: false};
_keys['down'] = {code:40, shift: false};
// extra keys
_keys['space'] = {code:32, shift: false};
_keys['enter'] = {code:13, shift: false};
_keys['shift'] = {code:16, shift: undefined};
_keys['esc'] = {code:27, shift: false};
_keys['backspace'] = {code:8, shift: false};
_keys['tab'] = {code:9, shift: false};
_keys['ctrl'] = {code:17, shift: false};
_keys['alt'] = {code:18, shift: false};
_keys['delete'] = {code:46, shift: false};
_keys['pageup'] = {code:33, shift: false};
_keys['pagedown'] = {code:34, shift: false};
// symbols
_keys['='] = {code:187, shift: false};
_keys['-'] = {code:189, shift: false};
_keys[']'] = {code:221, shift: false};
_keys['['] = {code:219, shift: false};
var down = function(event) {handleEvent(event,'keydown');};
var up = function(event) {handleEvent(event,'keyup');};
// handle the actualy bound key with the event
var handleEvent = function(event,type) {
if (_bound[type][event.keyCode] !== undefined) {
var bound = _bound[type][event.keyCode];
for (var i = 0; i < bound.length; i++) {
if (bound[i].shift === undefined) {
bound[i].fn(event);
}
else if (bound[i].shift == true && event.shiftKey == true) {
bound[i].fn(event);
}
else if (bound[i].shift == false && event.shiftKey == false) {
bound[i].fn(event);
}
}
if (preventDefault == true) {
event.preventDefault();
}
}
};
// bind a key to a callback
_exportFunctions.bind = function(key, callback, type) {
if (type === undefined) {
type = 'keydown';
}
if (_keys[key] === undefined) {
throw new Error("unsupported key: " + key);
}
if (_bound[type][_keys[key].code] === undefined) {
_bound[type][_keys[key].code] = [];
}
_bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift});
};
// bind all keys to a call back (demo purposes)
_exportFunctions.bindAll = function(callback, type) {
if (type === undefined) {
type = 'keydown';
}
for (var key in _keys) {
if (_keys.hasOwnProperty(key)) {
_exportFunctions.bind(key,callback,type);
}
}
};
// get the key label from an event
_exportFunctions.getKey = function(event) {
for (var key in _keys) {
if (_keys.hasOwnProperty(key)) {
if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) {
return key;
}
else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) {
return key;
}
else if (event.keyCode == _keys[key].code && key == 'shift') {
return key;
}
}
}
return "unknown key, currently not supported";
};
// unbind either a specific callback from a key or all of them (by leaving callback undefined)
_exportFunctions.unbind = function(key, callback, type) {
if (type === undefined) {
type = 'keydown';
}
if (_keys[key] === undefined) {
throw new Error("unsupported key: " + key);
}
if (callback !== undefined) {
var newBindings = [];
var bound = _bound[type][_keys[key].code];
if (bound !== undefined) {
for (var i = 0; i < bound.length; i++) {
if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) {
newBindings.push(_bound[type][_keys[key].code][i]);
}
}
}
_bound[type][_keys[key].code] = newBindings;
}
else {
_bound[type][_keys[key].code] = [];
}
};
// reset all bound variables.
_exportFunctions.reset = function() {
_bound = {keydown:{}, keyup:{}};
};
// unbind all listeners and reset all variables.
_exportFunctions.destroy = function() {
_bound = {keydown:{}, keyup:{}};
container.removeEventListener('keydown', down, true);
container.removeEventListener('keyup', up, true);
};
// create listeners.
container.addEventListener('keydown',down,true);
container.addEventListener('keyup',up,true);
// return the public functions.
return _exportFunctions;
}
return keycharm;
}));
/***/ },
/* 60 */
/***/ function(module, exports, __webpack_require__) {

+ 1
- 1
dist/vis.map
File diff suppressed because it is too large
View File


+ 14
- 14
dist/vis.min.js
File diff suppressed because it is too large
View File


+ 6
- 6
docs/network.html View File

@ -165,14 +165,14 @@ The constructor accepts three parameters:
<code>edges</code>, which both contain an array with objects.
Optionally, data may contain an <code>options</code> object.
The parameter <code>data</code> is optional, data can also be set using
the method <code>setData</code>. Section <a href="#Data_Format">Data Format</a>
the method <code>setData</code>. Section <a href="#Data_format">Data Format</a>
describes the data object.
</li>
<li>
<code>options</code> is an optional Object containing a name-value map
with options. Options can also be set using the method
<code>setOptions</code>.
Section <a href="#Configuration_Options">Configuration Options</a>
Section <a href="#Configuration_options">Configuration Options</a>
describes the available options.
</li>
</ul>
@ -214,7 +214,7 @@ var data = {
<span style="font-weight: bold;">A property <code>options</code></span>,
containing an object with global options.
Options can be provided as third parameter in the network constructor
as well. Section <a href="#Configuration_Options">Configuration Options</a>
as well. Section <a href="#Configuration_options">Configuration Options</a>
describes the available options.
</li>
@ -647,7 +647,7 @@ var options = {
</tr>
<tr>
<td>freezeForStabilization</a></td>
<td>freezeForStabilization</td>
<td>Boolean</td>
<td>false</td>
<td>
@ -972,7 +972,7 @@ mySize = minSize + diff * scale;
<td>When using values, you can let the font scale with the size of the nodes if you enable the scaleFontWithValue option. This is the minimum value of the fontSize.</td>
</tr>
<tr>
<td></td>fontSizeMax</td>
<td>fontSizeMax</td>
<td>Number</td>
<td>30</td>
<td>When using values, you can let the font scale with the size of the nodes if you enable the scaleFontWithValue option. This is the maximum value of the fontSize.</td>
@ -1483,7 +1483,7 @@ To unify the physics system, the damping, repulsion distance and edge length hav
If no options for the physics system are supplied, the Barnes-Hut method will be used with the default parameters. If you want to customize the physics system easily, you can use the configurePhysics option. <br/>
When using the hierarchical display option, hierarchicalRepulsion is automatically used as the physics solver. Similarly, if you use the hierarchicalRepulsion physics option, hierarchical display is automatically turned on with default settings.
<p class="important_note">Note: if the behaviour of your network is not the way you want it, use configurePhysics as described <u><a href="#PhysicsConfiguration">below</a></u> or by <u><a href="../examples/network/25_physics_configuration.html">example 25</a></u>.</p>
<p class="important_note">Note: if the behaviour of your network is not the way you want it, use configurePhysics as described <u><a href="#PhysicsConfiguration">below</a></u> or by <u><a href="../examples/network/25_physics_configuration.html">example 25</a></u>.
</p>
<pre class="prettyprint">
// These variables must be defined in an options object named physics.

+ 63
- 10
docs/timeline.html View File

@ -919,6 +919,26 @@ timeline.clear({options: true}); // clear options only
</td>
</tr>
<tr id="getEventProperties">
<td>getEventProperties(event)</td>
<td>Object</td>
<td>
Returns an Object with relevant properties from an event:
<ul>
<li><code>group</code> (Number | null): the id of the clicked group.</li>
<li><code>item</code> (Number | null): the id of the clicked item.</li>
<li><code>pageX</code> (Number): absolute horizontal position of the click event.</li>
<li><code>pageY</code> (Number): absolute vertical position of the click event.</li>
<li><code>x</code> (Number): relative horizontal position of the click event.</li>
<li><code>y</code> (Number): relative vertical position of the click event.</li>
<li><code>time</code> (Date): Date of the clicked event.</li>
<li><code>snappedTime</code> (Date): Date of the clicked event, snapped to a nice value.</li>
<li><code>what</code> (String | null): name of the clicked thing: <code>item</code>, <code>background</code>, <code>axis</code>, <code>group-label</code>, <code>custom-time</code>, or <code>current-time</code>.</li>
<li><code>event</code> (Object): the original click event.</li>
</ul>
</td>
</tr>
<tr>
<td>getSelection()</td>
<td>Number[]</td>
@ -1093,16 +1113,49 @@ timeline.off('select', onSelect);
<th>Description</th>
<th>Properties</th>
</tr>
<tr>
<td>finishedRedraw</td>
<td>Fired after a redraw is complete. When moving the timeline around, this could be fired frequently.
</td>
<td>
none.
</td>
</tr>
<tr>
<tr>
<td>click</td>
<td>Fired when clicked inside the Timeline.
</td>
<td>
Passes a properties object as returned by the method <a href="#getEventProperties"><code>Timeline.getEventProperties(event)</code></a>.
</td>
</tr>
<tr>
<td>contextmenu</td>
<td>Fired when right-clicked inside the Timeline. Note that in order to prevent the context menu from showing up, default behavior of the event must be stopped:
<pre class="prettyprint lang-js">timeline.on('contextmenu', function (props) {
alert('Right click!');
props.event.preventDefault();
});
</pre>
</td>
<td>
Passes a properties object as returned by the method <a href="#getEventProperties"><code>Timeline.getEventProperties(event)</code></a>.
</td>
</tr>
<tr>
<td>doubleClick</td>
<td>Fired when double clicked inside the Timeline.
</td>
<td>
Passes a properties object as returned by the method <a href="#getEventProperties"><code>Timeline.getEventProperties(event)</code></a>.
</td>
</tr>
<tr>
<td>finishedRedraw</td>
<td>Fired after a redraw is complete. When moving the timeline around, this could be fired frequently.
</td>
<td>
none.
</td>
</tr>
<tr>
<td>rangechange</td>
<td>Fired repeatedly when the timeline window is being changed.
</td>

+ 6
- 6
lib/timeline/Core.js View File

@ -27,7 +27,7 @@ Emitter(Core.prototype);
* top, bottom, content, and background panel.
* @param {Element} container The container element where the Core will
* be attached.
* @private
* @protected
*/
Core.prototype._create = function (container) {
this.dom = {};
@ -55,7 +55,7 @@ Core.prototype._create = function (container) {
this.dom.background.className = 'vispanel background';
this.dom.backgroundVertical.className = 'vispanel background vertical';
this.dom.backgroundHorizontal.className = 'vispanel background horizontal';
this.dom.centerContainer.className = 'vispanel center';
this.dom.centerContainer.className = 'vispanel center jooo';
this.dom.leftContainer.className = 'vispanel left';
this.dom.rightContainer.className = 'vispanel right';
this.dom.top.className = 'vispanel top';
@ -787,7 +787,7 @@ Core.prototype.getCurrentTime = function() {
* Convert a position on screen (pixels) to a datetime
* @param {int} x Position on the screen in pixels
* @return {Date} time The datetime the corresponds with given position x
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toTime = function(x) {
@ -798,7 +798,7 @@ Core.prototype._toTime = function(x) {
* Convert a position on the global screen (pixels) to a datetime
* @param {int} x Position on the screen in pixels
* @return {Date} time The datetime the corresponds with given position x
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toGlobalTime = function(x) {
@ -812,7 +812,7 @@ Core.prototype._toGlobalTime = function(x) {
* @param {Date} time A date
* @return {int} x The position on the screen in pixels which corresponds
* with the given date.
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toScreen = function(time) {
@ -827,7 +827,7 @@ Core.prototype._toScreen = function(time) {
* @param {Date} time A date
* @return {int} x The position on root in pixels which corresponds
* with the given date.
* @private
* @protected
*/
// TODO: move this function to Range
Core.prototype._toGlobalScreen = function(time) {

+ 53
- 0
lib/timeline/Timeline.js View File

@ -102,6 +102,16 @@ function Timeline (container, items, groups, options) {
this.itemsData = null; // DataSet
this.groupsData = null; // DataSet
this.on('tap', function (event) {
me.emit('click', me.getEventProperties(event))
});
this.on('doubletap', function (event) {
me.emit('doubleClick', me.getEventProperties(event))
});
this.dom.root.oncontextmenu = function (event) {
me.emit('contextmenu', me.getEventProperties(event))
};
// apply options
if (options) {
this.setOptions(options);
@ -326,5 +336,48 @@ Timeline.prototype.getItemRange = function() {
};
};
/**
* Generate Timeline related information from an event
* @param {Event} event
* @return {Object} An object with related information, like on which area
* The event happened, whether clicked on an item, etc.
*/
Timeline.prototype.getEventProperties = function (event) {
var item = this.itemSet.itemFromTarget(event);
var group = this.itemSet.groupFromTarget(event);
var pageX = event.gesture ? event.gesture.center.pageX : event.pageX;
var pageY = event.gesture ? event.gesture.center.pageY : event.pageY;
var x = pageX - util.getAbsoluteLeft(this.dom.centerContainer);
var y = pageY - util.getAbsoluteTop(this.dom.centerContainer);
var snap = this.itemSet.options.snap || null;
var scale = this.body.util.getScale();
var step = this.body.util.getStep();
var time = this._toTime(x);
var snappedTime = snap ? snap(time, scale, step) : time;
var element = util.getTarget(event);
var what = null;
if (item != null) {what = 'item';}
else if (util.hasParent(element, this.dom.center)) {what = 'background';}
else if (util.hasParent(element, this.timeAxis.dom.foreground)) {what = 'axis';}
else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {what = 'axis';}
else if (util.hasParent(element, this.itemSet.dom.labelSet)) {what = 'group-label';}
else if (util.hasParent(element, this.customTime.bar)) {what = 'custom-time';}
else if (util.hasParent(element, this.currentTime.bar)) {what = 'current-time';}
return {
event: event,
item: item ? item.id : null,
group: group ? group.groupId : null,
what: what,
pageX: pageX,
pageY: pageY,
x: x,
y: y,
time: time,
snappedTime: snappedTime
}
};
module.exports = Timeline;

+ 6
- 16
lib/timeline/component/ItemSet.js View File

@ -1082,7 +1082,7 @@ ItemSet.prototype._constructByEndArray = function(array) {
*/
ItemSet.prototype._onTouch = function (event) {
// store the touched item, used in _onDragStart
this.touchParams.item = ItemSet.itemFromTarget(event);
this.touchParams.item = this.itemFromTarget(event);
};
/**
@ -1407,7 +1407,7 @@ ItemSet.prototype._onSelectItem = function (event) {
var oldSelection = this.getSelection();
var item = ItemSet.itemFromTarget(event);
var item = this.itemFromTarget(event);
var selection = item ? [item.id] : [];
this.setSelection(selection);
@ -1433,7 +1433,7 @@ ItemSet.prototype._onAddItem = function (event) {
var me = this,
snap = this.options.snap || null,
item = ItemSet.itemFromTarget(event);
item = this.itemFromTarget(event);
if (item) {
// update item
@ -1491,7 +1491,7 @@ ItemSet.prototype._onMultiSelectItem = function (event) {
if (!this.options.selectable) return;
var selection,
item = ItemSet.itemFromTarget(event);
item = this.itemFromTarget(event);
if (item) {
// multi select items
@ -1579,7 +1579,7 @@ ItemSet._getItemRange = function(itemsData) {
* @param {Event} event
* @return {Item | null} item
*/
ItemSet.itemFromTarget = function(event) {
ItemSet.prototype.itemFromTarget = function(event) {
var target = event.target;
while (target) {
if (target.hasOwnProperty('timeline-item')) {
@ -1598,17 +1598,7 @@ ItemSet.itemFromTarget = function(event) {
* @return {Group | null} group
*/
ItemSet.prototype.groupFromTarget = function(event) {
// TODO: cleanup when the new solution is stable (also on mobile)
//var target = event.target;
//while (target) {
// if (target.hasOwnProperty('timeline-group')) {
// return target['timeline-group'];
// }
// target = target.parentNode;
//}
//
var clientY = event.gesture.center.clientY;
var clientY = event.gesture ? event.gesture.center.clientY : event.clientY;
for (var i = 0; i < this.groupIds.length; i++) {
var groupId = this.groupIds[i];
var group = this.groups[groupId];

+ 18
- 0
lib/util.js View File

@ -665,6 +665,24 @@ exports.getTarget = function(event) {
return target;
};
/**
* Check if given element contains given parent somewhere in the DOM tree
* @param {Element} element
* @param {Element} parent
*/
exports.hasParent = function (element, parent) {
var e = element;
while (e) {
if (e === parent) {
return true;
}
e = e.parentNode;
}
return false;
};
exports.option = {};
/**

+ 10
- 2
test/timeline.html View File

@ -26,12 +26,12 @@
#visualization .grid.vertical.saturday,
#visualization .grid.vertical.sunday {
background: gray;
background: #ffef9f;
}
#visualization .text.saturday,
#visualization .text.sunday {
color: white;
color: #ff7c02;
}
</style>
@ -140,6 +140,14 @@
console.log('select', selection);
});
timeline.on('click', function (props) {
console.log('click', props);
});
timeline.on('contextmenu', function (props) {
console.log('contextmenu', props);
});
/*
timeline.on('rangechange', function (range) {
console.log('rangechange', range);

+ 12
- 0
test/timeline_groups.html View File

@ -165,6 +165,18 @@
});
*/
timeline.on('click', function (props) {
console.log('click', props);
});
timeline.on('doubleClick', function (props) {
console.log('doubleClick', props);
});
timeline.on('contextmenu', function (props) {
console.log('contextmenu', props);
});
items.on('add', console.log.bind(console));
items.on('update', console.log.bind(console));
items.on('remove', console.log.bind(console));

Loading…
Cancel
Save