TimelineJS v3: A Storytelling Timeline built in JavaScript. http://timeline.knightlab.com

Related tags

Timeline TimelineJS3
Overview

TimelineJS3

TimelineJS v3: A Storytelling Timeline built in JavaScript. https://timeline.knightlab.com

Overview

TimelineJS 3 is a rewrite of the popular Timeline JS software. Please be clear that this is software which "does" the same thing, but it isn't the same software, so some details will vary. See https://timeline.knightlab.com for more information.

Contributing to TimelineJS

Are you trying to contribute to or develop TimelineJS3? Here's where you should start.

Getting Started

The official documentation for embedding a Timeline in your page instead of using an embed is now maintained on the main TimelineJS website.

Options

The official documentation for configuration options is now maintained on the main TimelineJS website.

Data file

The official documentation for the JSON format is now maintained on the main TimelineJS website.

API

See API doc here: https://github.com/NUKnightLab/TimelineJS3/blob/master/API.md

Media Types

The official documentation for available media types is now maintained on the main TimelineJS website.

Extending Media Types

  • Create a new class for the media type in source/js/media/type. It's easiest to duplicate an existing one and change the filename and classname.
  • Add the new file to the code-kit compile list inside the main TL.Timeline.js file. Code-kit uses the following language to prepend the file to the compile // @codekit-prepend "media/types/TL.Media.YourMediaTypeName.js";
  • Add a new object to the media_types array in source/js/media/TL.MediaType.js. Make sure to have the correct class name in cls and use match_str as a regex to help timeline figure out what type of media the given url is.
  • If you want icons for the media to show up in the Timeline, then you will also need to add an icon class to source/less/icons/Icons.less that has the name .tl-icon-yourmediatypename.
