All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.primefaces.timeline.1-timeline.js Maven / Gradle / Ivy

There is a newer version: 7.0
Show newest version
/**
 * __PrimeFaces Timeline Widget__
 *
 * Timeline is an interactive graph to visualize events in time. Currently uses
 * [vis-timeline](https://github.com/visjs/vis-timeline).
 *
 * @implements {PrimeFaces.widget.ContextMenu.ContextMenuProvider}
 *
 * @typedef PrimeFaces.widget.Timeline.AddCallbackCallback Callback that is invoked when an event was added to this
 * timeline. See also {@link Timeline.addCallback}.
 * @param {import("vis-timeline").TimelineItem} PrimeFaces.widget.Timeline.AddCallbackCallback.item The timeline item
 * that was added.
 *
 * @typedef PrimeFaces.widget.Timeline.ChangedCallbackCallback Callback for when an item of the timeline has changed or
 * was moved. See also {@link Timeline.changedCallback}.
 * @param {import("vis-timeline").TimelineItem} PrimeFaces.widget.Timeline.ChangedCallbackCallback.item  The timeline
 * item that was changed.
 *
 * @typedef PrimeFaces.widget.Timeline.DeleteCallbackCallback Callback for when an item of the timeline was deleted. See
 * also {@link Timeline.deleteCallback}.
 * @param {import("vis-timeline").TimelineItem} PrimeFaces.widget.Timeline.DeleteCallbackCallback.item The timeline item
 * that was deleted.
 *
 * @typedef PrimeFaces.widget.Timeline.TimelineExtender An extender function that may be used modify the settings of the
 * configuration object. The {@link TimelineCfg.opts} are passed directly to vis-timeline. See also
 * {@link TimelineCfg.extender}.
 * @this {PrimeFaces.widget.Timeline} PrimeFaces.widget.Timeline.TimelineExtender
 *
 * @interface {PrimeFaces.widget.Timeline.TimeRange} TimeRange Represents a time range between two points in time.
 * @prop {number | null} TimeRange.start Lower bound of the time range.
 * @prop {number | null} TimeRange.end Upper bound of the time range.
 *
 * @interface {PrimeFaces.widget.Timeline.TimelineBiRange} TimelineBiRange Represents one or two time ranges.
 * @prop {number} TimelineBiRange.startFirst Start of the first time range.
 * @prop {number} TimelineBiRange.endFirst End of the first time range.
 * @prop {number | null} TimelineBiRange.startSecond Start of the second time range.
 * @prop {number | null} TimelineBiRange.endSecond End of the second time range.
 *
 * @prop {PrimeFaces.widget.Timeline.AddCallbackCallback} addCallback Callback that is invoked when an event was added
 * to this timeline.
 * @prop {PrimeFaces.widget.Timeline.ChangedCallbackCallback} changedCallback Callback for when an item of the timeline
 * has changed or was moved.
 * @prop {PrimeFaces.widget.Timeline.DeleteCallbackCallback} deleteCallback Callback for when an item of the timeline
 * was deleted.
 * @prop {boolean} [initiatedByUser] When the timeline is move to the right or left, whether that move was initiated by
 * the user.
 * @prop {import("vis-timeline").Timeline} instance The current vis-timeline instance.
 * @prop {boolean} lazy Whether the lazy loading feature is enabled, which loads events dynamically via AJAX.
 * @prop {number | null} max If restricting the timeline to a certain range, the upper bound.
 * @prop {number | null} min If restricting the timeline to a certain range, the lower bound.
 * @prop {number} pFactor The current preload factor, see {@link TimelineCfg.preloadFactor}.
 * @prop {PrimeFaces.widget.Timeline.TimeRange} rangeLoadedEvents Time range of the events that were loaded.
 *
 * @interface {PrimeFaces.widget.TimelineCfg} cfg The configuration for the {@link  Timeline| Timeline widget}.
 * You can access this configuration via {@link PrimeFaces.widget.BaseWidget.cfg|BaseWidget.cfg}. Please note that this
 * configuration is usually meant to be read-only and should not be modified.
 * @extends {PrimeFaces.widget.DeferredWidgetCfg} cfg
 *
 * @prop {string} cfg.accept `accept` option for the jQueryUI droppable overlay when using drag&drop.
 * @prop {string} cfg.activeClass Active style class for the droppable overlay when using drag&drop.
 * @prop {Date} cfg.currentTime The currently shown time.
 * @prop {import("vis-data/declarations/data-set").DataSetInitialOptions<"id">} cfg.data The data for the vis-timeline
 * data set.
 * @prop {PrimeFaces.widget.Timeline.TimelineExtender} cfg.extender An extender function that may be used modify the
 * settings of this configuration object. The {@link opts} are passed directly to vis-timeline.
 * @prop {import("vis-timeline").DataGroupCollectionType} cfg.groups Optional groups for the events.
 * @prop {string} cfg.hoverClass Hover style class for the droppable overlay when using drag&drop.
 * @prop {boolean} cfg.isMenuPresent Whether a menu should be present for the timeline.
 * @prop {import("vis-timeline").TimelineOptions} cfg.opts The options for the vis timeline.
 * @prop {number} cfg.preloadFactor Preload factor is a positive float value or 0 which can be used for lazy loading of
 * events. When the lazy loading feature is active, the calculated time range for preloading will be multiplied by the
 * preload factor. The result of this multiplication specifies the additional time range which will be considered for
 * the preloading during moving / zooming too. For example, if the calculated time range for preloading is 5 days and
 * the preload factor is `0.2`, the result is `5 * 0.2 = 1` day. That means, 1 day backwards and 1 day onwards will be
 * added to the original calculated time range. The event's area to be preloaded is wider then. This helps to avoid
 * frequently, time-consuming fetching of events. Default value is `0`.
 * @prop {string} cfg.scope `scope` option for the jQuery UI droppable overlay when using drag&drop.
 */
