From 70e0723aa4299a1dfd11c42dd90e65b964beacf7 Mon Sep 17 00:00:00 2001 From: josdejong Date: Wed, 9 Apr 2014 19:19:05 +0200 Subject: [PATCH] Components are now controlled by their parents (GroupSet is currently broken) --- src/timeline/Timeline.js | 59 +++++++++---- src/timeline/component/Component.js | 18 +--- src/timeline/component/CurrentTime.js | 100 +++++++++++----------- src/timeline/component/CustomTime.js | 94 +++++++++------------ src/timeline/component/Group.js | 9 -- src/timeline/component/GroupSet.js | 106 ++++++++++++------------ src/timeline/component/ItemSet.js | 88 +++++++++++--------- src/timeline/component/Panel.js | 53 +++++++----- src/timeline/component/RootPanel.js | 74 ++++++++++------- src/timeline/component/TimeAxis.js | 41 ++++----- src/timeline/component/css/groupset.css | 12 ++- src/timeline/component/css/panel.css | 5 +- test/timeline.html | 2 +- 13 files changed, 344 insertions(+), 317 deletions(-) diff --git a/src/timeline/Timeline.js b/src/timeline/Timeline.js index 3d10a304..81adeb0f 100644 --- a/src/timeline/Timeline.js +++ b/src/timeline/Timeline.js @@ -81,13 +81,13 @@ function Timeline (container, items, options) { // add item on doubletap 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 () { - return (labelOptions.orientation == 'top') ? '0' : ''; + return (sideOptions.orientation == 'top') ? '0' : ''; }, bottom: function () { - return (labelOptions.orientation == 'top') ? '' : '0'; + return (sideOptions.orientation == 'top') ? '' : '0'; }, left: '0', right: null, @@ -100,10 +100,10 @@ function Timeline (container, items, options) { 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) var mainOptions = util.extend(Object.create(this.options), { @@ -115,12 +115,12 @@ function Timeline (container, items, options) { }, left: function () { // we align left to enable a smooth resizing of the window - return me.labelPanel.width; + return me.sidePanel.width; }, right: null, height: '100%', width: function () { - return me.rootPanel.width - me.labelPanel.width; + return me.rootPanel.width - me.sidePanel.width; }, className: 'main' }); @@ -174,13 +174,14 @@ function Timeline (container, items, options) { this.contentPanel = new Panel(contentOptions); this.mainPanel.appendChild(this.contentPanel); + // current time bar + // Note: time bar will be attached in this.setOptions when selected this.currentTime = new CurrentTime(this.range, rootOptions); - this.mainPanel.appendChild(this.currentTime); // custom time bar + // Note: time bar will be attached in this.setOptions when selected this.customTime = new CustomTime(rootOptions); - this.mainPanel.appendChild(this.customTime); this.customTime.on('timechange', function (time) { me.emit('timechange', time); }); @@ -241,6 +242,33 @@ Timeline.prototype.setOptions = function (options) { }).bind(this); ['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(); }; @@ -352,24 +380,23 @@ Timeline.prototype.setGroups = function(groups) { if (this.itemSet) { this.itemSet.hide(); // TODO: not so nice having to hide here this.contentPanel.removeChild(this.itemSet); - this.itemSet.setItems(); // disconnect from items + this.itemSet.setItems(); // disconnect from itemset this.itemSet = null; } // 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.setRange(this.range); this.groupSet.setItems(this.itemsData); this.groupSet.setGroups(this.groupsData); - this.contentPanel.appendChild(this.groupSet); } else { // ItemSet if (this.groupSet) { 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; } diff --git a/src/timeline/component/Component.js b/src/timeline/component/Component.js index b8737264..ad0919a1 100644 --- a/src/timeline/component/Component.js +++ b/src/timeline/component/Component.js @@ -7,7 +7,6 @@ function Component () { this.childs = null; this.options = null; - this.frame = null; // main DOM element this.top = 0; this.left = 0; this.width = 0; @@ -53,24 +52,13 @@ Component.prototype.getOption = function getOption(name) { 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. * @returns {HTMLElement | null} frame */ 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 * @return {Boolean} changed */ +// TODO: remove hide and show Component.prototype.hide = function hide() { if (this.frame && this.frame.parentNode) { 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 * @return {Boolean} changed */ +// TODO: remove hide and show Component.prototype.show = function show() { if (!this.frame || !this.frame.parentNode) { return this.repaint(); diff --git a/src/timeline/component/CurrentTime.js b/src/timeline/component/CurrentTime.js index d141afeb..089a5e13 100644 --- a/src/timeline/component/CurrentTime.js +++ b/src/timeline/component/CurrentTime.js @@ -15,6 +15,8 @@ function CurrentTime (range, options) { this.defaultOptions = { showCurrentTime: false }; + + this._create(); } CurrentTime.prototype = new Component(); @@ -22,73 +24,73 @@ CurrentTime.prototype = new Component(); 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 * @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) { clearTimeout(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; }; diff --git a/src/timeline/component/CustomTime.js b/src/timeline/component/CustomTime.js index f75fec73..4b7634b3 100644 --- a/src/timeline/component/CustomTime.js +++ b/src/timeline/component/CustomTime.js @@ -16,6 +16,9 @@ function CustomTime (options) { this.customTime = new Date(); this.eventParams = {}; // stores state parameters while dragging the bar + + // create the DOM + this._create(); } CustomTime.prototype = new Component(); @@ -23,12 +26,40 @@ CustomTime.prototype = new Component(); 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 */ 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); - bar.style.left = x + 'px'; - bar.title = 'Time: ' + this.customTime; + this.bar.style.left = x + 'px'; + this.bar.title = 'Time: ' + this.customTime; return false; }; diff --git a/src/timeline/component/Group.js b/src/timeline/component/Group.js index 2a8084da..061653bb 100644 --- a/src/timeline/component/Group.js +++ b/src/timeline/component/Group.js @@ -35,15 +35,6 @@ Group.prototype = new Component(); // TODO: comment 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, * filtered by the groups id. diff --git a/src/timeline/component/GroupSet.js b/src/timeline/component/GroupSet.js index 60594c38..a42fb2bb 100644 --- a/src/timeline/component/GroupSet.js +++ b/src/timeline/component/GroupSet.js @@ -1,14 +1,16 @@ /** * 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 * options. * @constructor GroupSet * @extends Panel */ -function GroupSet(labelPanel, options) { +function GroupSet(contentPanel, labelPanel, options) { this.id = util.randomUUID(); + this.contentPanel = contentPanel; this.labelPanel = labelPanel; this.options = options || {}; @@ -43,10 +45,37 @@ function GroupSet(labelPanel, options) { me._onRemove(params.items); } }; + + // create HTML DOM + this._create(); } 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. * @param {Object} [options] The following options are available: @@ -211,38 +240,9 @@ GroupSet.prototype.repaint = function repaint() { asString = util.option.asString, options = this.options, 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, groups = this.groups, groupsData = this.groupsData; @@ -266,7 +266,7 @@ GroupSet.prototype.repaint = function repaint() { 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.setRange(me.range); group.setItems(me.itemsData); // attach items data @@ -299,6 +299,7 @@ GroupSet.prototype.repaint = function repaint() { order: this.options.groupOrder }); + /* TODO // (re)create the labels while (labelSet.firstChild) { labelSet.removeChild(labelSet.firstChild); @@ -308,6 +309,7 @@ GroupSet.prototype.repaint = function repaint() { label = this._createLabel(id); labelSet.appendChild(label); } + */ } // repaint all groups in order @@ -350,25 +352,27 @@ GroupSet.prototype.repaint = function repaint() { } // 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 - 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; + /* TODO // reposition labels labelSet.style.top = asSize(options.top, '0'); labelSet.style.height = fixedHeight ? asSize(options.height) : this.height + 'px'; + */ }; /** @@ -403,19 +407,11 @@ GroupSet.prototype._createLabel = function(id) { return label; }; -/** - * Get container element - * @return {HTMLElement} container - */ -GroupSet.prototype.getContainer = function getContainer() { - return this.dom.frame; -}; - /** * Get the width of the group labels * @return {Number} width */ -GroupSet.prototype.getLabelsWidth = function getContainer() { +GroupSet.prototype.getLabelsWidth = function getLabelsWidth() { return this.props.labels.width; }; diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js index e87b392d..451fbc1d 100644 --- a/src/timeline/component/ItemSet.js +++ b/src/timeline/component/ItemSet.js @@ -50,6 +50,9 @@ function ItemSet(options) { this.touchParams = {}; // stores properties while dragging // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis + + // create the HTML DOM + this._create(); } ItemSet.prototype = new Panel(); @@ -62,6 +65,42 @@ ItemSet.types = { 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. * @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 * @return {boolean} Returns true if the component is resized @@ -173,47 +220,6 @@ ItemSet.prototype.repaint = function repaint() { orientation = this.getOption('orientation'), 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 frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : ''); diff --git a/src/timeline/component/Panel.js b/src/timeline/component/Panel.js index d7c02bb2..d9076ea7 100644 --- a/src/timeline/component/Panel.js +++ b/src/timeline/component/Panel.js @@ -11,7 +11,7 @@ */ function Panel(options) { this.id = util.randomUUID(); - this.parent = null; + this.parent = null; // TODO: remove parent? this.childs = []; this.options = options || {}; @@ -31,12 +31,19 @@ Panel.prototype = new Component(); 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) { this.childs.push(child); 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); if (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; } }; +/** + * 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 * @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 () { var asString = util.option.asString, 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 frame.className = 'vpanel' + (options.className ? (' ' + asString(options.className)) : ''); diff --git a/src/timeline/component/RootPanel.js b/src/timeline/component/RootPanel.js index b87455f1..4ec47e6b 100644 --- a/src/timeline/component/RootPanel.js +++ b/src/timeline/component/RootPanel.js @@ -15,11 +15,49 @@ function RootPanel(container, options) { 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(); } 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. * @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 */ 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 var options = this.options; var className = 'vis timeline rootpanel ' + options.orientation + (options.editable ? ' editable' : ''); diff --git a/src/timeline/component/TimeAxis.js b/src/timeline/component/TimeAxis.js index 17df2f37..95f71057 100644 --- a/src/timeline/component/TimeAxis.js +++ b/src/timeline/component/TimeAxis.js @@ -38,6 +38,9 @@ function TimeAxis (options) { }; this.range = null; + + // create the HTML DOM + this._create(); } TimeAxis.prototype = new Component(); @@ -45,6 +48,13 @@ TimeAxis.prototype = new Component(); // TODO: comment options 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) * @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; }; +/** + * Get the outer frame of the time axis + * @return {HTMLElement} frame + */ +TimeAxis.prototype.getFrame = function getFrame() { + return this.frame; +}; + /** * Repaint the component * @return {boolean} Returns true if the component is resized @@ -64,26 +82,11 @@ TimeAxis.prototype.setRange = function (range) { TimeAxis.prototype.repaint = function () { var asSize = util.option.asSize, 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; if (parent) { diff --git a/src/timeline/component/css/groupset.css b/src/timeline/component/css/groupset.css index e843988d..82b2e303 100644 --- a/src/timeline/component/css/groupset.css +++ b/src/timeline/component/css/groupset.css @@ -14,11 +14,9 @@ padding: 0; margin: 0; - - border-right: 1px solid #bfbfbf; } -.vis.timeline .labels .label-set { +.vis.timeline .labels .labelset { position: relative; top: 0; left: 0; @@ -33,7 +31,7 @@ box-sizing: border-box; } -.vis.timeline .labels .label-set .vlabel { +.vis.timeline .labels .labelset .vlabel { position: absolute; left: 0; top: 0; @@ -44,19 +42,19 @@ box-sizing: border-box; } -.vis.timeline.top .labels .label-set .vlabel, +.vis.timeline.top .labels .labelset .vlabel, .vis.timeline.top .groupset .itemset .axis { border-top: 1px solid #bfbfbf; border-bottom: none; } -.vis.timeline.bottom .labels .label-set .vlabel, +.vis.timeline.bottom .labels .labelset .vlabel, .vis.timeline.bottom .groupset .itemset .axis { border-top: none; border-bottom: 1px solid #bfbfbf; } -.vis.timeline .labels .label-set .vlabel .inner { +.vis.timeline .labels .labelset .vlabel .inner { display: inline-block; padding: 5px; } diff --git a/src/timeline/component/css/panel.css b/src/timeline/component/css/panel.css index 4f0491d7..f68fa878 100644 --- a/src/timeline/component/css/panel.css +++ b/src/timeline/component/css/panel.css @@ -15,11 +15,12 @@ .vis.timeline .vpanel { position: absolute; + overflow: hidden; -moz-box-sizing: border-box; box-sizing: border-box; } -.vis.timeline .vpanel { - overflow: hidden; +.vis.timeline .vpanel.side { + border-right: 1px solid #bfbfbf; } diff --git a/test/timeline.html b/test/timeline.html index a52af1c8..417d5808 100644 --- a/test/timeline.html +++ b/test/timeline.html @@ -77,7 +77,7 @@ //end: moment('2013-12-31'), //min: moment('2013-01-01'), //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 };