Comments
  • mobile styling for iOS

    mobile styling for iOS

    using this URL http://cdn.knightlab.com/libs/timeline3/latest/embed/index.html?source=1cWqQBZCkX9GpzFtxCWHoqFXCHg-ylTVUWlnrdYMzKUI&font=Default&lang=en&height=650

    gets this in the iOS simulator on initial load image

    image

    opened by JoeGermuska 19
  • "Error: Timeline configuration has no events" showing up for all timelines, even the template one

    It appears that all of our timelines have stopped working and are now displaying the Timeline configuration has no events error, even though no changes were made to them. While the timelines shown on the Examples page seem to be working, when I made a copy of them, published them and then tried to create new timelines, they would result in the same error. Same goes for the template spreadsheet.

    Any insight into what may be causing this issue?

    Example timeline: https://cdn.knightlab.com/libs/timeline3/latest/embed/index.html?source=15JEMy5hSybqF92vgA7qv_1brJy6ecixMgpnEH-VF9KY&font=Default&lang=en&initial_zoom=2&height=650 Source spreadsheet: https://docs.google.com/spreadsheets/d/15JEMy5hSybqF92vgA7qv_1brJy6ecixMgpnEH-VF9KY/edit#gid=0

    opened by vojtechsedlak 17
  • Feature proposal: Adding a navigation button to quickly navigate to the last timeline entry

    Feature proposal: Adding a navigation button to quickly navigate to the last timeline entry

    First of all, thank you for this wonderful tool!

    I use TimelineJS in my blog to showcase the timeline of the posts I wrote. There is a quick navigation button which immediately sends the user to the first entry in the timeline. It would be nice if there was a similar button which would send the user to the last entry in the timeline.

    opened by jeweljohnsonj 16
  • Add back examples from old site?

    Add back examples from old site?

    I think the breadth of examples we had on the old site really helped tell people how timeline is used across different types of organizations. I'd hate to see that totally lost just because they're using an older version of Timeline.

    question Website 
    opened by zachwise 12
  • How can I use this tool offline?

    How can I use this tool offline?

    First ,this is a wonderful TOOL. Love it. BUT, I am a Chinese Developer, you know the web-is-blocked problem. How can I use this tool offline? Thanks very much

    opened by sannyii 11
  • Uncaught TypeError: this.fire is not a function

    Uncaught TypeError: this.fire is not a function

    Uncaught TypeError: this.fire is not a function
        at HTMLDocument._onDataLoaded (Timeline.js:354)
    

    Hi guys i'm encountering this error , any known issue? Using the latest version. It worked normally but suddenly it broke.

    opened by amouratoglou 10
  • accessibility

    accessibility

    When french blind people read a timeline with its "voice machine", the code says that the text is in English even if it's in french. And the "voice machine" reads the french text with an english accent. Could you make something to solve this problem ? Thanks for blind people. (and sorry for my english...)

    Accessibility 
    opened by fabiennecharraire 10
  • Presentation of Timeline on site

    Presentation of Timeline on site

    If we're not going to have the timeline on the homepage go 100%, we should add a border to it and some margin at the top of it. I had no idea that we were doing a cutover yesterday and this is the first time I'm seeing this!

    google chrome

    Website 
    opened by zachwise 10
  • Implement 'options' for embeds

    Implement 'options' for embeds

    • Language
    • fonts
    • map type (options may be different than before)
    • Start at end
    • Hash bookmarks (may not be implemented in core Timeline either)
    • debug
    • default zoom
    • default start slide (we should consider linking this to "start at end" option, so they are a single user widget)
    opened by JoeGermuska 10
  • Still

    Still "all slides overlapping"

    Hi. We have a problem, which described at https://github.com/NUKnightLab/TimelineJS3/issues/653 it does not always appear. But often. If refresh page - it make OK Timeline. As we can see in the page code: that when the problem is present, the slides do not have a "left" offset.

    Can you help us? Problem page: https://chepetsk.ru/city-history/

    opened by chepetsk 9
  • When events are re-laid out into multiple rows, groups don't adjust correctly.

    When events are re-laid out into multiple rows, groups don't adjust correctly.

    A user sent a test example demonstrating that when one zooms in to a timeline in a way that causes events to be reassigned to new rows, the 'group' rows don't correctly adjust.

    Bug Time axis layout: markers and groups 
    opened by JoeGermuska 9
  • Clearer handling of 400 (and 403?) errors from Google Sheets fetch

    Clearer handling of 400 (and 403?) errors from Google Sheets fetch

    A Zendesk ticket reported a timeline which "stuck" on the "loading" screen.

    Upon closer review, the configuration spreadsheet was not published to the entire web. The assumption is that it was in a private Google Workspace where administrators restrict "publish to the web" to only other members of the workspace.

    In any case, the attempt to fetch the data got a 400 error from the sheets proxy, and failed to handle it. We should trap that error and provide more clear messaging. It's not clear to me if that was the proxy returning 400 intentionally or some other reason but in any case, we should handle it.

    opened by JoeGermuska 0
  • improvements of the display of groups and events

    improvements of the display of groups and events

    • development of the display of events to show the causal links between them
    • parameterization of the display of the colors and the order of the groups
    • see documentations (pdf) in /docs for more details
    opened by mrouan 0
  • hash_bookmark doesn't work with non-ASCII headlines and blank headlines

    hash_bookmark doesn't work with non-ASCII headlines and blank headlines

    We got a support request from the creator of a Chinese-language timeline who wanted to use hash bookmarks.

    They were failing because the slide IDs were different every time the page is loaded.

    When a TimelineConfig object is instantiated, populated, it attempts to ensure that all slides have unique identifiers, mostly to support the hash bookmark feature. If the configuration is JSON based, the IDs can be specified using the unique_id property of a slide object, but for most people using Spreadsheet configuration, there is no direct control over the IDs.

    If no ID is specified, timeline tries to construct one from the headline, but the slugify function filters out non-ASCII characters, leaving an empty "slug" for Chinese-language headlines (among others).

    That function calls out to another, ensureUniqueKey which is designed to do things like add numbers if the same headline is used more than once. If this function gets a blank ID, it generates a random six-character string. Since these have no relationship to the content of the slide, they will almost never come up the same way twice.

    The tl;dr is that for timelines that have slides with non-ascii headlines or blank headlines, the hash_bookmark function will not work, unless the timelines are configured using JSON with explicit IDs specified.

    Probably we need to come up with some kind of hashing function, which could be applied to non-ascii strings, but there would still be an issue with slides which have no headline. I'd probably be OK with saying that people who want to use hash_bookmark and have slides with blank headlines should use the JSON format, since it seems like quite an edge case.

    opened by JoeGermuska 0
  • enable swipe by default?

    enable swipe by default?

    have this in use on a large touchscreen via raspberry pi running chromium as a kiosk. I'm able to set my user agent string to whatever, but that doesn't seem to do the tricks. Feels like there's a dimension test i'm missing too?

    opened by bankeizen 1
  • Timeline.add(data) will cause 0 distance between ticks when the timeline is hidden when .add() is called

    Timeline.add(data) will cause 0 distance between ticks when the timeline is hidden when .add() is called

    When an embedded timeline is rendered but is currently hidden, then when when Timeline.add is called, the distance between timeMakers is set to 0 as the current width of the hidden Timeline is calculated as 0 and therefore the distance between each marker is 0.

    The Tab the timeline embed is rendered in image

    Then navigating to a different tab (Add a memory) hides the timeline, and it is while the timeline is hidden that Timeline.add is called and the width is calculated to be zero.

    so when navigating back to the Timeline the markers are no longer visible as the distance between them is now 0 image

    Timeline.js

    // Add an event
    add(data) {
        var unique_id = this.config.addEvent(data);
    
        var n = this._getEventIndex(unique_id);
        var d = this.config.events[n];
    
        this._storyslider.createSlide(d, this.config.title ? n + 1 : n);
        this._storyslider._updateDrawSlides();
    
        this._timenav.createMarker(d, n);
        this._timenav._updateDrawTimeline(false);
    
        this.fire("added", { unique_id: unique_id });
    }
    

    Calls this._timenav._updateDrawTimeline(false);

    TimeNav.js

    _updateDrawTimeline(check_update) {
        var do_update = false;
    
        // Check to see if redraw is needed
        if (check_update) {
            /* keep this aligned with _getTimeScale or reduce code duplication */
            var temp_timescale = new TimeScale(this.config, {
                display_width: this._el.container.offsetWidth,
                screen_multiplier: this.options.scale_factor,
                max_rows: this.max_rows
    
            });
    
            if (this.timescale.getMajorScale() == temp_timescale.getMajorScale() &&
                this.timescale.getMinorScale() == temp_timescale.getMinorScale()) {
                do_update = true;
            }
        } else {
            do_update = true;
        }
    
        // Perform update or redraw
        if (do_update) {
            this.timescale = this._getTimeScale();
            this.timeaxis.positionTicks(this.timescale, this.options.optimal_tick_width);
            this.positionMarkers();
            this._assignRowsToMarkers();
            this._positionGroups();
            if (this.has_eras) {
                this._positionEras();
            }
            this.updateDisplay();
        } else {
            this._drawTimeline(true);
        }
    
        return do_update;
    
    }
    

    Which then calls this.timeaxis.positionTicks(this.timescale, this.options.optimal_tick_width);

    TimeAxis.js

    _positionTickArray(tick_array, timescale, optimal_tick_width) {
        // Poition Ticks & Handle density of ticks
        if (tick_array[1] && tick_array[0]) {
            var distance = (timescale.getPosition(tick_array[1].date.getMillisecond()) - timescale.getPosition(tick_array[0].date.getMillisecond())),
                fraction_of_array = 1;
    
    
            if (distance < optimal_tick_width) {
                fraction_of_array = Math.round(optimal_tick_width / timescale.getPixelsPerTick());
            }
    
            var show = 1;
    
            for (var i = 0; i < tick_array.length; i++) {
    
                var tick = tick_array[i];
    
                // Poition Ticks
                tick.tick.style.left = timescale.getPosition(tick.date.getMillisecond()) + "px";
                tick.tick_text.innerHTML = tick.display_date;
    
                // Handle density of ticks
                if (fraction_of_array > 1) {
                    if (show >= fraction_of_array) {
                        show = 1;
                        tick.tick_text.style.opacity = 1;
                        tick.tick.className = "tl-timeaxis-tick";
                    } else {
                        show++;
                        tick.tick_text.style.opacity = 0;
                        tick.tick.className = "tl-timeaxis-tick tl-timeaxis-tick-hidden";
                    }
                } else {
                    tick.tick_text.style.opacity = 1;
                    tick.tick.className = "tl-timeaxis-tick";
                }
    
            }
        }
    }
    

    The main issue is that

    display_width: this._el.container.offsetWidth,
    

    this._el.container.offsetWidth can be equal to 0 when the timeline is not currently on screen. Which causes the downstream calculation in the TimeAxis to have a display_width of 0 and end up calculating for timescale.getPosition incorrectly

    tick.tick.style.left = timescale.getPosition(tick.date.getMillisecond()) + "px";
    

    As the display_width is used in the Timescale.js constructor, it will create a _pixel_width of 0

    this._pixel_width = this._screen_multiplier * this._display_width;
    

    which is what _pixels_per_milli is calculated from, and will also be 0

     this._pixels_per_milli = this.getPixelWidth() / this._span_in_millis;
    

    As updates call the this.timeaxis.positionTicks for getting the Ticks positions, this will always be 0 when _pixels_per_milli as display_width is 0

    getPosition(time_in_millis) {
        // be careful using millis, as they won't scale to cosmological time.
        // however, we're moving to make the arg to this whatever value
        // comes from TLDate.getTime() which could be made smart about that --
        // so it may just be about the naming.
        return (time_in_millis - this._earliest) * this._pixels_per_milli
    }
    

    I suggest that TimeNav.js be updated in 2 locations when it creates a new TimeScale in _getTimeScale(), and in_updateDrawTimeline(check_update)

    Changing their respective display_width to either be display_width: this._el.container.offsetWidth, or if that is 0 then default to the this.options.width value so the last known value of the width is then used.

    so replace

    display_width: this._el.container.offsetWidth,
    

    with

    display_width: this._el.container.offsetWidth === 0 ? this.options.width : this._el.container.offsetWidth,
    

    Eg, below is are the functions with this change. I can confirm that this change works for me when implemented for my use case TimeNav.js

    _getTimeScale() {
        /* maybe the establishing config values (marker_height_min and max_rows) should be
        separated from making a TimeScale object, which happens in another spot in this file with duplicate mapping of properties of this TimeNav into the TimeScale options object? */
        // Set Max Rows
        var marker_height_min = 0;
        try {
            marker_height_min = parseInt(this.options.marker_height_min);
        } catch (e) {
            trace("Invalid value for marker_height_min option.");
            marker_height_min = 30;
        }
        if (marker_height_min == 0) {
            trace("marker_height_min option must not be zero.")
            marker_height_min = 30;
        }
        this.max_rows = Math.round((this.options.height - this._el.timeaxis_background.offsetHeight - (this.options.marker_padding)) / marker_height_min);
        if (this.max_rows < 1) {
            this.max_rows = 1;
        }
        return new TimeScale(this.config, {
            display_width: this._el.container.offsetWidth === 0 ? this.options.width : this._el.container.offsetWidth,
            screen_multiplier: this.options.scale_factor,
            max_rows: this.max_rows
    
        });
    }
    

    TimeNav.js

    _updateDrawTimeline(check_update) {
        var do_update = false;
    
        // Check to see if redraw is needed
        if (check_update) {
            /* keep this aligned with _getTimeScale or reduce code duplication */
            var temp_timescale = new TimeScale(this.config, {
                display_width: this._el.container.offsetWidth === 0 ? this.options.width : this._el.container.offsetWidth,
                screen_multiplier: this.options.scale_factor,
                max_rows: this.max_rows
    
            });
    
            if (this.timescale.getMajorScale() == temp_timescale.getMajorScale() &&
                this.timescale.getMinorScale() == temp_timescale.getMinorScale()) {
                do_update = true;
            }
        } else {
            do_update = true;
        }
    
        // Perform update or redraw
        if (do_update) {
            this.timescale = this._getTimeScale();
            this.timeaxis.positionTicks(this.timescale, this.options.optimal_tick_width);
            this.positionMarkers();
            this._assignRowsToMarkers();
            this._positionGroups();
            if (this.has_eras) {
                this._positionEras();
            }
            this.updateDisplay();
        } else {
            this._drawTimeline(true);
        }
    
        return do_update;
    
    }
    

    Please let me know if theres anything thats unclear, or if you see any concerns with what im proposing

    If you are OK with the proposed change I can submit a PR created that includes these changes

    opened by Tyfui90 3