PrimeFaces.widget.Timeline = PrimeFaces.widget.DeferredWidget.extend({
    /**
     * @override
     * @inheritdoc
     * @param {PrimeFaces.PartialWidgetCfg} cfg
     */
    init: function (cfg) {
        this._super(cfg);
        this.cfg = cfg;
        this.id = cfg.id;

        this.lazy = this.hasBehavior("lazyload");
        if (this.lazy) {
            this.min = (typeof this.cfg.opts.min !== 'undefined' ? this.cfg.opts.min.getTime() : null);
            this.max = (typeof this.cfg.opts.max !== 'undefined' ? this.cfg.opts.max.getTime() : null);
            this.pFactor = this.cfg.preloadFactor;

            this.rangeLoadedEvents = {
                start: null,
                end: null
            };
        }

        if (this.cfg.isMenuPresent) {
            this.cfg.opts.onInitialDrawComplete = $.proxy(function() {
                var el = document.getElementById(this.id);
                $(el).find(".timeline-menu").show();
            }, this);
        }

        if (this.cfg.extender) {
            this.cfg.extender.call(this);
        }

        this.renderDeferred();
    },

    /**
     * Creates timeline widget with all initialization steps.
     * @include
     * @override
     * @protected
     * @inheritdoc
     */
    _render: function () {
        // instantiate a timeline object
        var el = document.getElementById(this.id);
        var items = new vis.DataSet(this.cfg.data);

        // #7217 must allow HTML in elements
        this.cfg.opts.xss = {
            disabled: true
        };

        // bind items events
        this._bindItemsEvents();
        if (this.cfg.groups) {
            this.instance = new vis.Timeline(el, items, new vis.DataSet(this.cfg.groups), this.cfg.opts);
        } else {
            this.instance = new vis.Timeline(el, items, this.cfg.opts);
        }

        if (this.cfg.currentTime) {
            this.instance.setCurrentTime(this.cfg.currentTime);
        }

        // bind timeline events
        this._bindTimelineEvents(el);
    },

    /**
     * @override
     * @inheritdoc
     * @param {PrimeFaces.PartialWidgetCfg} cfg
     */
    refresh: function(cfg) {
        // clean up memory
        if (this.instance) {
            this.instance.destroy();
            this.instance = null;
        }

        this._super(cfg);
    },

    /**
     * @override
     * @inheritdoc
     */
    destroy: function() {
        this._super();

        // clean up memory
        if (this.instance) {
            this.instance.destroy();
            this.instance = null;
        }
    },

    /**
     * Sets up all event listeners for the timeline items.
     * @private
     */
    _bindItemsEvents: function () {
        // "add" event
        if (this.cfg.opts.selectable && this.cfg.opts.editable.add && this.hasBehavior("add")) {
            this.cfg.opts.onAdd =
               $.proxy(function(item, callback) {
                    var params = [];
                    if (!item.id) {
                        //item.undefined until https://github.com/visjs/vis-timeline/issues/97 is fixed.
                        item.id = item.undefined;
                    }

                    params.push({
                        name: this.id + '_id',
                        value: item.id
                    });

                    params.push({
                        name: this.id + '_startDate',
                        value: item.start.toISOString()
                    });

                    if (item.end) {
                        params.push({
                            name: this.id + '_endDate',
                            value: item.end.toISOString()
                        });
                    }

                    if (item.group) {
                        params.push({
                            name: this.id + '_group',
                            value: item.group
                        });
                    }

                    this.addCallback = callback;

                    this.callBehavior("add", {params: params, item: item, callback: callback});

                    if (this.addCallback) {
                        this.addCallback(item);
                        this.addCallback = null;
                    }
               }, this);
        }

        // "change" event
        if (this.cfg.opts.selectable && (this.cfg.opts.editable.updateTime || this.cfg.opts.editable.updateGroup) && this.hasBehavior("change")) {
            this.cfg.opts.onMoving =
                $.proxy(function(item, callback) {
                    var params = [];
                    params.push({
                        name: this.id + '_eventId',
                        value: item.id
                    });

                    params.push({
                        name: this.id + '_startDate',
                        value: item.start.toISOString()
                    });

                    if (item.end) {
                        params.push({
                            name: this.id + '_endDate',
                            value: item.end.toISOString()
                        });
                    }

                    if (item.group) {
                        params.push({
                            name: this.id + '_group',
                            value: item.group
                        });
                    }

                    this.callBehavior("change", {params: params, item: item, callback: callback});
                    callback(item);
                }, this);
        }

        // "changed" event
        if (this.cfg.opts.selectable && (this.cfg.opts.editable.updateTime || this.cfg.groups && this.cfg.opts.editable.updateGroup) && this.hasBehavior("changed")) {
            this.cfg.opts.onMove =
                $.proxy(function(item, callback) {
                    var params = [];
                    params.push({
                        name: this.id + '_eventId',
                        value: item.id
                    });

                    params.push({
                        name: this.id + '_startDate',
                        value: item.start.toISOString()
                    });

                    if (item.end) {
                        params.push({
                            name: this.id + '_endDate',
                            value: item.end.toISOString()
                        });
                    }

                    if (item.group) {
                        params.push({
                            name: this.id + '_group',
                            value: item.group
                        });
                    }

                    this.changedCallback = callback;
                    this.callBehavior("changed", {params: params, item: item, callback: callback});
                    if (this.changedCallback) {
                        this.changedCallback(item);
                        this.changedCallback = null;
                    }
                }, this);
        }

        // "edit" event
        if (this.cfg.opts.selectable && this.cfg.opts.editable.updateTime && this.hasBehavior("edit")) {
            this.cfg.opts.onUpdate =
                $.proxy(function(item, callback) {
                    var options = {
                        params: [
                            {name: this.id + '_eventId', value: item.id}
                        ],
                        item: item,
                        callback: callback
                    };

                    this.changedCallback = callback;
                    this.callBehavior("edit", options);
                    if (this.changedCallback) {
                        this.changedCallback(item);
                        this.changedCallback = null;
                    }
                }, this);
        }

        // "delete" event
        if (this.cfg.opts.selectable && this.cfg.opts.editable.remove && this.hasBehavior("delete")) {
            this.cfg.opts.onRemove =
                $.proxy(function(item, callback) {
                    var options = {
                        params: [
                            {name: this.id + '_eventId', value: item.id}
                        ],
                        item: item,
                        callback: callback
                    };

                    this.deleteCallback = callback;
                    this.callBehavior("delete", options);
                    if (this.deleteCallback) {
                        this.deleteCallback(item);
                        this.deleteCallback = null;
                    }
                }, this);
        }
    },

    /**
     * Sets up all event listeners for the timeline's events.
     * @private
     * @param {HTMLElement} el Main element of this widget.
     */
    _bindTimelineEvents: function (el) {
        // "select" event
        if (this.cfg.opts.selectable && this.hasBehavior("select")) {
            this.instance.on('select', $.proxy(function () {
                var selectedId = this.getSelectedId();
                if (!selectedId) {
                    return;
                }

                var options = {
                    params: [
                        {name: this.id + '_eventId', value: selectedId}
                    ]
                };

                this.callBehavior("select", options);
            }, this));
        }

        // "rangechange" event
        if ((this.cfg.opts.zoomable || this.cfg.opts.moveable) && this.hasBehavior("rangechange")) {
            this.instance.on('rangechange', $.proxy(function (properties) {
                var options = {
                    params: [
                        {name: this.id + '_startDate', value: properties.start.toISOString()},
                        {name: this.id + '_endDate', value: properties.end.toISOString()}
                    ],
                    properties: properties
                };

                this.callBehavior("rangechange", options);
            }, this));
        }

        // "rangechanged" event
        if ((this.cfg.opts.zoomable || this.cfg.opts.moveable) && this.hasBehavior("rangechanged")) {
            this.instance.on('rangechanged', $.proxy(function (properties) {
                var options = {
                    params: [
                        {name: this.id + '_startDate', value: properties.start.toISOString()},
                        {name: this.id + '_endDate', value: properties.end.toISOString()}
                    ],
                    properties: properties
                };

                // #5455/#6902 only fire if initiated by user
                if (properties.byUser || this.initiatedByUser) {
                    this.initiatedByUser = false;
                    this.callBehavior("rangechanged", options);
                }
            }, this));
        }

        // "lazyload" event
        if (this.lazy) {
            // initial page load
            this.fireLazyLoading();

            // moving / zooming
            this.instance.on('rangechanged', $.proxy(function () {
                this.fireLazyLoading();
            }, this));
        }

        // register this timeline as droppable if needed
        if (this.cfg.opts.selectable && this.cfg.opts.editable && this.hasBehavior("drop")) {
            var droppableOpts = {tolerance: "pointer"};
            if (this.cfg.hoverClass) {
                droppableOpts.hoverClass = this.cfg.hoverClass;
            }

            if (this.cfg.activeClass) {
                droppableOpts.activeClass = this.cfg.activeClass;
            }

            if (this.cfg.accept) {
                droppableOpts.accept = this.cfg.accept;
            }

            if (this.cfg.scope) {
                droppableOpts.scope = this.cfg.scope;
            }

            droppableOpts.drop = $.proxy(function (evt, ui) {
                var inst = this.getInstance();

                var x = evt.pageX - vis.util.getAbsoluteLeft(inst.dom.center);
                var y = evt.pageY - vis.util.getAbsoluteTop(inst.dom.center);

                var xstart = inst._toTime(x);
                var xend = inst._toTime(x + inst.dom.container.clientWidth / 10); // add 10% of timeline width

                var snap = inst.itemSet.options.snap || null;
                var scale = inst.body.util.getScale();
                var step = inst.body.util.getStep();
                var snappedStart = snap ? new Date(snap(xstart, scale, step)) : xstart;
                var snappedEnd = snap ? new Date(snap(xend, scale, step)) : xend;

                var params = [];
                params.push({
                    name: this.id + '_startDate',
                    value: snappedStart.toISOString()
                });

                params.push({
                    name: this.id + '_endDate',
                    value: snappedEnd.toISOString()
                });

                var group = inst.itemSet.groupFromTarget(evt); // (group may be undefined)

                if (group) {
                    params.push({
                        name: this.id + '_group',
                        value: group.groupId
                    });
                }

                params.push({
                    name: this.id + '_dragId',
                    value: ui.draggable.attr('id')
                });

                // check if draggable is within a data iteration component
                var uiData = ui.draggable.closest(".ui-datatable, .ui-datagrid, .ui-datalist, .ui-carousel, .ui-treetable");
                if (uiData.length > 0) {
                    params.push({
                        name: this.id + '_uiDataId',
                        value: uiData.attr('id')
                    });
                }

                // call the drop listener
                // parameters event and ui can be accessible in "onstart" (p:ajax) via cfg.ext.event and cfg.ext.ui
                // or in "execute" (pe:javascript) via ext.event and ext.ui
                this.callBehavior("drop", {params: params, event: evt, ui: ui});
            }, this);

            // make the timeline droppable
            $(el).droppable(droppableOpts);
        }
    },

   /**
     * @override
     * @inheritdoc
     * @param {PrimeFaces.widget.ContextMenu} menuWidget
     * @param {PrimeFaces.widget.Timeline} targetWidget
     * @param {string} targetId
     * @param {PrimeFaces.widget.ContextMenuCfg} cfg 
     */
    bindContextMenu : function(menuWidget, targetWidget, targetId, cfg) {
        // block default context menu
        targetWidget.instance.on('contextmenu', function (properties) {
           menuWidget.show(properties.event);
           properties.event.preventDefault();
        });
    },

    /**
     * Retrieves the array of current data (events) as an JSON string. This method is useful when you done some changes
     * in timeline and want to send them to server to update the backing model (with `pe:remoteCommand` and
     * `pe:convertTimelineEvents`).
     *
     * @return {string} A JSON string with the current data.
     */
    getData: function () {
        var newData = this.instance.itemsData.map($.proxy(function(item) {
            var newItem = {};
            if (item.hasOwnProperty('content')) {
                newItem.data = item.content;
            }

            if (item.hasOwnProperty('start')) {
                newItem.startDate = item.start.getTime();
            }

            if (item.hasOwnProperty('end') && (item.end != null)) {
                newItem.endDate = item.end.getTime();
            } else {
                newItem.endDate = null;
            }

            if (item.hasOwnProperty('editable')) {
                newItem.editable = item.editable;
            } else {
                newItem.editable = null;
            }

            if (item.hasOwnProperty('group')) {
                newItem.group = item.group;
            } else {
                newItem.group = null;
            }

            if (item.hasOwnProperty('className')) {
                newItem.styleClass = item.className;
            } else {
                newItem.styleClass = null;
            }

            return newItem;
        }, this));

        return JSON.stringify(newData);
    },

    /**
     * Fires event for lazy loading.
     * @private
     */
    fireLazyLoading: function() {
        var range = this.getLazyLoadRange();
        if (range == null) {
            // don't send event
            return;
        }

        var options = {
            params: []
        };

        if (range.startFirst != null && range.endFirst != null) {
            options.params[0] = {name: this.id + '_startDateFirst', value: new Date(range.startFirst).toISOString()};
            options.params[1] = {name: this.id + '_endDateFirst', value: new Date(range.endFirst).toISOString()};
        }

        if (range.startSecond != null && range.endSecond != null) {
            options.params[2] = {name: this.id + '_startDateSecond', value: new Date(range.startSecond).toISOString()};
            options.params[3] = {name: this.id + '_endDateSecond', value: new Date(range.endSecond).toISOString()};
        }

        this.callBehavior("lazyload", options);
    },

    /**
     * Gets time range(s) for events to be lazy loaded.
     *
     * The internal time range for already loaded events will be updated.
     *
     * @private
     * @return {PrimeFaces.widget.Timeline.TimelineBiRange | null} The time range(s) for events to be lazy loaded.
     */
    getLazyLoadRange: function() {
        var visibleRange = this.instance.getWindow();

        if (this.rangeLoadedEvents.start == null || this.rangeLoadedEvents.end == null) {
            // initial load
            var pArea = (visibleRange.end.getTime() - visibleRange.start.getTime()) * this.pFactor;
            this.rangeLoadedEvents.start = Math.round(visibleRange.start.getTime() - pArea);
            this.rangeLoadedEvents.end = Math.round(visibleRange.end.getTime() + pArea);

            if (this.min != null && this.rangeLoadedEvents.start < this.min) {
                this.rangeLoadedEvents.start = this.min;
            }

            if (this.max != null && this.rangeLoadedEvents.end > this.max) {
                this.rangeLoadedEvents.end = this.max;
            }

            return {
                startFirst: this.rangeLoadedEvents.start,
                endFirst: this.rangeLoadedEvents.end,
                startSecond: null,
                endSecond: null
            };
        }

        if ((visibleRange.end.getTime() > this.rangeLoadedEvents.end) &&
            (visibleRange.start.getTime() >= this.rangeLoadedEvents.start)) {
            // moving right
            var startFirstR = this.rangeLoadedEvents.end + 1;
            this.rangeLoadedEvents.end = Math.round(visibleRange.end.getTime() +
                (visibleRange.end.getTime() - visibleRange.start.getTime()) * this.pFactor);

            if (this.max != null && this.rangeLoadedEvents.end > this.max) {
                this.rangeLoadedEvents.end = this.max;
            }

            return {
                startFirst: startFirstR,
                endFirst: this.rangeLoadedEvents.end,
                startSecond: null,
                endSecond: null
            };
        }

        if ((visibleRange.start.getTime() < this.rangeLoadedEvents.start) &&
            (visibleRange.end.getTime() <= this.rangeLoadedEvents.end)) {
            // moving left
            var endFirstL = this.rangeLoadedEvents.start - 1;
            this.rangeLoadedEvents.start = Math.round(visibleRange.start.getTime() -
                (visibleRange.end.getTime() - visibleRange.start.getTime()) * this.pFactor);

            if (this.min != null && this.rangeLoadedEvents.start < this.min) {
                this.rangeLoadedEvents.start = this.min;
            }

            return {
                startFirst: this.rangeLoadedEvents.start,
                endFirst: endFirstL,
                startSecond: null,
                endSecond: null
            };
        }

        if ((visibleRange.start.getTime() < this.rangeLoadedEvents.start) &&
            (visibleRange.end.getTime() > this.rangeLoadedEvents.end)) {
            // zooming out
            var pAreaZ = (visibleRange.end.getTime() - visibleRange.start.getTime()) * this.pFactor;
            var endFirstZ = this.rangeLoadedEvents.start - 1;
            var startSecondZ = this.rangeLoadedEvents.end + 1;
            this.rangeLoadedEvents.start = Math.round(visibleRange.start.getTime() - pAreaZ);
            this.rangeLoadedEvents.end = Math.round(visibleRange.end.getTime() + pAreaZ);

            if (this.min != null && this.rangeLoadedEvents.start < this.min) {
                this.rangeLoadedEvents.start = this.min;
            }

            if (this.max != null && this.rangeLoadedEvents.end > this.max) {
                this.rangeLoadedEvents.end = this.max;
            }

            return {
                startFirst: this.rangeLoadedEvents.start,
                endFirst: endFirstZ,
                startSecond: startSecondZ,
                endSecond: this.rangeLoadedEvents.end
            };
        }

        return null;
    },

    /**
     * Force render the timeline component.
     */
    renderTimeline: function () {
        this.instance.redraw();
    },

    /**
     * Adds an event to the timeline.
     *
     * @param {import("vis-timeline").DataItem} properties Properties for the event.
     */
    addEvent: function (properties) {
        this.instance.itemsData.add(properties);
    },

    /**
     * Changes properties of an existing item in the timeline. The provided parameter properties is an object,
     * and can contain parameters "start" (Date), "end" (Date), "content" (String), "group" (String).
     *
     * @param {import("vis-data/declarations/data-interface").DeepPartial} properties
     * Properties for the event.
     */
    changeEvent: function (properties) {
        this.instance.itemsData.update(properties);
    },

    /**
     * Deletes an existing event.
     *
     * @param {import("vis-timeline").IdType} id Index of the event.
     */
    deleteEvent: function (id) {
        this.instance.itemsData.remove(id);
    },

    /**
     * Deletes all events from the timeline.
     */
    deleteAllEvents: function () {
        this.instance.itemsData.clear();
    },

    /**
     * Updates a group of the timeline, adding it if it does not exists.
     *
     * The provided parameter properties is an object, containing the properties
     *
     * - `id` (string)
     * - `content` (string)
     * - `style` (string)
     * - `className` (string)
     * - `order` (number)
     *
     * Parameters `style`, `className` and `order` are optional.
     *
     * @param {import("vis-data/declarations/data-interface").DeepPartial} properties
     * The event's properties.
     */
    updateGroup: function (properties) {
        var dataset = this.instance.groupsData.getDataSet();
        dataset.update(properties);
    },

    /**
     * Cancels event adding.
     */
    cancelAdd: function () {
        if (this.addCallback) {
            this.addCallback(null);
            this.addCallback = null;
        }
    },

    /**
     * Cancels event changing.
     */
    cancelChange: function () {
        if (this.changedCallback) {
            this.changedCallback(null);
            this.changedCallback = null;
        }
    },

    /**
     * Cancels event deleting.
     */
    cancelDelete: function () {
        if (this.deleteCallback) {
            this.deleteCallback(null);
            this.deleteCallback = null;
        }
    },

    /**
     * Retrieves the properties of a single event. The returned object can contain parameters `start` (Date), `end`
     * (Date), `content` (String), `group` (String).
     *
     * @param {import("vis-timeline").IdType} id 0-based index of the item to retrieve.
     * @return {import("vis-data/declarations/data-interface").FullItem | null}
     * The event at the given index, or `null` when no such events exists at the index.
     */
    getEvent: function (id) {
        return this.instance.itemsData.get(id);
    },

    /**
     * Is the event by given id editable?
     *
     * @param {number} id Index of the event to check.
     * @return {import("vis-timeline").TimelineItemEditableType} An object with properties `updateTime`,
     * `updateGroup` and `remove`.
     */
    getEditable: function(id) {
        return this.instance.itemSet.getItemById(id).editable;
    },

    /**
     * The currently visible time range of the timeline.
     *
     * @return {import("vis-timeline").TimelineWindow} The time range that is currently visible.
     */
    getVisibleRange: function () {
        return this.instance.getWindow();
    },

    /**
     * Set the current visible window. The parameters `start` and `end` can be a date, number, or string.
     *
     * If the parameter value of `start` or `end` is `null`, the parameter will be left unchanged.
     *
     * The parameter `options` must include an `animation` property, which can be a boolean or an object of the form
     * `{duration: number, easingFunction: string}`.
     *
     * If `true` (default) or an object, the range is animated smoothly to the new window. An object can be provided to
     * specify duration and easing function.
     *
     * Default duration is 500 ms, and default easing function is `easeInOutQuad`.
     *
     * Available easing functions:
     *
     * - `linear`
     * - `easeInQuad`
     * - `easeOutQuad`
     * - `easeInOutQuad`
     * - `easeInCubic`,
     * - `easeOutCubic`
     * - `easeInOutCubic`
     * - `easeInQuart`
     * - `easeOutQuart`
     * - `easeInOutQuart`
     * - `easeInQuint`
     * - `easeOutQuint`
     * - `easeInOutQuint`.
     *
     * @param {import("vis-timeline").DateType} start Start of the time range.
     * @param {import("vis-timeline").DateType} end End of the time range.
     * @param {import("vis-timeline").TimelineAnimationOptions} [options] Optional settings.
     * @param {() => void} [callback] Function A callback function can be passed as an optional parameter. This function
     * will be called at the end of the `setVisibleRange` function.
     * @return {undefined} Always returns `undefined`.
     */
    setVisibleRange: function (start, end, options, callback) {
        return this.instance.setWindow(start, end, options, callback);
    },

    /**
     * Moves the timeline the given move factor to the left or right. Start and end date will be adjusted, and the
     * timeline will be redrawn.
     *
     * For example, try a move factor of `0.1` or `-0.1`. The move factor is a number that determines the moving amount.
     * A positive value will move right, a negative value will move left.
     *
     * The parameter `options` must include an `animation` property, which can be a boolean or an object of the form
     * `{duration: number, easingFunction: string}`.
     *
     * If `true` (default) or an object, the range is animated smoothly to the new window. An object can be provided to
     * specify duration and easing function.
     *
     * Default duration is 500 ms, and default easing function is `easeInOutQuad`.
     *
     * Available easing functions:
     *
     * - `linear`
     * - `easeInQuad`
     * - `easeOutQuad`
     * - `easeInOutQuad`
     * - `easeInCubic`,
     * - `easeOutCubic`
     * - `easeInOutCubic`
     * - `easeInQuart`
     * - `easeOutQuart`
     * - `easeInOutQuart`
     * - `easeInQuint`
     * - `easeOutQuint`
     * - `easeInOutQuint`.
     *
     * @param {number} moveFactor The amount to move by. A positive value will move right, a negative value will move
     * left.
     * @param {import("vis-timeline").TimelineAnimationOptions} [options] Optional settings.
     * @param {() => void} [callback] A callback function can be passed as an optional parameter. This function will be
     * called at the end of move operation.
     */
    move: function (moveFactor, options, callback) {
        this.initiatedByUser = true;
        var range = this.instance.getWindow();
        var interval = range.end - range.start;
        var start = range.start.valueOf() + interval * moveFactor;
        var end = range.end.valueOf() + interval * moveFactor;

        this.instance.setWindow(start, end, options, callback);
    },

    /**
     * Zooms the timeline the given zoom factor in or out.
     *
     * The parameter `options` must include an `animation` property, which can be a boolean or an object of the form
     * `{duration: number, easingFunction: string}`.
     *
     * If `true` (default) or an object, the range is animated smoothly to the new window. An object can be provided to
     * specify duration and easing function.
     *
     * Default duration is 500 ms, and default easing function is `easeInOutQuad`.
     *
     * Available easing functions:
     *
     * - `linear`
     * - `easeInQuad`
     * - `easeOutQuad`
     * - `easeInOutQuad`
     * - `easeInCubic`,
     * - `easeOutCubic`
     * - `easeInOutCubic`
     * - `easeInQuart`
     * - `easeOutQuart`
     * - `easeInOutQuart`
     * - `easeInQuint`
     * - `easeOutQuint`
     * - `easeInOutQuint`.
     *
     * @param {number} zoomFactor An number between -1 and +1. If positive zoom in, and if negative zoom out.
     * @param {import("vis-timeline").TimelineAnimationOptions} [options] Optional settings.
     * @param {() => void} [callback] A callback function can be passed as an optional parameter. This function will be
     * called at the end of the zooming operation.
     * @return {undefined} Always returns `undefined`.
     */
    zoom: function (zoomFactor, options, callback) {
        this.initiatedByUser = true;
        if (zoomFactor >= 0) {
            return this.instance.zoomIn(zoomFactor, options, callback);
        } else {
            return this.instance.zoomOut(-zoomFactor, options, callback);
        }
    },

    /**
     * Gets number of events (items in the timeline).
     *
     * @return {number} The number of event in the timeline.
     */
    getNumberOfEvents: function () {
        return this.instance.itemsData.length;
    },

    /**
     * Gets id of the currently selected event.
     *
     * @return {import("vis-timeline").IdType | null} The index of the currently selected event, or `null` if no event
     * is currently selected.
     */
    getSelectedId: function () {
        var selection = this.instance.getSelection();
        if (selection.length) {
            if (selection[0] != undefined) {
                return selection[0];
            }
        }

        return null;
    },

    /**
     * Finds the currently selected event.
     *
     * @return {import("vis-data/declarations/data-interface").FullItem | null}
     * The currently selected event, or `null` when no event is selected.
     */
    getSelectedEvent: function () {
        var id = this.getSelectedId();
        if (id) {
            return this.instance.itemsData.get(id);
        }

        return null;
    },

    /**
     * Selects an event by its ID. The visible range will be moved, so that the selected event is placed in the middle.
     *
     * To unselect all events, pass null as the parameter.
     *
     * @param {import("vis-timeline").IdType | null} id Index of the event to select.
     * When negative, unselects all events.
     */
    setSelection: function(id) {
        if (id) {
            this.instance.setSelection(id);
        } else {
            // unselect all events
            this.instance.setSelection([]);
        }
    },

    /**
     * Gets instance of the timeline object.
     *
     * @return {import("vis-timeline").Timeline} The current timeline instance.
     */
    getInstance: function () {
        return this.instance;
    }
});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy