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
  • 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
  • Uncaught NotFoundError: Failed to execute 'removeChild'...

    Uncaught NotFoundError: Failed to execute 'removeChild'...

    TimelineJS3 is throwing the following error when a URL used for a Flickr image 404s:

    Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
    
    VCO.Media.VCO.Class.extend.loadErrorDisplay @ timeline.js:6847
    (anonymous function) @ timeline.js:7200
    ajaxSuccess @ timeline.js:1845
    (anonymous function) @ timeline.js:1892
    events.split.forEach.handler.proxy @ timeline.js:1591
    

    You can see an example of this here - until the client fixes the URLs.

    opened by brettshumaker 9
  • Error:  TypeError: can't assign to property

    Error: TypeError: can't assign to property "date_obj" on "1390-01-01T00:00:00.000Z": not an object

    I only get this error message when opening a timeline with Firefox on MacOS. The date varies on different timelines.

    I tested it on Chromium and it did work. I have not tested it on other devices. The error may result by my configuration of firefox but I am not shure.

    The Error is displayed on the site and not in the console.

    opened by CommandLord 0
  • Mobile YouTube links do not work

    Mobile YouTube links do not work

    While editing a Google Sheet from an iPad, we found out that if you use a mobile YouTube link as media (starts with m.youtube.com) it results in a broken link on the live timeline.

    opened by rmiessle 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
  • Deprecate / remove DOMUtil

    Deprecate / remove DOMUtil

    The DomUtil class is unnecessary. It has three functions which are better handled using standard DOM Methods:

    Calls to DOMUtil.addClass(el, name) could be replaced with el.classList.add(name)

    Calls to DOMUtil.removeClass(el,name) could be replaced with el.classList.remove(name)

    Calls to DOMUtil.hasClass(el,name) could be replaced with el.classList.contains(name)

    Before today, that left only isInHorizontalViewport(element), which is only used in TimeAxis... so instead of leaving it hanging around, I just moved it.

    If I felt like we had a better method of testing UX changes, I might have gone ahead and put in those changes above, at least within DOMUtil but it feels a bit risky.

    This issue's title references deprecation, but I don't even know if that's necessary. Does anyone really use anything that deep in the "public" API for TimelineJS?

    (This issue inspired by some code inspection around integrating #798)

    opened by JoeGermuska 0
  • Setting Colour Flags by Group in v3.8.21

    Setting Colour Flags by Group in v3.8.21

    The code below provides a method of setting the colour of the Marker Flag by group using CSS. It is based on the code given in #489 which was written for v3.6.5.

    The setMarkerGroup Function should be placed after the positionMarkers function and before the updateDisplay function, (Insert Approx Line No 6146 in formated timeline.js file downloaded as zip file bundle from Knightlab).

    Two calls are made to the function and should be inserted following each call to positionMarkers function (drawTimeLine & updateDrawTimeLine - approx lines 6432 & 6449).

    Javascript

                    setMarkerGroup() {
                        function slugify(string) {
                            return string
                                .toString()
                                .trim()
                                .toLowerCase()
                                .replace(/\s+/g, "-")
                                .replace(/[^\w\-]+/g, "")
                                .replace(/\-\-+/g, "-")
                                .replace(/^-+/, "")
                                .replace(/-+$/, "");
                            }
    					for (var i = 0; i < this._markers.length; i++) 
    					{
    					if (this._markers[i].data.group.length > 0 )
                        {
                            var groupClass = 'tl-timemarker-group-' + slugify(this._markers[i].data.group);
    					this._markers[i]._el.container.classList.add(groupClass);
    					}
    					}
    				}
    

    This will support additional CSS for each group to colour the background of each marker flag in the group. If a group is not given a specific background colour, the default will be applied, similarly any events not in a group will use default colour. (from original CSS or a custom CSS for default). Note: The 'slugify' code means that 'group' names are converted to lower case and spaces replaced by '-' (hyphen/minus sign) e.g 'Rest of the World' becomes 'rest-of-the-world' ; (see Git Hub or web for numerous options here)

    CSS

    /* Development - Coloured Flags for each Media Type 
    =================================================*/
    .tl-timemarker-group-video .tl-timemarker-content-container {
    	color: #000000;
    	background-color: rgba(255, 0, 0, .5)   /*  50% opaque red */
    }
    .tl-timemarker-group-audio .tl-timemarker-content-container {
    	background-color: rgba(0, 0, 255, .25)  /*  25% opaque blue */
    }
    .tl-timemarker-group-html .tl-timemarker-content-container {
    	color: #000000;
    	background: rgba(0, 255, 0, .1)        /*  10% opaque green */
    }
    .tl-timemarker-group-image .tl-timemarker-content-container {
    	**color: #000000;**
    	background-color: purple;
    

    The above CSS was used to create the screenshot below which is from the Media Types timeline. Media Types

    Note: Some additional CSS mods on layout.

    opened by Coral-66 4
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 Nov 20, 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 515 Nov 5, 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 74 Nov 4, 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 Nov 25, 2022
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 220 Nov 11, 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 18 Oct 23, 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 68 Nov 20, 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.3k Nov 29, 2022
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 391 Oct 28, 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 Nov 24, 2022
Processing Foundation 18.4k Nov 24, 2022