Owner
null
Gantt Gantt Gantt Timeline Schedule Calendar [ javascript gantt, js gantt, projects gantt, timeline, scheduler, gantt timeline, reservation timeline, react gantt, angular gantt, vue gantt, svelte gantt, booking manager ]

Gantt Gantt Gantt Timeline Schedule Calendar [ javascript gantt, js gantt, projects gantt, timeline, scheduler, gantt timeline, reservation timeline, react gantt, angular gantt, vue gantt, svelte gantt, booking manager ]

neuronet.io 2.1k Dec 30, 2022
An open-source visualization library specialized for authoring charts that facilitate data storytelling with a high-level action-driven grammar.

Narrative Chart Introduction Narrative Chart is an open-source visualization library specialized for authoring charts that facilitate data storytellin

Narrative Chart 45 Nov 2, 2022
Portable Activity Timeline that draws the Timeline based on data given in JSON or CSV format

Portable Activity Timeline that draws the Timeline based on data given in JSON or CSV format. By clicking on any activity a detailed modal window is displayed. Initially developed for post incident investigations to get a overview of the situation in an it-environment.

Daniel 5 Oct 11, 2022
Horizontal Timeline 2.0, a fully customisable jQuery plugin to create a dynamic timeline on the horizontal axis.

Horizontal Timeline 2.0 by yCodeTech Twitter @yCodeTech Current Version: 2.0.5.3 Quick Links: Setup | Options | Autoplay | Known Issues | Known Issues

