diff --git a/dist/vis.css b/dist/vis.css index 93889d03..e97f3199 100644 --- a/dist/vis.css +++ b/dist/vis.css @@ -565,6 +565,17 @@ input.vis-configuration.vis-config-range:focus::-ms-fill-upper { cursor: pointer; } +.vis-item .vis-delete-rtl { + background: url('img/timeline/delete.png') no-repeat center; + position: absolute; + width: 24px; + height: 24px; + top: -4px; + left: -24px; + cursor: pointer; +} + + .vis-item.vis-range .vis-drag-left { position: absolute; width: 24px; @@ -637,6 +648,11 @@ input.vis-configuration.vis-config-range:focus::-ms-fill-upper { border-left: 1px solid; } +.vis-time-axis .vis-grid.vis-vertical-rtl { + position: absolute; + border-right: 1px solid; +} + .vis-time-axis .vis-grid.vis-minor { border-color: #e5e5e5; } diff --git a/dist/vis.js b/dist/vis.js index 43b53bf7..77f3ffd5 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 4.15.1 - * @date 2016-03-08 + * @date 2016-03-13 * * @license * Copyright (C) 2011-2016 Almende B.V, http://almende.com @@ -723,6 +723,10 @@ return /******/ (function(modules) { // webpackBootstrap return elem.getBoundingClientRect().left; }; + exports.getAbsoluteRight = function (elem) { + return elem.getBoundingClientRect().right; + }; + /** * Retrieve the absolute top value of a DOM element * @param {Element} elem A dom element, for example a div @@ -10661,6 +10665,7 @@ return /******/ (function(modules) { // webpackBootstrap * @extends Core */ function Timeline(container, items, groups, options) { + if (!(this instanceof Timeline)) { throw new SyntaxError('Constructor must be called with the new operator'); } @@ -10684,7 +10689,7 @@ return /******/ (function(modules) { // webpackBootstrap axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both' item: 'bottom' // not relevant }, - + rtl: false, moment: moment, width: null, @@ -10739,7 +10744,7 @@ return /******/ (function(modules) { // webpackBootstrap this.components.push(this.currentTime); // item set - this.itemSet = new ItemSet(this.body); + this.itemSet = new ItemSet(this.body, this.options); this.components.push(this.itemSet); this.itemsData = null; // DataSet @@ -10822,6 +10827,7 @@ return /******/ (function(modules) { // webpackBootstrap Timeline.prototype.setOptions = function (options) { // validate options var errorFound = _Validator2.default.validate(options, allOptions); + if (errorFound === true) { console.log('%cErrors have been found in the supplied options object.', printStyle); } @@ -11051,16 +11057,23 @@ return /******/ (function(modules) { // webpackBootstrap var start = getStart(item); var end = getEnd(item); + console.log(start, end); + console.log(this.options); - var left = start - (item.getWidthLeft() + 10) * factor; - var right = end + (item.getWidthRight() + 10) * factor; + if (this.options.rtl) { + var startSide = start - (item.getWidthRight() + 10) * factor; + var endSide = end + (item.getWidthLeft() + 10) * factor; + } else { + var startSide = start - (item.getWidthLeft() + 10) * factor; + var endSide = end + (item.getWidthRight() + 10) * factor; + } - if (left < min) { - min = left; + if (startSide < min) { + min = startSide; minItem = item; } - if (right > max) { - max = right; + if (endSide > max) { + max = endSide; maxItem = item; } }.bind(_this)); @@ -11071,8 +11084,13 @@ return /******/ (function(modules) { // webpackBootstrap delta = _this.props.center.width - lhs - rhs; // px if (delta > 0) { - min = getStart(minItem) - lhs * interval / delta; // ms - max = getEnd(maxItem) + rhs * interval / delta; // ms + if (_this.options.rtl) { + min = getStart(minItem) - rhs * interval / delta; // ms + max = getEnd(maxItem) + lhs * interval / delta; // ms + } else { + min = getStart(minItem) - lhs * interval / delta; // ms + max = getEnd(maxItem) + rhs * interval / delta; // ms + } } } })(); @@ -11121,7 +11139,11 @@ return /******/ (function(modules) { // webpackBootstrap Timeline.prototype.getEventProperties = function (event) { var clientX = event.center ? event.center.x : event.clientX; var clientY = event.center ? event.center.y : event.clientY; - var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); + if (this.options.rtl) { + var x = util.getAbsoluteRight(this.dom.centerContainer) - clientX; + } else { + var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); + } var y = clientY - util.getAbsoluteTop(this.dom.centerContainer); var item = this.itemSet.itemFromTarget(event); @@ -15804,9 +15826,9 @@ return /******/ (function(modules) { // webpackBootstrap // http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript /* Copyright (c) 2011 Andrei Mackenzie - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ }, { @@ -15887,6 +15909,7 @@ return /******/ (function(modules) { // webpackBootstrap // default options this.defaultOptions = { + rtl: false, start: null, end: null, moment: moment, @@ -15899,7 +15922,6 @@ return /******/ (function(modules) { // webpackBootstrap zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds }; this.options = util.extend({}, this.defaultOptions); - this.props = { touch: {} }; @@ -15941,7 +15963,7 @@ return /******/ (function(modules) { // webpackBootstrap Range.prototype.setOptions = function (options) { if (options) { // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey']; + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl']; util.selectiveExtend(fields, this.options, options); if ('start' in options || 'end' in options) { @@ -16263,7 +16285,13 @@ return /******/ (function(modules) { // webpackBootstrap interval -= duration; var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; + + if (this.options.rtl) { + var diffRange = delta / width * interval; + } else { + var diffRange = -delta / width * interval; + } + var newStart = this.props.touch.start + diffRange; var newEnd = this.props.touch.end + diffRange; @@ -16365,7 +16393,7 @@ return /******/ (function(modules) { // webpackBootstrap } // calculate center, the date to zoom around - var pointer = getPointer({ x: event.clientX, y: event.clientY }, this.body.dom.center); + var pointer = this.getPointer({ x: event.clientX, y: event.clientY }, this.body.dom.center); var pointerDate = this._pointerToDate(pointer); this.zoom(scale, pointerDate, delta); @@ -16401,7 +16429,7 @@ return /******/ (function(modules) { // webpackBootstrap this.props.touch.allowDragging = false; if (!this.props.touch.center) { - this.props.touch.center = getPointer(event.center, this.body.dom.center); + this.props.touch.center = this.getPointer(event.center, this.body.dom.center); } var scale = 1 / (event.scale + this.scaleOffset); @@ -16446,7 +16474,11 @@ return /******/ (function(modules) { // webpackBootstrap // calculate the time where the mouse is, check whether inside // and no scroll action should happen. var clientX = event.center ? event.center.x : event.clientX; - var x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer); + if (this.options.rtl) { + var x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer); + } else { + var x = util.getAbsoluteRight(this.body.dom.centerContainer) - clientX; + } var time = this.body.util.toTime(x); return time >= this.start && time <= this.end; @@ -16480,12 +16512,19 @@ return /******/ (function(modules) { // webpackBootstrap * @return {{x: Number, y: Number}} pointer * @private */ - function getPointer(touch, element) { - return { - x: touch.x - util.getAbsoluteLeft(element), - y: touch.y - util.getAbsoluteTop(element) - }; - } + Range.prototype.getPointer = function (touch, element) { + if (this.options.rtl) { + return { + x: util.getAbsoluteRight(element) - touch.x, + y: touch.y - util.getAbsoluteTop(element) + }; + } else { + return { + x: touch.x - util.getAbsoluteLeft(element), + y: touch.y - util.getAbsoluteTop(element) + }; + } + }; /** * Zoom the range the given scale in or out. Start and end date will @@ -17321,9 +17360,17 @@ return /******/ (function(modules) { // webpackBootstrap Core.prototype.setOptions = function (options) { if (options) { // copy the known options - var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'locale', 'locales', 'moment', 'throttleRedraw']; + var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'locale', 'locales', 'moment', 'rtl', 'throttleRedraw']; util.selectiveExtend(fields, this.options, options); + if (this.options.rtl) { + var contentContainer = this.dom.leftContainer; + this.dom.leftContainer = this.dom.rightContainer; + this.dom.rightContainer = contentContainer; + this.dom.container.style.direction = "rtl"; + this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; + } + this.options.orientation = { item: undefined, axis: undefined }; if ('orientation' in options) { if (typeof options.orientation === 'string') { @@ -17622,7 +17669,7 @@ return /******/ (function(modules) { // webpackBootstrap var interval = range.max - range.min; var min = new Date(range.min.valueOf() - interval * 0.01); var max = new Date(range.max.valueOf() + interval * 0.01); - + console.log(min, max); var animation = options && options.animation !== undefined ? options.animation : true; this.range.setRange(min, max, animation); }; @@ -18170,8 +18217,8 @@ return /******/ (function(modules) { // webpackBootstrap */ function ItemSet(body, options) { this.body = body; - this.defaultOptions = { + rtl: false, type: null, // 'box', 'point', 'range', 'background' orientation: { item: 'bottom' // item orientation: 'top' or 'bottom' @@ -18374,8 +18421,8 @@ return /******/ (function(modules) { // webpackBootstrap // add item on doubletap this.hammer.on('doubletap', this._onAddItem.bind(this)); - this.groupHammer = new Hammer(this.body.dom.leftContainer); + this.groupHammer.on('panstart', this._onGroupDragStart.bind(this)); this.groupHammer.on('panmove', this._onGroupDrag.bind(this)); this.groupHammer.on('panend', this._onGroupDragEnd.bind(this)); @@ -18452,7 +18499,7 @@ return /******/ (function(modules) { // webpackBootstrap ItemSet.prototype.setOptions = function (options) { if (options) { // copy all options that we know - var fields = ['type', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap']; + var fields = ['type', 'rtl', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap']; util.selectiveExtend(fields, this.options, options); if ('orientation' in options) { @@ -18637,8 +18684,14 @@ return /******/ (function(modules) { // webpackBootstrap */ ItemSet.prototype.getVisibleItems = function () { var range = this.body.range.getRange(); - var left = this.body.util.toScreen(range.start); - var right = this.body.util.toScreen(range.end); + + if (this.options.rtl) { + var right = this.body.util.toScreen(range.start); + var left = this.body.util.toScreen(range.end); + } else { + var left = this.body.util.toScreen(range.start); + var right = this.body.util.toScreen(range.end); + } var ids = []; for (var groupId in this.groups) { @@ -18651,8 +18704,14 @@ return /******/ (function(modules) { // webpackBootstrap for (var i = 0; i < rawVisibleItems.length; i++) { var item = rawVisibleItems[i]; // TODO: also check whether visible vertically - if (item.left < right && item.left + item.width > left) { - ids.push(item.id); + if (this.options.rtl) { + if (item.right < left && item.right + item.width > right) { + ids.push(item.id); + } + } else { + if (item.left < right && item.left + item.width > left) { + ids.push(item.id); + } } } } @@ -18692,7 +18751,12 @@ return /******/ (function(modules) { // webpackBootstrap // recalculate absolute position (before redrawing groups) this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; - this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; + + if (this.options.rtl) { + this.props.right = this.body.domProps.right.width + this.body.domProps.border.right; + } else { + this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; + } // update class name frame.className = 'vis-itemset'; @@ -18743,7 +18807,11 @@ return /******/ (function(modules) { // webpackBootstrap // reposition axis this.dom.axis.style.top = asSize(orientation == 'top' ? this.body.domProps.top.height + this.body.domProps.border.top : this.body.domProps.top.height + this.body.domProps.centerContainer.height); - this.dom.axis.style.left = '0'; + if (this.options.rtl) { + this.dom.axis.style.right = '0'; + } else { + this.dom.axis.style.left = '0'; + } // check if this component is resized resized = this._isResized() || resized; @@ -19363,8 +19431,15 @@ return /******/ (function(modules) { // webpackBootstrap */ ItemSet.prototype._onDragStartAddItem = function (event) { var snap = this.options.snap || null; - var xAbs = util.getAbsoluteLeft(this.dom.frame); - var x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px + + if (this.options.rtl) { + var xAbs = util.getAbsoluteRight(this.dom.frame); + var x = xAbs - event.center.x + 10; // plus 10 to compensate for the drag starting as soon as you've moved 10px + } else { + var xAbs = util.getAbsoluteLeft(this.dom.frame); + var x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px + } + var time = this.body.util.toTime(x); var scale = this.body.util.getScale(); var step = this.body.util.getStep(); @@ -19385,7 +19460,6 @@ return /******/ (function(modules) { // webpackBootstrap if (group) { itemData.group = group.groupId; } - var newItem = new RangeItem(itemData, this.conversion, this.options); newItem.id = id; // TODO: not so nice setting id afterwards newItem.data = this._cloneItemData(itemData); @@ -19393,10 +19467,15 @@ return /******/ (function(modules) { // webpackBootstrap var props = { item: newItem, - dragRight: true, initialX: event.center.x, data: newItem.data }; + + if (this.options.rtl) { + props.dragLeft = true; + } else { + props.dragRight = true; + } this.touchParams.itemProps = [props]; event.stopPropagation(); @@ -19413,7 +19492,13 @@ return /******/ (function(modules) { // webpackBootstrap var me = this; var snap = this.options.snap || null; - var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; + + if (this.options.rtl) { + var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.right.width; + } else { + var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; + } + var scale = this.body.util.getScale(); var step = this.body.util.getStep(); @@ -19437,7 +19522,12 @@ return /******/ (function(modules) { // webpackBootstrap this.touchParams.itemProps.forEach(function (props) { var current = me.body.util.toTime(event.center.x - xOffset); var initial = me.body.util.toTime(props.initialX - xOffset); - var offset = current - initial; // ms + + if (this.options.rtl) { + var offset = -(current - initial); // ms + } else { + var offset = current - initial; // ms + } var itemData = this._cloneItemData(props.item.data); // clone the data if (props.item.editable === false) { @@ -19445,27 +19535,45 @@ return /******/ (function(modules) { // webpackBootstrap } var updateTimeAllowed = me.options.editable.updateTime || props.item.editable === true; - if (updateTimeAllowed) { if (props.dragLeft) { // drag left side of a range item - if (itemData.start != undefined) { - var initialStart = util.convert(props.data.start, 'Date'); - var start = new Date(initialStart.valueOf() + offset); - // TODO: pass a Moment instead of a Date to snap(). (Breaking change) - itemData.start = snap ? snap(start, scale, step) : start; + if (this.options.rtl) { + if (itemData.end != undefined) { + var initialEnd = util.convert(props.data.end, 'Date'); + var end = new Date(initialEnd.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.end = snap ? snap(end, scale, step) : end; + } + } else { + if (itemData.start != undefined) { + var initialStart = util.convert(props.data.start, 'Date'); + var start = new Date(initialStart.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.start = snap ? snap(start, scale, step) : start; + } } } else if (props.dragRight) { // drag right side of a range item - if (itemData.end != undefined) { - var initialEnd = util.convert(props.data.end, 'Date'); - var end = new Date(initialEnd.valueOf() + offset); - // TODO: pass a Moment instead of a Date to snap(). (Breaking change) - itemData.end = snap ? snap(end, scale, step) : end; + if (this.options.rtl) { + if (itemData.start != undefined) { + var initialStart = util.convert(props.data.start, 'Date'); + var start = new Date(initialStart.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.start = snap ? snap(start, scale, step) : start; + } + } else { + if (itemData.end != undefined) { + var initialEnd = util.convert(props.data.end, 'Date'); + var end = new Date(initialEnd.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.end = snap ? snap(end, scale, step) : end; + } } } else { // drag both start and end if (itemData.start != undefined) { + var initialStart = util.convert(props.data.start, 'Date').valueOf(); var start = new Date(initialStart + offset); @@ -19811,8 +19919,15 @@ return /******/ (function(modules) { // webpackBootstrap }); } else { // add item - var xAbs = util.getAbsoluteLeft(this.dom.frame); - var x = event.center.x - xAbs; + if (this.options.rtl) { + var xAbs = util.getAbsoluteRight(this.dom.frame); + var x = xAbs - event.center.x; + } else { + var xAbs = util.getAbsoluteLeft(this.dom.frame); + var x = event.center.x - xAbs; + } + // var xAbs = util.getAbsoluteLeft(this.dom.frame); + // var x = event.center.x - xAbs; var start = this.body.util.toTime(x); var scale = this.body.util.getScale(); var step = this.body.util.getStep(); @@ -20967,8 +21082,8 @@ return /******/ (function(modules) { // webpackBootstrap this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); } else { // no custom order function, lazy stacking - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); if (this.itemSet.options.stack) { // TODO: ugly way to access options... stack.stack(this.visibleItems, margin, restack); @@ -20984,10 +21099,9 @@ return /******/ (function(modules) { // webpackBootstrap // calculate actual size and position var foreground = this.dom.foreground; this.top = foreground.offsetTop; - this.left = foreground.offsetLeft; + this.right = foreground.offsetLeft; this.width = foreground.offsetWidth; resized = util.updateProperty(this, 'height', height) || resized; - // recalculate size of label resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; @@ -21427,7 +21541,6 @@ return /******/ (function(modules) { // webpackBootstrap */ exports.stack = function (items, margin, force) { var i, iMax; - if (force) { // reset top position of all items for (i = 0, iMax = items.length; i < iMax; i++) { @@ -21448,7 +21561,7 @@ return /******/ (function(modules) { // webpackBootstrap var collidingItem = null; for (var j = 0, jj = items.length; j < jj; j++) { var other = items[j]; - if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { + if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item, other.options.rtl)) { collidingItem = other; break; } @@ -21501,8 +21614,14 @@ return /******/ (function(modules) { // webpackBootstrap * minimum required margin. * @return {boolean} true if a and b collide, else false */ - exports.collision = function (a, b, margin) { - return a.left - margin.horizontal + EPSILON < b.left + b.width && a.left + a.width + margin.horizontal - EPSILON > b.left && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top; + exports.collision = function (a, b, margin, rtl) { + var isCollision = null; + if (rtl) { + isCollision = a.right - margin.horizontal + EPSILON < b.right + b.width && a.right + a.width + margin.horizontal - EPSILON > b.right && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top; + } else { + a.left - margin.horizontal + EPSILON < b.left + b.width && a.left + a.width + margin.horizontal - EPSILON > b.left && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top; + } + return isCollision; }; /***/ }, @@ -21531,7 +21650,7 @@ return /******/ (function(modules) { // webpackBootstrap } }; this.overflow = false; // if contents can overflow (css styling), this flag is set to true - + this.options = options; // validate data if (data) { if (data.start == undefined) { @@ -21631,7 +21750,7 @@ return /******/ (function(modules) { // webpackBootstrap this.dirty = false; } - + console.log("redrawing range"); this._repaintDeleteButton(dom.box); this._repaintDragLeft(); this._repaintDragRight(); @@ -21676,7 +21795,7 @@ return /******/ (function(modules) { // webpackBootstrap var parentWidth = this.parent.width; var start = this.conversion.toScreen(this.data.start); var end = this.conversion.toScreen(this.data.end); - var contentLeft; + var contentStartPosition; var contentWidth; // limit the width of the range, as browsers cannot draw very wide divs @@ -21691,7 +21810,11 @@ return /******/ (function(modules) { // webpackBootstrap var boxWidth = Math.max(end - start, 1); if (this.overflow) { - this.left = start; + if (this.options.rtl) { + this.right = start; + } else { + this.left = start; + } this.width = boxWidth + this.props.content.width; contentWidth = this.props.content.width; @@ -21699,25 +21822,46 @@ return /******/ (function(modules) { // webpackBootstrap // a width which will not change when moving the Timeline // So no re-stacking needed, which is nicer for the eye; } else { - this.left = start; + if (this.options.rtl) { + this.right = start; + } else { + this.left = start; + } this.width = boxWidth; contentWidth = Math.min(end - start, this.props.content.width); } - this.dom.box.style.left = this.left + 'px'; + if (this.options.rtl) { + this.dom.box.style.right = this.right + 'px'; + } else { + this.dom.box.style.left = this.left + 'px'; + } this.dom.box.style.width = boxWidth + 'px'; switch (this.options.align) { case 'left': - this.dom.content.style.left = '0'; + if (this.options.rtl) { + this.dom.content.style.right = '0'; + } else { + this.dom.content.style.left = '0'; + } break; case 'right': - this.dom.content.style.left = Math.max(boxWidth - contentWidth, 0) + 'px'; + if (this.options.rtl) { + this.dom.content.style.right = Math.max(boxWidth - contentWidth, 0) + 'px'; + } else { + this.dom.content.style.left = Math.max(boxWidth - contentWidth, 0) + 'px'; + } break; case 'center': - this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; + if (this.options.rtl) { + this.dom.content.style.right = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; + } else { + this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; + } + break; default: @@ -21725,18 +21869,22 @@ return /******/ (function(modules) { // webpackBootstrap // when range exceeds left of the window, position the contents at the left of the visible area if (this.overflow) { if (end > 0) { - contentLeft = Math.max(-start, 0); + contentStartPosition = Math.max(-start, 0); } else { - contentLeft = -contentWidth; // ensure it's not visible anymore + contentStartPosition = -contentWidth; // ensure it's not visible anymore } } else { if (start < 0) { - contentLeft = -start; + contentStartPosition = -start; } else { - contentLeft = 0; + contentStartPosition = 0; } } - this.dom.content.style.left = contentLeft + 'px'; + if (this.options.rtl) { + this.dom.content.style.right = contentStartPosition + 'px'; + } else { + this.dom.content.style.left = contentStartPosition + 'px'; + } } }; @@ -21782,6 +21930,7 @@ return /******/ (function(modules) { // webpackBootstrap * @protected */ RangeItem.prototype._repaintDragRight = function () { + console.log("repainting!!!!"); if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { // create and show drag area var dragRight = document.createElement('div'); @@ -21832,6 +21981,7 @@ return /******/ (function(modules) { // webpackBootstrap this.dirty = true; this.top = null; + this.right = null; this.left = null; this.width = null; this.height = null; @@ -21958,7 +22108,12 @@ return /******/ (function(modules) { // webpackBootstrap var me = this; var deleteButton = document.createElement('div'); - deleteButton.className = 'vis-delete'; + + if (this.options.rtl) { + deleteButton.className = 'vis-delete-rtl'; + } else { + deleteButton.className = 'vis-delete'; + } deleteButton.title = 'Delete this item'; // TODO: be able to destroy the delete button @@ -22196,7 +22351,7 @@ return /******/ (function(modules) { // webpackBootstrap height: 0 } }; - + this.options = options; // validate data if (data) { if (data.start == undefined) { @@ -22340,27 +22495,54 @@ return /******/ (function(modules) { // webpackBootstrap // calculate left position of the box if (align == 'right') { - this.left = start - this.width; + if (this.options.rtl) { + this.right = start - this.width; - // reposition box, line, and dot - this.dom.box.style.left = this.left + 'px'; - this.dom.line.style.left = start - this.props.line.width + 'px'; - this.dom.dot.style.left = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; + // reposition box, line, and dot + this.dom.box.style.right = this.right + 'px'; + this.dom.line.style.right = start - this.props.line.width + 'px'; + this.dom.dot.style.right = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; + } else { + this.left = start - this.width; + + // reposition box, line, and dot + this.dom.box.style.left = this.left + 'px'; + this.dom.line.style.left = start - this.props.line.width + 'px'; + this.dom.dot.style.left = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; + } } else if (align == 'left') { - this.left = start; + if (this.options.rtl) { + this.right = start; - // reposition box, line, and dot - this.dom.box.style.left = this.left + 'px'; - this.dom.line.style.left = start + 'px'; - this.dom.dot.style.left = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; + // reposition box, line, and dot + this.dom.box.style.right = this.right + 'px'; + this.dom.line.style.right = start + 'px'; + this.dom.dot.style.right = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; + } else { + this.left = start; + + // reposition box, line, and dot + this.dom.box.style.left = this.left + 'px'; + this.dom.line.style.left = start + 'px'; + this.dom.dot.style.left = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; + } } else { // default or 'center' - this.left = start - this.width / 2; + if (this.options.rtl) { + this.right = start - this.width / 2; - // reposition box, line, and dot - this.dom.box.style.left = this.left + 'px'; - this.dom.line.style.left = start - this.props.line.width / 2 + 'px'; - this.dom.dot.style.left = start - this.props.dot.width / 2 + 'px'; + // reposition box, line, and dot + this.dom.box.style.right = this.right + 'px'; + this.dom.line.style.right = start - this.props.line.width + 'px'; + this.dom.dot.style.right = start - this.props.dot.width / 2 + 'px'; + } else { + this.left = start - this.width / 2; + + // reposition box, line, and dot + this.dom.box.style.left = this.left + 'px'; + this.dom.line.style.left = start - this.props.line.width / 2 + 'px'; + this.dom.dot.style.left = start - this.props.dot.width / 2 + 'px'; + } } }; @@ -22438,10 +22620,11 @@ return /******/ (function(modules) { // webpackBootstrap }, content: { height: 0, - marginLeft: 0 + marginLeft: 0, + marginRight: 0 } }; - + this.options = options; // validate data if (data) { if (data.start == undefined) { @@ -22531,7 +22714,11 @@ return /******/ (function(modules) { // webpackBootstrap this.props.content.height = dom.content.offsetHeight; // resize contents - dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + if (this.options.rtl) { + dom.content.style.marginRight = 2 * this.props.dot.width + 'px'; + } else { + dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + } //dom.content.style.marginRight = ... + 'px'; // TODO: margin right // recalculate size @@ -22540,7 +22727,11 @@ return /******/ (function(modules) { // webpackBootstrap // reposition the dot dom.dot.style.top = (this.height - this.props.dot.height) / 2 + 'px'; - dom.dot.style.left = this.props.dot.width / 2 + 'px'; + if (this.options.rtl) { + dom.dot.style.right = this.props.dot.width / 2 + 'px'; + } else { + dom.dot.style.left = this.props.dot.width / 2 + 'px'; + } this.dirty = false; } @@ -22578,10 +22769,17 @@ return /******/ (function(modules) { // webpackBootstrap PointItem.prototype.repositionX = function () { var start = this.conversion.toScreen(this.data.start); - this.left = start - this.props.dot.width; + if (this.options.rtl) { + this.right = start - this.props.dot.width; - // reposition point - this.dom.point.style.left = this.left + 'px'; + // reposition point + this.dom.point.style.right = this.right + 'px'; + } else { + this.left = start - this.props.dot.width; + + // reposition point + this.dom.point.style.left = this.left + 'px'; + } }; /** @@ -22591,7 +22789,6 @@ return /******/ (function(modules) { // webpackBootstrap PointItem.prototype.repositionY = function () { var orientation = this.options.orientation.item; var point = this.dom.point; - if (orientation == 'top') { point.style.top = this.top + 'px'; } else { @@ -22612,7 +22809,7 @@ return /******/ (function(modules) { // webpackBootstrap * @return {number} */ PointItem.prototype.getWidthRight = function () { - return this.width - this.props.dot.width; + return this.props.dot.width; }; module.exports = PointItem; @@ -22915,7 +23112,7 @@ return /******/ (function(modules) { // webpackBootstrap TimeAxis.prototype.setOptions = function (options) { if (options) { // copy all options that we know - util.selectiveExtend(['showMinorLabels', 'showMajorLabels', 'maxMinorChars', 'hiddenDates', 'timeAxis', 'moment'], this.options, options); + util.selectiveExtend(['showMinorLabels', 'showMajorLabels', 'maxMinorChars', 'hiddenDates', 'timeAxis', 'moment', 'rtl'], this.options, options); // deep copy the format options util.selectiveDeepExtend(['format'], this.options, options); @@ -23019,7 +23216,6 @@ return /******/ (function(modules) { // webpackBootstrap } else { this.body.dom.backgroundVertical.appendChild(background); } - return this._isResized() || parentChanged; }; @@ -23172,7 +23368,13 @@ return /******/ (function(modules) { // webpackBootstrap label.childNodes[0].nodeValue = text; label.style.top = orientation == 'top' ? this.props.majorLabelHeight + 'px' : '0'; - label.style.left = x + 'px'; + + if (this.options.rtl) { + label.style.left = ""; + label.style.right = x + 'px'; + } else { + label.style.left = x + 'px'; + }; label.className = 'vis-text vis-minor ' + className; //label.title = title; // TODO: this is a heavy operation @@ -23206,7 +23408,12 @@ return /******/ (function(modules) { // webpackBootstrap //label.title = title; // TODO: this is a heavy operation label.style.top = orientation == 'top' ? '0' : this.props.minorLabelHeight + 'px'; - label.style.left = x + 'px'; + if (this.options.rtl) { + label.style.left = ""; + label.style.right = x + 'px'; + } else { + label.style.left = x + 'px'; + }; return label; }; @@ -23237,11 +23444,16 @@ return /******/ (function(modules) { // webpackBootstrap line.style.top = this.body.domProps.top.height + 'px'; } line.style.height = props.minorLineHeight + 'px'; - line.style.left = x - props.minorLineWidth / 2 + 'px'; + if (this.options.rtl) { + line.style.left = ""; + line.style.right = x - props.minorLineWidth / 2 + 'px'; + line.className = 'vis-grid vis-vertical-rtl vis-minor ' + className; + } else { + line.style.left = x - props.minorLineWidth / 2 + 'px'; + line.className = 'vis-grid vis-vertical vis-minor ' + className; + }; line.style.width = width + 'px'; - line.className = 'vis-grid vis-vertical vis-minor ' + className; - return line; }; @@ -23270,12 +23482,19 @@ return /******/ (function(modules) { // webpackBootstrap } else { line.style.top = this.body.domProps.top.height + 'px'; } - line.style.left = x - props.majorLineWidth / 2 + 'px'; + + if (this.options.rtl) { + line.style.left = ""; + line.style.right = x - props.majorLineWidth / 2 + 'px'; + line.className = 'vis-grid vis-vertical-rtl vis-major ' + className; + } else { + line.style.left = x - props.majorLineWidth / 2 + 'px'; + line.className = 'vis-grid vis-vertical vis-major ' + className; + } + line.style.height = props.majorLineHeight + 'px'; line.style.width = width + 'px'; - line.className = 'vis-grid vis-vertical vis-major ' + className; - return line; }; @@ -23972,6 +24191,7 @@ return /******/ (function(modules) { // webpackBootstrap // default options this.defaultOptions = { + rtl: false, showCurrentTime: true, moment: moment, @@ -24020,7 +24240,7 @@ return /******/ (function(modules) { // webpackBootstrap CurrentTime.prototype.setOptions = function (options) { if (options) { // copy all options that we know - util.selectiveExtend(['showCurrentTime', 'moment', 'locale', 'locales'], this.options, options); + util.selectiveExtend(['rtl', 'showCurrentTime', 'moment', 'locale', 'locales'], this.options, options); } }; @@ -24055,7 +24275,11 @@ return /******/ (function(modules) { // webpackBootstrap var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss'); title = title.charAt(0).toUpperCase() + title.substring(1); - this.bar.style.left = x + 'px'; + if (this.options.rtl) { + this.bar.style.right = x + 'px'; + } else { + this.bar.style.left = x + 'px'; + } this.bar.title = title; } else { // remove the line from the DOM @@ -24162,6 +24386,7 @@ return /******/ (function(modules) { // webpackBootstrap //globals : align: { string: string }, + rtl: { boolean: boolean, 'undefined': 'undefined' }, autoResize: { boolean: boolean }, throttleRedraw: { number: number }, clickToUse: { boolean: boolean }, @@ -24279,6 +24504,7 @@ return /******/ (function(modules) { // webpackBootstrap var configureOptions = { global: { align: ['center', 'left', 'right'], + direction: false, autoResize: true, throttleRedraw: [10, 0, 1000, 10], clickToUse: false, @@ -25952,10 +26178,10 @@ return /******/ (function(modules) { // webpackBootstrap DataAxis.prototype.show = function () { this.hidden = false; if (!this.dom.frame.parentNode) { - if (this.options.orientation === 'left') { + if (this.options.rtl) { this.body.dom.left.appendChild(this.dom.frame); } else { - this.body.dom.right.appendChild(this.dom.frame); + this.body.dom.left.appendChild(this.dom.frame); } } @@ -42865,7 +43091,12 @@ return /******/ (function(modules) { // webpackBootstrap }, { key: '_createDeleteButton', value: function _createDeleteButton(locale) { - var button = this._createButton('delete', 'vis-button vis-delete', locale['del'] || this.options.locales['en']['del']); + if (this.options.rtl) { + var deleteBtnClass = 'vis-button vis-delete-rtl'; + } else { + var deleteBtnClass = 'vis-button vis-delete'; + } + var button = this._createButton('delete', deleteBtnClass, locale['del'] || this.options.locales['en']['del']); this.manipulationDiv.appendChild(button); this._bindHammerToDiv(button, this.deleteSelected.bind(this)); } diff --git a/dist/vis.min.css b/dist/vis.min.css index d7e4286c..40d182cf 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file +.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item .vis-delete,.vis-item .vis-delete-rtl{background:url(img/timeline/delete.png) center no-repeat;height:24px;top:-4px;cursor:pointer}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{position:absolute;width:24px;right:-24px}.vis-item .vis-delete-rtl{position:absolute;width:24px;left:-24px}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-vertical-rtl{position:absolute;border-right:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} \ No newline at end of file diff --git a/docs/data/dataset.html b/docs/data/dataset.html index c143305c..3f90ead4 100644 --- a/docs/data/dataset.html +++ b/docs/data/dataset.html @@ -431,7 +431,7 @@ var data = new vis.DataSet([data] [, options]) Number[] - Update on ore multiple existing items. data can be a single item or an array with items. When an item doesn't exist, it will be created. Returns an array with the ids of the removed items. See section Data Manipulation. + Update one or multiple existing items. data can be a single item or an array with items. When an item doesn't exist, it will be created. Returns an array with the ids of the removed items. See section Data Manipulation. diff --git a/examples/timeline/groups/groupsEditable.html b/examples/timeline/groups/groupsEditable.html index 8fce1410..25ac3a57 100644 --- a/examples/timeline/groups/groupsEditable.html +++ b/examples/timeline/groups/groupsEditable.html @@ -299,6 +299,7 @@ a.value = b.value; b.value = v; }, + orientation: 'both', editable: true, groupEditable: true, start: new Date(2015, 6, 1), diff --git a/examples/timeline/interaction/animateWindow.html b/examples/timeline/interaction/animateWindow.html index 5ec8d7bd..c702f283 100644 --- a/examples/timeline/interaction/animateWindow.html +++ b/examples/timeline/interaction/animateWindow.html @@ -58,7 +58,7 @@ start: '2014-01-10', end: '2014-02-10', editable: true, - showCurrentTime: true + showCurrentTime: true, }; var timeline = new vis.Timeline(container, items, options); diff --git a/examples/timeline/other/rtl.html b/examples/timeline/other/rtl.html new file mode 100644 index 00000000..63fc79ad --- /dev/null +++ b/examples/timeline/other/rtl.html @@ -0,0 +1,77 @@ + + + + Timeline | RTL example + + + + + + + + + +

An editable timeline allows to drag items around, create new items, and remove items. Changes are logged in the browser console.

+ +
+ + + + \ No newline at end of file diff --git a/lib/network/modules/ManipulationSystem.js b/lib/network/modules/ManipulationSystem.js index 72b6778d..3fd82a8c 100644 --- a/lib/network/modules/ManipulationSystem.js +++ b/lib/network/modules/ManipulationSystem.js @@ -690,7 +690,12 @@ class ManipulationSystem { } _createDeleteButton(locale) { - let button = this._createButton('delete', 'vis-button vis-delete', locale['del'] || this.options.locales['en']['del']); + if (this.options.rtl) { + var deleteBtnClass = 'vis-button vis-delete-rtl'; + } else { + var deleteBtnClass = 'vis-button vis-delete'; + } + let button = this._createButton('delete', deleteBtnClass, locale['del'] || this.options.locales['en']['del']); this.manipulationDiv.appendChild(button); this._bindHammerToDiv(button, this.deleteSelected.bind(this)); } diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index 97281b73..6984e252 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -222,11 +222,19 @@ Core.prototype.setOptions = function (options) { var fields = [ 'width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', - 'locale', 'locales', 'moment', + 'locale', 'locales', 'moment', 'rtl', 'throttleRedraw' ]; util.selectiveExtend(fields, this.options, options); + + if (this.options.rtl) { + var contentContainer = this.dom.leftContainer; + this.dom.leftContainer = this.dom.rightContainer; + this.dom.rightContainer = contentContainer; + this.dom.container.style.direction = "rtl"; + this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; } + this.options.orientation = {item:undefined,axis:undefined}; if ('orientation' in options) { if (typeof options.orientation === 'string') { @@ -529,7 +537,7 @@ Core.prototype.fit = function(options) { var interval = range.max - range.min; var min = new Date(range.min.valueOf() - interval * 0.01); var max = new Date(range.max.valueOf() + interval * 0.01); - + console.log(min, max); var animation = (options && options.animation !== undefined) ? options.animation : true; this.range.setRange(min, max, animation); }; diff --git a/lib/timeline/Range.js b/lib/timeline/Range.js index 97e690e2..41e500af 100644 --- a/lib/timeline/Range.js +++ b/lib/timeline/Range.js @@ -25,6 +25,7 @@ function Range(body, options) { // default options this.defaultOptions = { + rtl: false, start: null, end: null, moment: moment, @@ -37,7 +38,6 @@ function Range(body, options) { zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds }; this.options = util.extend({}, this.defaultOptions); - this.props = { touch: {} }; @@ -81,7 +81,7 @@ Range.prototype.setOptions = function (options) { // copy the options that we know var fields = [ 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', - 'moment', 'activate', 'hiddenDates', 'zoomKey' + 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl' ]; util.selectiveExtend(fields, this.options, options); @@ -411,7 +411,13 @@ Range.prototype._onDrag = function (event) { interval -= duration; var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; + + if (this.options.rtl) { + var diffRange = delta / width * interval; + } else { + var diffRange = -delta / width * interval; + } + var newStart = this.props.touch.start + diffRange; var newEnd = this.props.touch.end + diffRange; @@ -513,7 +519,7 @@ Range.prototype._onMouseWheel = function(event) { } // calculate center, the date to zoom around - var pointer = getPointer({x: event.clientX, y: event.clientY}, this.body.dom.center); + var pointer = this.getPointer({x: event.clientX, y: event.clientY}, this.body.dom.center); var pointerDate = this._pointerToDate(pointer); this.zoom(scale, pointerDate, delta); @@ -549,7 +555,7 @@ Range.prototype._onPinch = function (event) { this.props.touch.allowDragging = false; if (!this.props.touch.center) { - this.props.touch.center = getPointer(event.center, this.body.dom.center); + this.props.touch.center = this.getPointer(event.center, this.body.dom.center); } var scale = 1 / (event.scale + this.scaleOffset); @@ -594,7 +600,11 @@ Range.prototype._isInsideRange = function(event) { // calculate the time where the mouse is, check whether inside // and no scroll action should happen. var clientX = event.center ? event.center.x : event.clientX; - var x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer); + if (this.options.rtl) { + var x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer); + } else { + var x = util.getAbsoluteRight(this.body.dom.centerContainer) - clientX; + } var time = this.body.util.toTime(x); return time >= this.start && time <= this.end; @@ -629,11 +639,18 @@ Range.prototype._pointerToDate = function (pointer) { * @return {{x: Number, y: Number}} pointer * @private */ -function getPointer (touch, element) { - return { - x: touch.x - util.getAbsoluteLeft(element), - y: touch.y - util.getAbsoluteTop(element) - }; +Range.prototype.getPointer = function (touch, element) { + if (this.options.rtl) { + return { + x: util.getAbsoluteRight(element) - touch.x, + y: touch.y - util.getAbsoluteTop(element) + }; + } else { + return { + x: touch.x - util.getAbsoluteLeft(element), + y: touch.y - util.getAbsoluteTop(element) + }; + } } /** diff --git a/lib/timeline/Stack.js b/lib/timeline/Stack.js index e39e10c6..b1b8fb03 100644 --- a/lib/timeline/Stack.js +++ b/lib/timeline/Stack.js @@ -37,8 +37,7 @@ exports.orderByEnd = function(items) { * items having a top===null will be re-stacked */ exports.stack = function(items, margin, force) { - var i, iMax; - + var i, iMax; if (force) { // reset top position of all items for (i = 0, iMax = items.length; i < iMax; i++) { @@ -59,7 +58,7 @@ exports.stack = function(items, margin, force) { var collidingItem = null; for (var j = 0, jj = items.length; j < jj; j++) { var other = items[j]; - if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { + if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item, other.options.rtl)) { collidingItem = other; break; } @@ -114,9 +113,18 @@ exports.nostack = function(items, margin, subgroups) { * minimum required margin. * @return {boolean} true if a and b collide, else false */ -exports.collision = function(a, b, margin) { - return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && - (a.left + a.width + margin.horizontal - EPSILON) > b.left && - (a.top - margin.vertical + EPSILON) < (b.top + b.height) && - (a.top + a.height + margin.vertical - EPSILON) > b.top); +exports.collision = function(a, b, margin, rtl) { + var isCollision = null; + if (rtl) { + isCollision = ((a.right - margin.horizontal + EPSILON) < (b.right + b.width) && + (a.right + a.width + margin.horizontal - EPSILON) > b.right && + (a.top - margin.vertical + EPSILON) < (b.top + b.height) && + (a.top + a.height + margin.vertical - EPSILON) > b.top); + } else { + ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && + (a.left + a.width + margin.horizontal - EPSILON) > b.left && + (a.top - margin.vertical + EPSILON) < (b.top + b.height) && + (a.top + a.height + margin.vertical - EPSILON) > b.top); + } + return isCollision; }; diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js index a69f58dd..9a84c993 100644 --- a/lib/timeline/Timeline.js +++ b/lib/timeline/Timeline.js @@ -29,6 +29,7 @@ import Validator from '../shared/Validator'; * @extends Core */ function Timeline (container, items, groups, options) { + if (!(this instanceof Timeline)) { throw new SyntaxError('Constructor must be called with the new operator'); } @@ -52,7 +53,7 @@ function Timeline (container, items, groups, options) { axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both' item: 'bottom' // not relevant }, - + rtl: false, moment: moment, width: null, @@ -107,7 +108,7 @@ function Timeline (container, items, groups, options) { this.components.push(this.currentTime); // item set - this.itemSet = new ItemSet(this.body); + this.itemSet = new ItemSet(this.body, this.options); this.components.push(this.itemSet); this.itemsData = null; // DataSet @@ -191,6 +192,7 @@ Timeline.prototype.redraw = function() { Timeline.prototype.setOptions = function (options) { // validate options let errorFound = Validator.validate(options, allOptions); + if (errorFound === true) { console.log('%cErrors have been found in the supplied options object.', printStyle); } @@ -413,16 +415,24 @@ Timeline.prototype.getItemRange = function () { var start = getStart(item); var end = getEnd(item); + console.log(start, end) + console.log(this.options); + + if (this.options.rtl) { + var startSide = start - (item.getWidthRight() + 10) * factor; + var endSide = end + (item.getWidthLeft() + 10) * factor; + } else { + var startSide = start - (item.getWidthLeft() + 10) * factor; + var endSide = end + (item.getWidthRight() + 10) * factor; + } + - var left = start - (item.getWidthLeft() + 10) * factor; - var right = end + (item.getWidthRight() + 10) * factor; - - if (left < min) { - min = left; + if (startSide < min) { + min = startSide; minItem = item; } - if (right > max) { - max = right; + if (endSide > max) { + max = endSide; maxItem = item; } }.bind(this)); @@ -433,8 +443,13 @@ Timeline.prototype.getItemRange = function () { var delta = this.props.center.width - lhs - rhs; // px if (delta > 0) { - min = getStart(minItem) - lhs * interval / delta; // ms - max = getEnd(maxItem) + rhs * interval / delta; // ms + if (this.options.rtl) { + min = getStart(minItem) - rhs * interval / delta; // ms + max = getEnd(maxItem) + lhs * interval / delta; // ms + } else { + min = getStart(minItem) - lhs * interval / delta; // ms + max = getEnd(maxItem) + rhs * interval / delta; // ms + } } } } @@ -482,7 +497,11 @@ Timeline.prototype.getDataRange = function() { Timeline.prototype.getEventProperties = function (event) { var clientX = event.center ? event.center.x : event.clientX; var clientY = event.center ? event.center.y : event.clientY; - var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); + if (this.options.rtl) { + var x = util.getAbsoluteRight(this.dom.centerContainer) - clientX; + } else { + var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); + } var y = clientY - util.getAbsoluteTop(this.dom.centerContainer); var item = this.itemSet.itemFromTarget(event); diff --git a/lib/timeline/component/CurrentTime.js b/lib/timeline/component/CurrentTime.js index ee1c7148..a697a582 100644 --- a/lib/timeline/component/CurrentTime.js +++ b/lib/timeline/component/CurrentTime.js @@ -16,6 +16,7 @@ function CurrentTime (body, options) { // default options this.defaultOptions = { + rtl: false, showCurrentTime: true, moment: moment, @@ -64,7 +65,7 @@ CurrentTime.prototype.destroy = function () { CurrentTime.prototype.setOptions = function(options) { if (options) { // copy all options that we know - util.selectiveExtend(['showCurrentTime', 'moment', 'locale', 'locales'], this.options, options); + util.selectiveExtend(['rtl', 'showCurrentTime', 'moment', 'locale', 'locales'], this.options, options); } }; @@ -99,7 +100,11 @@ CurrentTime.prototype.redraw = function() { var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss'); title = title.charAt(0).toUpperCase() + title.substring(1); - this.bar.style.left = x + 'px'; + if (this.options.rtl) { + this.bar.style.right = x + 'px'; + } else { + this.bar.style.left = x + 'px'; + } this.bar.title = title; } else { diff --git a/lib/timeline/component/DataAxis.js b/lib/timeline/component/DataAxis.js index 61055666..67af1974 100644 --- a/lib/timeline/component/DataAxis.js +++ b/lib/timeline/component/DataAxis.js @@ -212,11 +212,11 @@ DataAxis.prototype._cleanupIcons = function() { DataAxis.prototype.show = function() { this.hidden = false; if (!this.dom.frame.parentNode) { - if (this.options.orientation === 'left') { + if (this.options.rtl) { this.body.dom.left.appendChild(this.dom.frame); } else { - this.body.dom.right.appendChild(this.dom.frame); + this.body.dom.left.appendChild(this.dom.frame); } } diff --git a/lib/timeline/component/Group.js b/lib/timeline/component/Group.js index 8a09e4ae..c11d7cce 100644 --- a/lib/timeline/component/Group.js +++ b/lib/timeline/component/Group.js @@ -209,8 +209,8 @@ Group.prototype.redraw = function(range, margin, restack) { } else { // no custom order function, lazy stacking - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); if (this.itemSet.options.stack) { // TODO: ugly way to access options... stack.stack(this.visibleItems, margin, restack); } @@ -225,10 +225,9 @@ Group.prototype.redraw = function(range, margin, restack) { // calculate actual size and position var foreground = this.dom.foreground; this.top = foreground.offsetTop; - this.left = foreground.offsetLeft; + this.right = foreground.offsetLeft; this.width = foreground.offsetWidth; resized = util.updateProperty(this, 'height', height) || resized; - // recalculate size of label resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 27aaee38..373a92f3 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -26,8 +26,8 @@ var BACKGROUND = '__background__'; // reserved group id for background items wit */ function ItemSet(body, options) { this.body = body; - this.defaultOptions = { + rtl: false, type: null, // 'box', 'point', 'range', 'background' orientation: { item: 'bottom' // item orientation: 'top' or 'bottom' @@ -96,7 +96,7 @@ function ItemSet(body, options) { // options is shared by this ItemSet and all its items this.options = util.extend({}, this.defaultOptions); - + // options for getting items from the DataSet with the correct type this.itemOptions = { type: {start: 'Date', end: 'Date'} @@ -230,8 +230,8 @@ ItemSet.prototype._create = function(){ // add item on doubletap this.hammer.on('doubletap', this._onAddItem.bind(this)); - this.groupHammer = new Hammer(this.body.dom.leftContainer); + this.groupHammer.on('panstart', this._onGroupDragStart.bind(this)); this.groupHammer.on('panmove', this._onGroupDrag.bind(this)); this.groupHammer.on('panend', this._onGroupDragEnd.bind(this)); @@ -308,7 +308,7 @@ ItemSet.prototype._create = function(){ ItemSet.prototype.setOptions = function(options) { if (options) { // copy all options that we know - var fields = ['type', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap']; + var fields = ['type', 'rtl', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap']; util.selectiveExtend(fields, this.options, options); if ('orientation' in options) { @@ -498,8 +498,14 @@ ItemSet.prototype.getSelection = function() { */ ItemSet.prototype.getVisibleItems = function() { var range = this.body.range.getRange(); - var left = this.body.util.toScreen(range.start); - var right = this.body.util.toScreen(range.end); + + if (this.options.rtl) { + var right = this.body.util.toScreen(range.start); + var left = this.body.util.toScreen(range.end); + } else { + var left = this.body.util.toScreen(range.start); + var right = this.body.util.toScreen(range.end); + } var ids = []; for (var groupId in this.groups) { @@ -512,8 +518,14 @@ ItemSet.prototype.getVisibleItems = function() { for (var i = 0; i < rawVisibleItems.length; i++) { var item = rawVisibleItems[i]; // TODO: also check whether visible vertically - if ((item.left < right) && (item.left + item.width > left)) { - ids.push(item.id); + if (this.options.rtl) { + if ((item.right < left) && (item.right + item.width > right)) { + ids.push(item.id); + } + } else { + if ((item.left < right) && (item.left + item.width > left)) { + ids.push(item.id); + } } } } @@ -552,7 +564,12 @@ ItemSet.prototype.redraw = function() { // recalculate absolute position (before redrawing groups) this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; - this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; + + if (this.options.rtl) { + this.props.right = this.body.domProps.right.width + this.body.domProps.border.right; + } else { + this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; + } // update class name frame.className = 'vis-itemset'; @@ -605,7 +622,11 @@ ItemSet.prototype.redraw = function() { this.dom.axis.style.top = asSize((orientation == 'top') ? (this.body.domProps.top.height + this.body.domProps.border.top) : (this.body.domProps.top.height + this.body.domProps.centerContainer.height)); - this.dom.axis.style.left = '0'; + if (this.options.rtl) { + this.dom.axis.style.right = '0'; + } else { + this.dom.axis.style.left = '0'; + } // check if this component is resized resized = this._isResized() || resized; @@ -1245,8 +1266,15 @@ ItemSet.prototype._onDragStart = function (event) { */ ItemSet.prototype._onDragStartAddItem = function (event) { var snap = this.options.snap || null; - var xAbs = util.getAbsoluteLeft(this.dom.frame); - var x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px + + if (this.options.rtl) { + var xAbs = util.getAbsoluteRight(this.dom.frame); + var x = xAbs - event.center.x + 10; // plus 10 to compensate for the drag starting as soon as you've moved 10px + } else { + var xAbs = util.getAbsoluteLeft(this.dom.frame); + var x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px + } + var time = this.body.util.toTime(x); var scale = this.body.util.getScale(); var step = this.body.util.getStep(); @@ -1267,7 +1295,6 @@ ItemSet.prototype._onDragStartAddItem = function (event) { if (group) { itemData.group = group.groupId; } - var newItem = new RangeItem(itemData, this.conversion, this.options); newItem.id = id; // TODO: not so nice setting id afterwards newItem.data = this._cloneItemData(itemData); @@ -1275,10 +1302,15 @@ ItemSet.prototype._onDragStartAddItem = function (event) { var props = { item: newItem, - dragRight: true, initialX: event.center.x, data: newItem.data }; + + if (this.options.rtl) { + props.dragLeft = true; + } else { + props.dragRight = true; + } this.touchParams.itemProps = [props]; event.stopPropagation(); @@ -1295,7 +1327,13 @@ ItemSet.prototype._onDrag = function (event) { var me = this; var snap = this.options.snap || null; - var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; + + if (this.options.rtl) { + var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.right.width; + } else { + var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; + } + var scale = this.body.util.getScale(); var step = this.body.util.getStep(); @@ -1319,7 +1357,12 @@ ItemSet.prototype._onDrag = function (event) { this.touchParams.itemProps.forEach(function (props) { var current = me.body.util.toTime(event.center.x - xOffset); var initial = me.body.util.toTime(props.initialX - xOffset); - var offset = current - initial; // ms + + if (this.options.rtl) { + var offset = -(current - initial); // ms + } else { + var offset = (current - initial); // ms + } var itemData = this._cloneItemData(props.item.data); // clone the data if (props.item.editable === false) { @@ -1328,29 +1371,47 @@ ItemSet.prototype._onDrag = function (event) { var updateTimeAllowed = me.options.editable.updateTime || props.item.editable === true; - if (updateTimeAllowed) { if (props.dragLeft) { // drag left side of a range item - if (itemData.start != undefined) { - var initialStart = util.convert(props.data.start, 'Date'); - var start = new Date(initialStart.valueOf() + offset); - // TODO: pass a Moment instead of a Date to snap(). (Breaking change) - itemData.start = snap ? snap(start, scale, step) : start; + if (this.options.rtl) { + if (itemData.end != undefined) { + var initialEnd = util.convert(props.data.end, 'Date'); + var end = new Date(initialEnd.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.end = snap ? snap(end, scale, step) : end; + } + } else { + if (itemData.start != undefined) { + var initialStart = util.convert(props.data.start, 'Date'); + var start = new Date(initialStart.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.start = snap ? snap(start, scale, step) : start; + } } } else if (props.dragRight) { // drag right side of a range item - if (itemData.end != undefined) { - var initialEnd = util.convert(props.data.end, 'Date'); - var end = new Date(initialEnd.valueOf() + offset); - // TODO: pass a Moment instead of a Date to snap(). (Breaking change) - itemData.end = snap ? snap(end, scale, step) : end; + if (this.options.rtl) { + if (itemData.start != undefined) { + var initialStart = util.convert(props.data.start, 'Date'); + var start = new Date(initialStart.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.start = snap ? snap(start, scale, step) : start; + } + } else { + if (itemData.end != undefined) { + var initialEnd = util.convert(props.data.end, 'Date'); + var end = new Date(initialEnd.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) + itemData.end = snap ? snap(end, scale, step) : end; + } } } else { // drag both start and end if (itemData.start != undefined) { + var initialStart = util.convert(props.data.start, 'Date').valueOf(); var start = new Date(initialStart + offset); @@ -1366,6 +1427,8 @@ ItemSet.prototype._onDrag = function (event) { // TODO: pass a Moment instead of a Date to snap(). (Breaking change) itemData.start = snap ? snap(start, scale, step) : start; } + + } } } @@ -1706,8 +1769,15 @@ ItemSet.prototype._onAddItem = function (event) { } else { // add item - var xAbs = util.getAbsoluteLeft(this.dom.frame); - var x = event.center.x - xAbs; + if (this.options.rtl) { + var xAbs = util.getAbsoluteRight(this.dom.frame); + var x = xAbs - event.center.x; + } else { + var xAbs = util.getAbsoluteLeft(this.dom.frame); + var x = event.center.x - xAbs; + } + // var xAbs = util.getAbsoluteLeft(this.dom.frame); + // var x = event.center.x - xAbs; var start = this.body.util.toTime(x); var scale = this.body.util.getScale(); var step = this.body.util.getStep(); diff --git a/lib/timeline/component/TimeAxis.js b/lib/timeline/component/TimeAxis.js index 136179b4..d8043521 100644 --- a/lib/timeline/component/TimeAxis.js +++ b/lib/timeline/component/TimeAxis.js @@ -73,7 +73,8 @@ TimeAxis.prototype.setOptions = function(options) { 'maxMinorChars', 'hiddenDates', 'timeAxis', - 'moment' + 'moment', + 'rtl' ], this.options, options); // deep copy the format options @@ -183,7 +184,6 @@ TimeAxis.prototype.redraw = function () { else { this.body.dom.backgroundVertical.appendChild(background) } - return this._isResized() || parentChanged; }; @@ -336,7 +336,13 @@ TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className label.childNodes[0].nodeValue = text; label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; - label.style.left = x + 'px'; + + if (this.options.rtl) { + label.style.left = ""; + label.style.right = x + 'px'; + } else { + label.style.left = x + 'px'; + }; label.className = 'vis-text vis-minor ' + className; //label.title = title; // TODO: this is a heavy operation @@ -370,7 +376,12 @@ TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className //label.title = title; // TODO: this is a heavy operation label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); - label.style.left = x + 'px'; + if (this.options.rtl) { + label.style.left = ""; + label.style.right = x + 'px'; + } else { + label.style.left = x + 'px'; + }; return label; }; @@ -402,10 +413,17 @@ TimeAxis.prototype._repaintMinorLine = function (x, width, orientation, classNam line.style.top = this.body.domProps.top.height + 'px'; } line.style.height = props.minorLineHeight + 'px'; - line.style.left = (x - props.minorLineWidth / 2) + 'px'; + if (this.options.rtl) { + line.style.left = ""; + line.style.right = (x - props.minorLineWidth / 2) + 'px'; + line.className = 'vis-grid vis-vertical-rtl vis-minor ' + className; + } else { + line.style.left = (x - props.minorLineWidth / 2) + 'px'; + line.className = 'vis-grid vis-vertical vis-minor ' + className; + }; line.style.width = width + 'px'; - line.className = 'vis-grid vis-vertical vis-minor ' + className; + return line; }; @@ -436,12 +454,19 @@ TimeAxis.prototype._repaintMajorLine = function (x, width, orientation, classNam else { line.style.top = this.body.domProps.top.height + 'px'; } - line.style.left = (x - props.majorLineWidth / 2) + 'px'; + + if (this.options.rtl) { + line.style.left = ""; + line.style.right = (x - props.majorLineWidth / 2) + 'px'; + line.className = 'vis-grid vis-vertical-rtl vis-major ' + className; + } else { + line.style.left = (x - props.majorLineWidth / 2) + 'px'; + line.className = 'vis-grid vis-vertical vis-major ' + className; + } + line.style.height = props.majorLineHeight + 'px'; line.style.width = width + 'px'; - line.className = 'vis-grid vis-vertical vis-major ' + className; - return line; }; diff --git a/lib/timeline/component/css/item.css b/lib/timeline/component/css/item.css index 4e048885..77140861 100644 --- a/lib/timeline/component/css/item.css +++ b/lib/timeline/component/css/item.css @@ -100,6 +100,17 @@ cursor: pointer; } +.vis-item .vis-delete-rtl { + background: url('img/timeline/delete.png') no-repeat center; + position: absolute; + width: 24px; + height: 24px; + top: -4px; + left: -24px; + cursor: pointer; +} + + .vis-item.vis-range .vis-drag-left { position: absolute; width: 24px; diff --git a/lib/timeline/component/css/timeaxis.css b/lib/timeline/component/css/timeaxis.css index a9c72aa2..a2feddbb 100644 --- a/lib/timeline/component/css/timeaxis.css +++ b/lib/timeline/component/css/timeaxis.css @@ -41,6 +41,11 @@ border-left: 1px solid; } +.vis-time-axis .vis-grid.vis-vertical-rtl { + position: absolute; + border-right: 1px solid; +} + .vis-time-axis .vis-grid.vis-minor { border-color: #e5e5e5; } diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js index 8c58ca42..4aee6f7f 100644 --- a/lib/timeline/component/item/BoxItem.js +++ b/lib/timeline/component/item/BoxItem.js @@ -22,7 +22,7 @@ function BoxItem (data, conversion, options) { height: 0 } }; - + this.options = options; // validate data if (data) { if (data.start == undefined) { @@ -171,29 +171,56 @@ BoxItem.prototype.repositionX = function() { // calculate left position of the box if (align == 'right') { - this.left = start - this.width; - - // reposition box, line, and dot - this.dom.box.style.left = this.left + 'px'; - this.dom.line.style.left = (start - this.props.line.width) + 'px'; - this.dom.dot.style.left = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px'; + if (this.options.rtl) { + this.right = start - this.width; + + // reposition box, line, and dot + this.dom.box.style.right = this.right + 'px'; + this.dom.line.style.right = (start - this.props.line.width) + 'px'; + this.dom.dot.style.right = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px'; + } else { + this.left = start - this.width; + + // reposition box, line, and dot + this.dom.box.style.left = this.left + 'px'; + this.dom.line.style.left = (start - this.props.line.width) + 'px'; + this.dom.dot.style.left = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px'; + } } else if (align == 'left') { - this.left = start; - - // reposition box, line, and dot - this.dom.box.style.left = this.left + 'px'; - this.dom.line.style.left = start + 'px'; - this.dom.dot.style.left = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px'; + if (this.options.rtl) { + this.right = start; + + // reposition box, line, and dot + this.dom.box.style.right = this.right + 'px'; + this.dom.line.style.right = start + 'px'; + this.dom.dot.style.right = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px'; + } else { + this.left = start; + + // reposition box, line, and dot + this.dom.box.style.left = this.left + 'px'; + this.dom.line.style.left = start + 'px'; + this.dom.dot.style.left = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px'; + } } else { // default or 'center' - this.left = start - this.width / 2; - - // reposition box, line, and dot - this.dom.box.style.left = this.left + 'px'; - this.dom.line.style.left = (start - this.props.line.width / 2) + 'px'; - this.dom.dot.style.left = (start - this.props.dot.width / 2) + 'px'; + if (this.options.rtl) { + this.right = start - this.width / 2; + + // reposition box, line, and dot + this.dom.box.style.right = this.right + 'px'; + this.dom.line.style.right = (start - this.props.line.width) + 'px'; + this.dom.dot.style.right = (start - this.props.dot.width / 2) + 'px'; + } else { + this.left = start - this.width / 2; + + // reposition box, line, and dot + this.dom.box.style.left = this.left + 'px'; + this.dom.line.style.left = (start - this.props.line.width / 2) + 'px'; + this.dom.dot.style.left = (start - this.props.dot.width / 2) + 'px'; + } } }; diff --git a/lib/timeline/component/item/Item.js b/lib/timeline/component/item/Item.js index d25fcb43..ea5c7cbb 100644 --- a/lib/timeline/component/item/Item.js +++ b/lib/timeline/component/item/Item.js @@ -23,6 +23,7 @@ function Item (data, conversion, options) { this.dirty = true; this.top = null; + this.right = null; this.left = null; this.width = null; this.height = null; @@ -154,7 +155,12 @@ Item.prototype._repaintDeleteButton = function (anchor) { var me = this; var deleteButton = document.createElement('div'); - deleteButton.className = 'vis-delete'; + + if (this.options.rtl) { + deleteButton.className = 'vis-delete-rtl'; + } else { + deleteButton.className = 'vis-delete'; + } deleteButton.title = 'Delete this item'; // TODO: be able to destroy the delete button diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js index 6238e09e..a05b9982 100644 --- a/lib/timeline/component/item/PointItem.js +++ b/lib/timeline/component/item/PointItem.js @@ -19,10 +19,11 @@ function PointItem (data, conversion, options) { }, content: { height: 0, - marginLeft: 0 + marginLeft: 0, + marginRight: 0 } }; - + this.options = options; // validate data if (data) { if (data.start == undefined) { @@ -117,7 +118,11 @@ PointItem.prototype.redraw = function() { this.props.content.height = dom.content.offsetHeight; // resize contents - dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + if (this.options.rtl) { + dom.content.style.marginRight = 2 * this.props.dot.width + 'px'; + } else { + dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + } //dom.content.style.marginRight = ... + 'px'; // TODO: margin right // recalculate size @@ -126,7 +131,11 @@ PointItem.prototype.redraw = function() { // reposition the dot dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; - dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + if (this.options.rtl) { + dom.dot.style.right = (this.props.dot.width / 2) + 'px'; + } else { + dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + } this.dirty = false; } @@ -164,10 +173,17 @@ PointItem.prototype.hide = function() { PointItem.prototype.repositionX = function() { var start = this.conversion.toScreen(this.data.start); - this.left = start - this.props.dot.width; + if (this.options.rtl) { + this.right = start - this.props.dot.width; + + // reposition point + this.dom.point.style.right = this.right + 'px'; + } else { + this.left = start - this.props.dot.width; - // reposition point - this.dom.point.style.left = this.left + 'px'; + // reposition point + this.dom.point.style.left = this.left + 'px'; + } }; /** @@ -177,7 +193,6 @@ PointItem.prototype.repositionX = function() { PointItem.prototype.repositionY = function() { var orientation = this.options.orientation.item; var point = this.dom.point; - if (orientation == 'top') { point.style.top = this.top + 'px'; } @@ -199,7 +214,7 @@ PointItem.prototype.getWidthLeft = function () { * @return {number} */ PointItem.prototype.getWidthRight = function () { - return this.width - this.props.dot.width; + return this.props.dot.width; }; module.exports = PointItem; diff --git a/lib/timeline/component/item/RangeItem.js b/lib/timeline/component/item/RangeItem.js index e0b36a6f..f49cc134 100644 --- a/lib/timeline/component/item/RangeItem.js +++ b/lib/timeline/component/item/RangeItem.js @@ -18,7 +18,7 @@ function RangeItem (data, conversion, options) { } }; this.overflow = false; // if contents can overflow (css styling), this flag is set to true - + this.options = options; // validate data if (data) { if (data.start == undefined) { @@ -123,7 +123,7 @@ RangeItem.prototype.redraw = function() { this.dirty = false; } - + console.log("redrawing range"); this._repaintDeleteButton(dom.box); this._repaintDragLeft(); this._repaintDragRight(); @@ -168,7 +168,7 @@ RangeItem.prototype.repositionX = function(limitSize) { var parentWidth = this.parent.width; var start = this.conversion.toScreen(this.data.start); var end = this.conversion.toScreen(this.data.end); - var contentLeft; + var contentStartPosition; var contentWidth; // limit the width of the range, as browsers cannot draw very wide divs @@ -183,7 +183,11 @@ RangeItem.prototype.repositionX = function(limitSize) { var boxWidth = Math.max(end - start, 1); if (this.overflow) { - this.left = start; + if (this.options.rtl) { + this.right = start; + } else { + this.left = start; + } this.width = boxWidth + this.props.content.width; contentWidth = this.props.content.width; @@ -192,46 +196,71 @@ RangeItem.prototype.repositionX = function(limitSize) { // So no re-stacking needed, which is nicer for the eye; } else { - this.left = start; + if (this.options.rtl) { + this.right = start; + } else { + this.left = start; + } this.width = boxWidth; contentWidth = Math.min(end - start, this.props.content.width); } - this.dom.box.style.left = this.left + 'px'; + if (this.options.rtl) { + this.dom.box.style.right = this.right + 'px'; + } else { + this.dom.box.style.left = this.left + 'px'; + } this.dom.box.style.width = boxWidth + 'px'; switch (this.options.align) { case 'left': - this.dom.content.style.left = '0'; + if (this.options.rtl) { + this.dom.content.style.right = '0'; + } else { + this.dom.content.style.left = '0'; + } break; case 'right': - this.dom.content.style.left = Math.max((boxWidth - contentWidth), 0) + 'px'; + if (this.options.rtl) { + this.dom.content.style.right = Math.max((boxWidth - contentWidth), 0) + 'px'; + } else { + this.dom.content.style.left = Math.max((boxWidth - contentWidth), 0) + 'px'; + } break; case 'center': - this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; + if (this.options.rtl) { + this.dom.content.style.right = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; + } else { + this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; + } + break; default: // 'auto' // when range exceeds left of the window, position the contents at the left of the visible area if (this.overflow) { if (end > 0) { - contentLeft = Math.max(-start, 0); + contentStartPosition = Math.max(-start, 0); } else { - contentLeft = -contentWidth; // ensure it's not visible anymore + contentStartPosition = -contentWidth; // ensure it's not visible anymore } } else { if (start < 0) { - contentLeft = -start; + contentStartPosition = -start; } else { - contentLeft = 0; + contentStartPosition = 0; } } - this.dom.content.style.left = contentLeft + 'px'; + if (this.options.rtl) { + this.dom.content.style.right = contentStartPosition + 'px'; + } else { + this.dom.content.style.left = contentStartPosition + 'px'; + } } }; @@ -279,6 +308,7 @@ RangeItem.prototype._repaintDragLeft = function () { * @protected */ RangeItem.prototype._repaintDragRight = function () { + console.log("repainting!!!!"); if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { // create and show drag area var dragRight = document.createElement('div'); diff --git a/lib/timeline/optionsTimeline.js b/lib/timeline/optionsTimeline.js index acf93a17..0069a3db 100644 --- a/lib/timeline/optionsTimeline.js +++ b/lib/timeline/optionsTimeline.js @@ -15,7 +15,6 @@ let dom = 'dom'; let moment = 'moment'; let any = 'any'; - let allOptions = { configure: { enabled: {boolean}, @@ -26,6 +25,7 @@ let allOptions = { //globals : align: {string}, + rtl: {boolean, 'undefined': 'undefined'}, autoResize: {boolean}, throttleRedraw: {number}, clickToUse: {boolean}, @@ -143,6 +143,7 @@ let allOptions = { let configureOptions = { global: { align: ['center', 'left', 'right'], + direction: false, autoResize: true, throttleRedraw: [10, 0, 1000, 10], clickToUse: false, diff --git a/lib/util.js b/lib/util.js index a433075a..ce41023c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -599,6 +599,10 @@ exports.getAbsoluteLeft = function (elem) { return elem.getBoundingClientRect().left; }; +exports.getAbsoluteRight = function (elem) { + return elem.getBoundingClientRect().right; +}; + /** * Retrieve the absolute top value of a DOM element * @param {Element} elem A dom element, for example a div