Browse Source

Components are now controlled by their parents (GroupSet is currently broken)

css_transitions
josdejong 10 years ago
parent
commit
70e0723aa4
13 changed files with 344 additions and 317 deletions
  1. +43
    -16
      src/timeline/Timeline.js
  2. +4
    -14
      src/timeline/component/Component.js
  3. +51
    -49
      src/timeline/component/CurrentTime.js
  4. +38
    -56
      src/timeline/component/CustomTime.js
  5. +0
    -9
      src/timeline/component/Group.js
  6. +51
    -55
      src/timeline/component/GroupSet.js
  7. +47
    -41
      src/timeline/component/ItemSet.js
  8. +34
    -19
      src/timeline/component/Panel.js
  9. +45
    -29
      src/timeline/component/RootPanel.js
  10. +22
    -19
      src/timeline/component/TimeAxis.js
  11. +5
    -7
      src/timeline/component/css/groupset.css
  12. +3
    -2
      src/timeline/component/css/panel.css
  13. +1
    -1
      test/timeline.html

+ 43
- 16
src/timeline/Timeline.js View File

@ -81,13 +81,13 @@ function Timeline (container, items, options) {
// add item on doubletap // add item on doubletap
this.rootPanel.on('doubletap', this._onAddItem.bind(this)); this.rootPanel.on('doubletap', this._onAddItem.bind(this));
// label panel
var labelOptions = util.extend(Object.create(this.options), {
// side panel
var sideOptions = util.extend(Object.create(this.options), {
top: function () { top: function () {
return (labelOptions.orientation == 'top') ? '0' : '';
return (sideOptions.orientation == 'top') ? '0' : '';
}, },
bottom: function () { bottom: function () {
return (labelOptions.orientation == 'top') ? '' : '0';
return (sideOptions.orientation == 'top') ? '' : '0';
}, },
left: '0', left: '0',
right: null, right: null,
@ -100,10 +100,10 @@ function Timeline (container, items, options) {
return 0; return 0;
} }
}, },
className: 'labels'
className: 'side'
}); });
this.labelPanel = new Panel(labelOptions);
this.rootPanel.appendChild(this.labelPanel);
this.sidePanel = new Panel(sideOptions);
this.rootPanel.appendChild(this.sidePanel);
// main panel (contains time axis and itemsets) // main panel (contains time axis and itemsets)
var mainOptions = util.extend(Object.create(this.options), { var mainOptions = util.extend(Object.create(this.options), {
@ -115,12 +115,12 @@ function Timeline (container, items, options) {
}, },
left: function () { left: function () {
// we align left to enable a smooth resizing of the window // we align left to enable a smooth resizing of the window
return me.labelPanel.width;
return me.sidePanel.width;
}, },
right: null, right: null,
height: '100%', height: '100%',
width: function () { width: function () {
return me.rootPanel.width - me.labelPanel.width;
return me.rootPanel.width - me.sidePanel.width;
}, },
className: 'main' className: 'main'
}); });
@ -174,13 +174,14 @@ function Timeline (container, items, options) {
this.contentPanel = new Panel(contentOptions); this.contentPanel = new Panel(contentOptions);
this.mainPanel.appendChild(this.contentPanel); this.mainPanel.appendChild(this.contentPanel);
// current time bar // current time bar
// Note: time bar will be attached in this.setOptions when selected
this.currentTime = new CurrentTime(this.range, rootOptions); this.currentTime = new CurrentTime(this.range, rootOptions);
this.mainPanel.appendChild(this.currentTime);
// custom time bar // custom time bar
// Note: time bar will be attached in this.setOptions when selected
this.customTime = new CustomTime(rootOptions); this.customTime = new CustomTime(rootOptions);
this.mainPanel.appendChild(this.customTime);
this.customTime.on('timechange', function (time) { this.customTime.on('timechange', function (time) {
me.emit('timechange', time); me.emit('timechange', time);
}); });
@ -241,6 +242,33 @@ Timeline.prototype.setOptions = function (options) {
}).bind(this); }).bind(this);
['onAdd', 'onUpdate', 'onRemove', 'onMove'].forEach(validateCallback); ['onAdd', 'onUpdate', 'onRemove', 'onMove'].forEach(validateCallback);
// add/remove the current time bar
if (this.options.showCurrentTime) {
if (!this.mainPanel.hasChild(this.currentTime)) {
this.mainPanel.appendChild(this.currentTime);
this.currentTime.start();
}
}
else {
if (this.mainPanel.hasChild(this.currentTime)) {
this.currentTime.stop();
this.mainPanel.removeChild(this.currentTime);
}
}
// add/remove the custom time bar
if (this.options.showCustomTime) {
if (!this.mainPanel.hasChild(this.customTime)) {
this.mainPanel.appendChild(this.customTime);
}
}
else {
if (this.mainPanel.hasChild(this.customTime)) {
this.mainPanel.removeChild(this.customTime);
}
}
// repaint everything
this.rootPanel.repaint(); this.rootPanel.repaint();
}; };
@ -352,24 +380,23 @@ Timeline.prototype.setGroups = function(groups) {
if (this.itemSet) { if (this.itemSet) {
this.itemSet.hide(); // TODO: not so nice having to hide here this.itemSet.hide(); // TODO: not so nice having to hide here
this.contentPanel.removeChild(this.itemSet); this.contentPanel.removeChild(this.itemSet);
this.itemSet.setItems(); // disconnect from items
this.itemSet.setItems(); // disconnect from itemset
this.itemSet = null; this.itemSet = null;
} }
// create new GroupSet // create new GroupSet
this.groupSet = new GroupSet(this.labelPanel, options);
// TODO: replace this.sidePanel with a this.labelPanel
this.groupSet = new GroupSet(this.contentPanel, this.sidePanel, options);
this.groupSet.on('change', this.rootPanel.repaint.bind(this.rootPanel, 'changes')); this.groupSet.on('change', this.rootPanel.repaint.bind(this.rootPanel, 'changes'));
this.groupSet.setRange(this.range); this.groupSet.setRange(this.range);
this.groupSet.setItems(this.itemsData); this.groupSet.setItems(this.itemsData);
this.groupSet.setGroups(this.groupsData); this.groupSet.setGroups(this.groupsData);
this.contentPanel.appendChild(this.groupSet);
} }
else { else {
// ItemSet // ItemSet
if (this.groupSet) { if (this.groupSet) {
this.groupSet.hide(); // TODO: not so nice having to hide here this.groupSet.hide(); // TODO: not so nice having to hide here
this.contentPanel.removeChild(this.groupSet);
this.groupSet.setItems(); // disconnect from items
this.groupSet.setItems(); // disconnect from itemset
this.groupSet = null; this.groupSet = null;
} }

+ 4
- 14
src/timeline/component/Component.js View File

@ -7,7 +7,6 @@ function Component () {
this.childs = null; this.childs = null;
this.options = null; this.options = null;
this.frame = null; // main DOM element
this.top = 0; this.top = 0;
this.left = 0; this.left = 0;
this.width = 0; this.width = 0;
@ -53,24 +52,13 @@ Component.prototype.getOption = function getOption(name) {
return value; return value;
}; };
/**
* Get the container element of the component, which can be used by a child to
* add its own widgets. Not all components do have a container for childs, in
* that case null is returned.
* @returns {HTMLElement | null} container
*/
// TODO: get rid of the getContainer and getFrame methods, provide these via the options
Component.prototype.getContainer = function getContainer() {
// should be implemented by the component
return null;
};
/** /**
* Get the frame element of the component, the outer HTML DOM element. * Get the frame element of the component, the outer HTML DOM element.
* @returns {HTMLElement | null} frame * @returns {HTMLElement | null} frame
*/ */
Component.prototype.getFrame = function getFrame() { Component.prototype.getFrame = function getFrame() {
return this.frame;
// should be implemented by the component
return null;
}; };
/** /**
@ -86,6 +74,7 @@ Component.prototype.repaint = function repaint() {
* Hide the component from the DOM * Hide the component from the DOM
* @return {Boolean} changed * @return {Boolean} changed
*/ */
// TODO: remove hide and show
Component.prototype.hide = function hide() { Component.prototype.hide = function hide() {
if (this.frame && this.frame.parentNode) { if (this.frame && this.frame.parentNode) {
this.frame.parentNode.removeChild(this.frame); this.frame.parentNode.removeChild(this.frame);
@ -101,6 +90,7 @@ Component.prototype.hide = function hide() {
* A repaint will be executed when the component is not visible * A repaint will be executed when the component is not visible
* @return {Boolean} changed * @return {Boolean} changed
*/ */
// TODO: remove hide and show
Component.prototype.show = function show() { Component.prototype.show = function show() {
if (!this.frame || !this.frame.parentNode) { if (!this.frame || !this.frame.parentNode) {
return this.repaint(); return this.repaint();

+ 51
- 49
src/timeline/component/CurrentTime.js View File

@ -15,6 +15,8 @@ function CurrentTime (range, options) {
this.defaultOptions = { this.defaultOptions = {
showCurrentTime: false showCurrentTime: false
}; };
this._create();
} }
CurrentTime.prototype = new Component(); CurrentTime.prototype = new Component();
@ -22,73 +24,73 @@ CurrentTime.prototype = new Component();
CurrentTime.prototype.setOptions = Component.prototype.setOptions; CurrentTime.prototype.setOptions = Component.prototype.setOptions;
/** /**
* Get the container element of the bar, which can be used by a child to
* add its own widgets.
* @returns {HTMLElement} container
* Create the HTML DOM for the current time bar
* @private
*/ */
CurrentTime.prototype.getContainer = function () {
return this.frame;
CurrentTime.prototype._create = function _create () {
var bar = document.createElement('div');
bar.className = 'currenttime';
bar.style.position = 'absolute';
bar.style.top = '0px';
bar.style.height = '100%';
this.bar = bar;
};
/**
* Get the frame element of the current time bar
* @returns {HTMLElement} frame
*/
CurrentTime.prototype.getFrame = function getFrame() {
return this.bar;
}; };
/** /**
* Repaint the component * Repaint the component
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
*/ */
CurrentTime.prototype.repaint = function () {
var bar = this.frame,
parent = this.parent;
CurrentTime.prototype.repaint = function repaint() {
var parent = this.parent;
if (!parent) {
throw new Error('Cannot repaint bar: no parent attached');
}
var now = new Date();
var x = this.options.toScreen(now);
var parentContainer = parent.getContainer();
if (!parentContainer) {
throw new Error('Cannot repaint bar: parent has no container element');
}
this.bar.style.left = x + 'px';
this.bar.title = 'Current time: ' + now;
if (!this.getOption('showCurrentTime')) {
if (bar) {
parentContainer.removeChild(bar);
delete this.frame;
}
return false;
};
return false;
}
/**
* Start auto refreshing the current time bar
*/
CurrentTime.prototype.start = function start() {
var me = this;
if (!bar) {
bar = document.createElement('div');
bar.className = 'currenttime';
bar.style.position = 'absolute';
bar.style.top = '0px';
bar.style.height = '100%';
function update () {
me.stop();
parentContainer.appendChild(bar);
this.frame = bar;
}
// determine interval to refresh
var scale = me.range.conversion(parent.width).scale;
var interval = 1 / scale / 2;
if (interval < 30) interval = 30;
if (interval > 1000) interval = 1000;
var now = new Date();
var x = this.options.toScreen(now);
me.repaint();
bar.style.left = x + 'px';
bar.title = 'Current time: ' + now;
// start a timer to adjust for the new time
me.currentTimeTimer = setTimeout(update, interval);
}
update();
};
// start a timer to adjust for the new time
/**
* Stop auto refreshing the current time bar
*/
CurrentTime.prototype.stop = function stop() {
if (this.currentTimeTimer !== undefined) { if (this.currentTimeTimer !== undefined) {
clearTimeout(this.currentTimeTimer); clearTimeout(this.currentTimeTimer);
delete this.currentTimeTimer; delete this.currentTimeTimer;
} }
// determine interval to refresh
var timeline = this;
var scale = this.range.conversion(parent.width).scale;
var interval = 1 / scale / 2;
if (interval < 30) interval = 30;
if (interval > 1000) interval = 1000;
this.currentTimeTimer = setTimeout(function() {
timeline.repaint();
}, interval);
return false;
}; };

+ 38
- 56
src/timeline/component/CustomTime.js View File

@ -16,6 +16,9 @@ function CustomTime (options) {
this.customTime = new Date(); this.customTime = new Date();
this.eventParams = {}; // stores state parameters while dragging the bar this.eventParams = {}; // stores state parameters while dragging the bar
// create the DOM
this._create();
} }
CustomTime.prototype = new Component(); CustomTime.prototype = new Component();
@ -23,12 +26,40 @@ CustomTime.prototype = new Component();
CustomTime.prototype.setOptions = Component.prototype.setOptions; CustomTime.prototype.setOptions = Component.prototype.setOptions;
/** /**
* Get the container element of the bar, which can be used by a child to
* add its own widgets.
* @returns {HTMLElement} container
* Create the DOM for the custom time
* @private
*/
CustomTime.prototype._create = function _create () {
var bar = document.createElement('div');
bar.className = 'customtime';
bar.style.position = 'absolute';
bar.style.top = '0px';
bar.style.height = '100%';
this.bar = bar;
var drag = document.createElement('div');
drag.style.position = 'relative';
drag.style.top = '0px';
drag.style.left = '-10px';
drag.style.height = '100%';
drag.style.width = '20px';
bar.appendChild(drag);
// attach event listeners
this.hammer = Hammer(bar, {
prevent_default: true
});
this.hammer.on('dragstart', this._onDragStart.bind(this));
this.hammer.on('drag', this._onDrag.bind(this));
this.hammer.on('dragend', this._onDragEnd.bind(this));
};
/**
* Get the frame element of the custom time bar
* @returns {HTMLElement} frame
*/ */
CustomTime.prototype.getContainer = function () {
return this.frame;
CustomTime.prototype.getFrame = function getFrame() {
return this.bar;
}; };
/** /**
@ -36,59 +67,10 @@ CustomTime.prototype.getContainer = function () {
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
*/ */
CustomTime.prototype.repaint = function () { CustomTime.prototype.repaint = function () {
var bar = this.frame,
parent = this.parent;
if (!parent) {
throw new Error('Cannot repaint bar: no parent attached');
}
var parentContainer = parent.getContainer();
if (!parentContainer) {
throw new Error('Cannot repaint bar: parent has no container element');
}
if (!this.getOption('showCustomTime')) {
if (bar) {
parentContainer.removeChild(bar);
delete this.frame;
}
return false;
}
if (!bar) {
bar = document.createElement('div');
bar.className = 'customtime';
bar.style.position = 'absolute';
bar.style.top = '0px';
bar.style.height = '100%';
parentContainer.appendChild(bar);
var drag = document.createElement('div');
drag.style.position = 'relative';
drag.style.top = '0px';
drag.style.left = '-10px';
drag.style.height = '100%';
drag.style.width = '20px';
bar.appendChild(drag);
this.frame = bar;
// attach event listeners
this.hammer = Hammer(bar, {
prevent_default: true
});
this.hammer.on('dragstart', this._onDragStart.bind(this));
this.hammer.on('drag', this._onDrag.bind(this));
this.hammer.on('dragend', this._onDragEnd.bind(this));
}
var x = this.options.toScreen(this.customTime); var x = this.options.toScreen(this.customTime);
bar.style.left = x + 'px';
bar.title = 'Time: ' + this.customTime;
this.bar.style.left = x + 'px';
this.bar.title = 'Time: ' + this.customTime;
return false; return false;
}; };

+ 0
- 9
src/timeline/component/Group.js View File

@ -35,15 +35,6 @@ Group.prototype = new Component();
// TODO: comment // TODO: comment
Group.prototype.setOptions = Component.prototype.setOptions; Group.prototype.setOptions = Component.prototype.setOptions;
/**
* Get the container element of the panel, which can be used by a child to
* add its own widgets.
* @returns {HTMLElement} container
*/
Group.prototype.getContainer = function () {
return null;
};
/** /**
* Set item set for the group. The group will create a view on the itemSet, * Set item set for the group. The group will create a view on the itemSet,
* filtered by the groups id. * filtered by the groups id.

+ 51
- 55
src/timeline/component/GroupSet.js View File

@ -1,14 +1,16 @@
/** /**
* An GroupSet holds a set of groups * An GroupSet holds a set of groups
* @param {Panel} labelPanel
* @param {Panel} contentPanel Panel where the ItemSets will be created
* @param {Panel} labelPanel Panel where the labels will be created
* @param {Object} [options] See GroupSet.setOptions for the available * @param {Object} [options] See GroupSet.setOptions for the available
* options. * options.
* @constructor GroupSet * @constructor GroupSet
* @extends Panel * @extends Panel
*/ */
function GroupSet(labelPanel, options) {
function GroupSet(contentPanel, labelPanel, options) {
this.id = util.randomUUID(); this.id = util.randomUUID();
this.contentPanel = contentPanel;
this.labelPanel = labelPanel; this.labelPanel = labelPanel;
this.options = options || {}; this.options = options || {};
@ -43,10 +45,37 @@ function GroupSet(labelPanel, options) {
me._onRemove(params.items); me._onRemove(params.items);
} }
}; };
// create HTML DOM
this._create();
} }
GroupSet.prototype = new Panel(); GroupSet.prototype = new Panel();
/**
* Create the HTML DOM elements for the GroupSet
* @private
*/
GroupSet.prototype._create = function _create () {
// TODO: delete _create, GroupSet must not have DOM elements itself
var groupSet = document.createElement('div');
groupSet.className = 'groupset';
groupSet['timeline-groupset'] = this;
this.dom.groupSet = groupSet;
var labelSet = document.createElement('div');
labelSet.className = 'labelset';
this.dom.labelSet = labelSet;
};
/**
* Get the frame element of component
* @returns {null} Get frame is not supported by GroupSet
*/
GroupSet.prototype.getFrame = function getFrame() {
throw new Error('GroupSet is a virtual Component and doesn\'t have a frame.');
};
/** /**
* Set options for the GroupSet. Existing options will be extended/overwritten. * Set options for the GroupSet. Existing options will be extended/overwritten.
* @param {Object} [options] The following options are available: * @param {Object} [options] The following options are available:
@ -211,38 +240,9 @@ GroupSet.prototype.repaint = function repaint() {
asString = util.option.asString, asString = util.option.asString,
options = this.options, options = this.options,
orientation = this.getOption('orientation'), orientation = this.getOption('orientation'),
frame = this.dom.frame,
labels = this.dom.labels,
labelSet = this.dom.labelSet;
// create frame
if (!frame) {
frame = document.createElement('div');
frame.className = 'groupset';
frame['timeline-groupset'] = this;
this.dom.frame = frame;
if (!this.parent) throw new Error('Cannot repaint GroupSet: no parent attached');
var parentContainer = this.parent.getContainer();
if (!parentContainer) throw new Error('Cannot repaint GroupSet: parent has no container element');
parentContainer.appendChild(frame);
// create labels
var labelContainer = this.labelPanel.getContainer();
if (!labelContainer) throw new Error('Cannot repaint groupset: option "labelContainer" not defined');
labels = document.createElement('div');
labels.className = 'labels';
labelContainer.appendChild(labels);
this.dom.labels = labels;
labelSet = document.createElement('div');
labelSet.className = 'label-set';
labels.appendChild(labelSet);
this.dom.labelSet = labelSet;
}
var me = this,
groupSet = this.dom.groupSet,
labelSet = this.dom.labelSet,
me = this,
queue = this.queue, queue = this.queue,
groups = this.groups, groups = this.groups,
groupsData = this.groupsData; groupsData = this.groupsData;
@ -266,7 +266,7 @@ GroupSet.prototype.repaint = function repaint() {
height: null height: null
}); });
group = new Group(me, me.labelPanel, id, groupOptions);
group = new Group(me.contentPanel, me.labelPanel, id, groupOptions);
group.on('change', me.emit.bind(me, 'change')); // propagate change event group.on('change', me.emit.bind(me, 'change')); // propagate change event
group.setRange(me.range); group.setRange(me.range);
group.setItems(me.itemsData); // attach items data group.setItems(me.itemsData); // attach items data
@ -299,6 +299,7 @@ GroupSet.prototype.repaint = function repaint() {
order: this.options.groupOrder order: this.options.groupOrder
}); });
/* TODO
// (re)create the labels // (re)create the labels
while (labelSet.firstChild) { while (labelSet.firstChild) {
labelSet.removeChild(labelSet.firstChild); labelSet.removeChild(labelSet.firstChild);
@ -308,6 +309,7 @@ GroupSet.prototype.repaint = function repaint() {
label = this._createLabel(id); label = this._createLabel(id);
labelSet.appendChild(label); labelSet.appendChild(label);
} }
*/
} }
// repaint all groups in order // repaint all groups in order
@ -350,25 +352,27 @@ GroupSet.prototype.repaint = function repaint() {
} }
// update classname // update classname
frame.className = 'groupset' + (options.className ? (' ' + asString(options.className)) : '');
groupSet.className = 'groupset' + (options.className ? (' ' + asString(options.className)) : '');
// reposition frame
frame.style.top = asSize((orientation == 'top') ? '0' : '');
frame.style.bottom = asSize((orientation == 'top') ? '' : '0');
frame.style.left = asSize(options.left, '');
frame.style.right = asSize(options.right, '');
frame.style.width = asSize(options.width, '100%');
frame.style.height = asSize(height);
// reposition groupSet frame
groupSet.style.top = asSize((orientation == 'top') ? '0' : '');
groupSet.style.bottom = asSize((orientation == 'top') ? '' : '0');
groupSet.style.left = asSize(options.left, '');
groupSet.style.right = asSize(options.right, '');
groupSet.style.width = asSize(options.width, '100%');
groupSet.style.height = asSize(height);
// calculate actual size and position // calculate actual size and position
this.top = frame.offsetTop;
this.left = frame.offsetLeft;
this.width = frame.offsetWidth;
this.top = groupSet.offsetTop;
this.left = groupSet.offsetLeft;
this.width = groupSet.offsetWidth;
this.height = height; this.height = height;
/* TODO
// reposition labels // reposition labels
labelSet.style.top = asSize(options.top, '0'); labelSet.style.top = asSize(options.top, '0');
labelSet.style.height = fixedHeight ? asSize(options.height) : this.height + 'px'; labelSet.style.height = fixedHeight ? asSize(options.height) : this.height + 'px';
*/
}; };
/** /**
@ -403,19 +407,11 @@ GroupSet.prototype._createLabel = function(id) {
return label; return label;
}; };
/**
* Get container element
* @return {HTMLElement} container
*/
GroupSet.prototype.getContainer = function getContainer() {
return this.dom.frame;
};
/** /**
* Get the width of the group labels * Get the width of the group labels
* @return {Number} width * @return {Number} width
*/ */
GroupSet.prototype.getLabelsWidth = function getContainer() {
GroupSet.prototype.getLabelsWidth = function getLabelsWidth() {
return this.props.labels.width; return this.props.labels.width;
}; };

+ 47
- 41
src/timeline/component/ItemSet.js View File

@ -50,6 +50,9 @@ function ItemSet(options) {
this.touchParams = {}; // stores properties while dragging this.touchParams = {}; // stores properties while dragging
// TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
// create the HTML DOM
this._create();
} }
ItemSet.prototype = new Panel(); ItemSet.prototype = new Panel();
@ -62,6 +65,42 @@ ItemSet.types = {
point: ItemPoint point: ItemPoint
}; };
/**
* Create the HTML DOM for the ItemSet
*/
ItemSet.prototype._create = function _create(){
var frame = document.createElement('div');
frame['timeline-itemset'] = this;
this.frame = frame;
// create background panel
var background = document.createElement('div');
background.className = 'background';
frame.appendChild(background);
this.dom.background = background;
// create foreground panel
var foreground = document.createElement('div');
foreground.className = 'foreground';
frame.appendChild(foreground);
this.dom.foreground = foreground;
// create axis panel
var axis = document.createElement('div');
axis.className = 'axis';
this.dom.axis = axis;
frame.appendChild(axis);
// attach event listeners
// TODO: use event listeners from the rootpanel to improve performance
this.hammer = Hammer(frame, {
prevent_default: true
});
this.hammer.on('dragstart', this._onDragStart.bind(this));
this.hammer.on('drag', this._onDrag.bind(this));
this.hammer.on('dragend', this._onDragEnd.bind(this));
};
/** /**
* Set options for the ItemSet. Existing options will be extended/overwritten. * Set options for the ItemSet. Existing options will be extended/overwritten.
* @param {Object} [options] The following options are available: * @param {Object} [options] The following options are available:
@ -162,6 +201,14 @@ ItemSet.prototype._deselect = function _deselect(id) {
} }
}; };
/**
* Return the item sets frame
* @returns {HTMLElement} frame
*/
ItemSet.prototype.getFrame = function getFrame() {
return this.frame;
};
/** /**
* Repaint the component * Repaint the component
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
@ -173,47 +220,6 @@ ItemSet.prototype.repaint = function repaint() {
orientation = this.getOption('orientation'), orientation = this.getOption('orientation'),
frame = this.frame; frame = this.frame;
if (!frame) {
frame = document.createElement('div');
frame['timeline-itemset'] = this;
this.frame = frame;
// create background panel
var background = document.createElement('div');
background.className = 'background';
frame.appendChild(background);
this.dom.background = background;
// create foreground panel
var foreground = document.createElement('div');
foreground.className = 'foreground';
frame.appendChild(foreground);
this.dom.foreground = foreground;
// create axis panel
var axis = document.createElement('div');
axis.className = 'axis';
this.dom.axis = axis;
frame.appendChild(axis);
// attach event listeners
// TODO: use event listeners from the rootpanel to improve performance
this.hammer = Hammer(frame, {
prevent_default: true
});
this.hammer.on('dragstart', this._onDragStart.bind(this));
this.hammer.on('drag', this._onDrag.bind(this));
this.hammer.on('dragend', this._onDragEnd.bind(this));
}
if (!frame.parentNode) {
if (!this.parent) throw new Error('Cannot repaint itemset: no parent attached');
var parentContainer = this.parent.getContainer();
if (!parentContainer) throw new Error('Cannot repaint itemset: parent has no container element');
parentContainer.appendChild(frame);
}
// update className // update className
frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : ''); frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : '');

+ 34
- 19
src/timeline/component/Panel.js View File

@ -11,7 +11,7 @@
*/ */
function Panel(options) { function Panel(options) {
this.id = util.randomUUID(); this.id = util.randomUUID();
this.parent = null;
this.parent = null; // TODO: remove parent?
this.childs = []; this.childs = [];
this.options = options || {}; this.options = options || {};
@ -31,12 +31,19 @@ Panel.prototype = new Component();
Panel.prototype.setOptions = Component.prototype.setOptions; Panel.prototype.setOptions = Component.prototype.setOptions;
/** /**
* Get the container element of the panel, which can be used by a child to
* add its own widgets.
* @returns {HTMLElement} container
* Get the outer frame of the panel
* @returns {HTMLElement} frame
*/ */
Panel.prototype.getContainer = function () {
return this.frame;
Panel.prototype.getFrame = function () {
var frame = this.frame;
// create frame
if (!frame) {
frame = document.createElement('div');
this.frame = frame;
}
return frame;
}; };
/** /**
@ -46,6 +53,9 @@ Panel.prototype.getContainer = function () {
Panel.prototype.appendChild = function (child) { Panel.prototype.appendChild = function (child) {
this.childs.push(child); this.childs.push(child);
child.parent = this; child.parent = this;
// attach to the DOM
this.frame.appendChild(child.getFrame());
}; };
/** /**
@ -69,10 +79,27 @@ Panel.prototype.removeChild = function (child) {
var index = this.childs.indexOf(child); var index = this.childs.indexOf(child);
if (index != -1) { if (index != -1) {
this.childs.splice(index, 1); this.childs.splice(index, 1);
if (!child.getFrame() || child.getFrame().parentNode != this.frame) {
console.log('oops')
}
// remove from the DOM
this.frame.removeChild(child.getFrame());
child.parent = null; child.parent = null;
} }
}; };
/**
* Test whether the panel contains given child
* @param {Component} child
*/
Panel.prototype.hasChild = function (child) {
var index = this.childs.indexOf(child);
return (index != -1);
};
/** /**
* Repaint the component * Repaint the component
* @return {boolean} Returns true if the component was resized since previous repaint * @return {boolean} Returns true if the component was resized since previous repaint
@ -80,19 +107,7 @@ Panel.prototype.removeChild = function (child) {
Panel.prototype.repaint = function () { Panel.prototype.repaint = function () {
var asString = util.option.asString, var asString = util.option.asString,
options = this.options, options = this.options,
frame = this.frame;
// create frame
if (!frame) {
frame = document.createElement('div');
this.frame = frame;
if (!this.parent) throw new Error('Cannot repaint panel: no parent attached');
var parentContainer = this.parent.getContainer();
if (!parentContainer) throw new Error('Cannot repaint panel: parent has no container element');
parentContainer.appendChild(frame);
}
frame = this.getFrame();
// update className // update className
frame.className = 'vpanel' + (options.className ? (' ' + asString(options.className)) : ''); frame.className = 'vpanel' + (options.className ? (' ' + asString(options.className)) : '');

+ 45
- 29
src/timeline/component/RootPanel.js View File

@ -15,11 +15,49 @@ function RootPanel(container, options) {
autoResize: true autoResize: true
}; };
// create the HTML DOM
this._create();
// attach the root panel to the provided container
if (!this.container) throw new Error('Cannot repaint root panel: no container attached');
this.container.appendChild(this.getFrame());
this._initWatch(); this._initWatch();
} }
RootPanel.prototype = new Panel(); RootPanel.prototype = new Panel();
/**
* Create the HTML DOM for the root panel
*/
RootPanel.prototype._create = function _create() {
// create frame
this.frame = document.createElement('div');
// create event listeners for all interesting events, these events will be
// emitted via emitter
this.hammer = Hammer(this.frame, {
prevent_default: true
});
this.listeners = {};
var me = this;
var events = [
'touch', 'pinch', 'tap', 'doubletap', 'hold',
'dragstart', 'drag', 'dragend',
'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is for Firefox
];
events.forEach(function (event) {
var listener = function () {
var args = [event].concat(Array.prototype.slice.call(arguments, 0));
me.emit.apply(me, args);
};
me.hammer.on(event, listener);
me.listeners[event] = listener;
});
};
/** /**
* Set options. Will extend the current options. * Set options. Will extend the current options.
* @param {Object} [options] Available parameters: * @param {Object} [options] Available parameters:
@ -40,39 +78,17 @@ RootPanel.prototype.setOptions = function setOptions(options) {
} }
}; };
/**
* Get the frame of the root panel
*/
RootPanel.prototype.getFrame = function getFrame() {
return this.frame;
};
/** /**
* Repaint the root panel * Repaint the root panel
*/ */
RootPanel.prototype.repaint = function repaint() { RootPanel.prototype.repaint = function repaint() {
// create frame
if (!this.frame) {
if (!this.container) throw new Error('Cannot repaint root panel: no container attached');
this.frame = document.createElement('div');
this.container.appendChild(this.frame);
// create event listeners for all interesting events, these events will be
// emitted via emitter
this.hammer = Hammer(this.frame, {
prevent_default: true
});
this.listeners = {};
var me = this;
var events = [
'touch', 'pinch', 'tap', 'doubletap', 'hold',
'dragstart', 'drag', 'dragend',
'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is for Firefox
];
events.forEach(function (event) {
var listener = function () {
var args = [event].concat(Array.prototype.slice.call(arguments, 0));
me.emit.apply(me, args);
};
me.hammer.on(event, listener);
me.listeners[event] = listener;
});
}
// update class name // update class name
var options = this.options; var options = this.options;
var className = 'vis timeline rootpanel ' + options.orientation + (options.editable ? ' editable' : ''); var className = 'vis timeline rootpanel ' + options.orientation + (options.editable ? ' editable' : '');

+ 22
- 19
src/timeline/component/TimeAxis.js View File

@ -38,6 +38,9 @@ function TimeAxis (options) {
}; };
this.range = null; this.range = null;
// create the HTML DOM
this._create();
} }
TimeAxis.prototype = new Component(); TimeAxis.prototype = new Component();
@ -45,6 +48,13 @@ TimeAxis.prototype = new Component();
// TODO: comment options // TODO: comment options
TimeAxis.prototype.setOptions = Component.prototype.setOptions; TimeAxis.prototype.setOptions = Component.prototype.setOptions;
/**
* Create the HTML DOM for the TimeAxis
*/
TimeAxis.prototype._create = function _create() {
this.frame = document.createElement('div');
};
/** /**
* Set a range (start and end) * Set a range (start and end)
* @param {Range | Object} range A Range or an object containing start and end. * @param {Range | Object} range A Range or an object containing start and end.
@ -57,6 +67,14 @@ TimeAxis.prototype.setRange = function (range) {
this.range = range; this.range = range;
}; };
/**
* Get the outer frame of the time axis
* @return {HTMLElement} frame
*/
TimeAxis.prototype.getFrame = function getFrame() {
return this.frame;
};
/** /**
* Repaint the component * Repaint the component
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
@ -64,26 +82,11 @@ TimeAxis.prototype.setRange = function (range) {
TimeAxis.prototype.repaint = function () { TimeAxis.prototype.repaint = function () {
var asSize = util.option.asSize, var asSize = util.option.asSize,
options = this.options, options = this.options,
props = this.props;
var frame = this.frame;
if (!frame) {
frame = document.createElement('div');
this.frame = frame;
}
frame.className = 'axis';
// TODO: custom className?
props = this.props,
frame = this.frame;
if (!frame.parentNode) {
if (!this.parent) {
throw new Error('Cannot repaint time axis: no parent attached');
}
var parentContainer = this.parent.getContainer();
if (!parentContainer) {
throw new Error('Cannot repaint time axis: parent has no container element');
}
parentContainer.appendChild(frame);
}
// update classname
frame.className = 'axis'; // TODO: add className from options if defined
var parent = frame.parentNode; var parent = frame.parentNode;
if (parent) { if (parent) {

+ 5
- 7
src/timeline/component/css/groupset.css View File

@ -14,11 +14,9 @@
padding: 0; padding: 0;
margin: 0; margin: 0;
border-right: 1px solid #bfbfbf;
} }
.vis.timeline .labels .label-set {
.vis.timeline .labels .labelset {
position: relative; position: relative;
top: 0; top: 0;
left: 0; left: 0;
@ -33,7 +31,7 @@
box-sizing: border-box; box-sizing: border-box;
} }
.vis.timeline .labels .label-set .vlabel {
.vis.timeline .labels .labelset .vlabel {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
@ -44,19 +42,19 @@
box-sizing: border-box; box-sizing: border-box;
} }
.vis.timeline.top .labels .label-set .vlabel,
.vis.timeline.top .labels .labelset .vlabel,
.vis.timeline.top .groupset .itemset .axis { .vis.timeline.top .groupset .itemset .axis {
border-top: 1px solid #bfbfbf; border-top: 1px solid #bfbfbf;
border-bottom: none; border-bottom: none;
} }
.vis.timeline.bottom .labels .label-set .vlabel,
.vis.timeline.bottom .labels .labelset .vlabel,
.vis.timeline.bottom .groupset .itemset .axis { .vis.timeline.bottom .groupset .itemset .axis {
border-top: none; border-top: none;
border-bottom: 1px solid #bfbfbf; border-bottom: 1px solid #bfbfbf;
} }
.vis.timeline .labels .label-set .vlabel .inner {
.vis.timeline .labels .labelset .vlabel .inner {
display: inline-block; display: inline-block;
padding: 5px; padding: 5px;
} }

+ 3
- 2
src/timeline/component/css/panel.css View File

@ -15,11 +15,12 @@
.vis.timeline .vpanel { .vis.timeline .vpanel {
position: absolute; position: absolute;
overflow: hidden;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
} }
.vis.timeline .vpanel {
overflow: hidden;
.vis.timeline .vpanel.side {
border-right: 1px solid #bfbfbf;
} }

+ 1
- 1
test/timeline.html View File

@ -77,7 +77,7 @@
//end: moment('2013-12-31'), //end: moment('2013-12-31'),
//min: moment('2013-01-01'), //min: moment('2013-01-01'),
//max: moment('2013-12-31'), //max: moment('2013-12-31'),
zoomMin: 1000 * 60 * 60 * 24, // 1 day
//zoomMin: 1000 * 60 * 60 * 24, // 1 day
zoomMax: 1000 * 60 * 60 * 24 * 30 * 6 // 6 months zoomMax: 1000 * 60 * 60 * 24 * 30 * 6 // 6 months
}; };

Loading…
Cancel
Save