Stu 18 May 29, 2022
📡Usagi-http-interaction: A library for interacting with Http Interaction API

?? - A library for interacting with Http Interaction API (API for receiving interactions.)

Rabbit House Corp 3 Oct 24, 2022
Node.js web server framework for Http/1.1 or Http/2

Node.js web server framework for Http/1.1 or Http/2 Description: This is http framework, you can use it to create Http/1.1 or Http/2 service。 Now let'

Jeremy Yu 10 Mar 24, 2022
A compact JavaScript animation library with a GUI timeline for fast editing.

Timeline.js A compact JavaScript animation library with a GUI timeline for fast editing. Check it out in this example: http://vorg.github.io/timeline.

Marcin Ignac 516 Nov 26, 2022
Distributed/Decentralized p2p Audio Timeline

Hello, Timeline Decentralized p2p audio timeline experiment, pluggable with GUN, PeerJS or any other p2p network. About WebRTC is bandwidth expensive

Meething dWebRTC 7 Apr 25, 2022
Trajectware - timeline-based navigation across computing heritage

Trajectware - timeline-based navigation across computing heritage Goal and Scope The history of calculation, information processig and computation is

NAM-IP 1 Feb 13, 2022
Generate GitHub repositories timeline using a username

ReposTimeline About ReposTimeline is a NextJS app that helps visualize your repositories in a timeline you can easily share. Installation git clone ht

Nazif Barassounon 73 Dec 31, 2022
Timeline/Graph2D is an interactive visualization chart to visualize data in time.

vis-timeline The Timeline/Graph2D is an interactive visualization chart to visualize data in time. The data items can take place on a single date, or

vis.js 1.2k Jan 3, 2023
Timeline maker, based on D3.js

Timeline maker, based on D3.js

Altilunium 7 Sep 6, 2022
You can easily create the horizontal timeline with two types by using this jQuery plugin.

jQuery.Timeline V2 You are able to easily create two types of horizontal timeline with this jQuery plugin. Report bug · Request feature · Blog Table o

ka2 222 Dec 9, 2022
Hides all the @saveToNotion @memdotai @threadreaderapp tweets from your Twitter timeline

This is a Plasmo extension project bootstrapped with plasmo init. Getting Started First, run the development server: pnpm dev # or npm run dev Open yo

null 2 Oct 11, 2022
A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of Deno's native HTTP APIs

A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of Deno's native HTTP APIs with no dependencies. It also has a collection of useful middlewares: log file, serve static, CORS, session, rate limit, token, body parsers, redirect, proxy and handle upload. In "README" there are examples of all the resources. Faster's ideology is: all you need is an optimized middleware manager, all other functionality is middleware.

Henrique Emanoel Viana 19 Dec 28, 2022
A small, but powerful HTTP library for Deno & Deno Deploy, built for convenience and simplicity

Wren Wren is a small, but powerful HTTP library for Deno & Deno Deploy, built for convenience and simplicity. convenient aliases for HTTP responses au

Jakub Neander 69 Dec 12, 2022
🌳 Tiny & elegant JavaScript HTTP client based on the browser Fetch API

Huge thanks to for sponsoring me! Ky is a tiny and elegant HTTP client based on the browser Fetch API Ky targets modern browsers and Deno. For older b

Sindre Sorhus 8.5k Jan 2, 2023
RESTful HTTP client for JavaScript powered web applications

Amygdala is a RESTful HTTP library for JavaScript powered web applications. Simply configure it once with your API schema, and easily do GET, POST, PU

Lincoln Loop 392 Dec 6, 2022
Embedded JavaScript templates -- http://ejs.co

Embedded JavaScript templates Installation $ npm install ejs Features Control flow with <% %> Escaped output with <%= %> (escape function configurable

Matthew Eernisse 6.8k Dec 30, 2022
Processing Foundation 18.6k Jan 1, 2023