GithubHelp home page GithubHelp logo

turbolinks's Introduction

Turbolinks is no longer under active development

Please note that Turbolinks is no longer under active development. It has been superseded by a new framework called Turbo, which is part of the Hotwire umbrella.

Turbolinks

Turbolinks® makes navigating your web application faster. Get the performance benefits of a single-page application without the added complexity of a client-side JavaScript framework. Use HTML to render your views on the server side and link to pages as usual. When you follow a link, Turbolinks automatically fetches the page, swaps in its <body>, and merges its <head>, all without incurring the cost of a full page load.

Turbolinks

Features

  • Optimizes navigation automatically. No need to annotate links or specify which parts of the page should change.
  • No server-side cooperation necessary. Respond with full HTML pages, not partial page fragments or JSON.
  • Respects the web. The Back and Reload buttons work just as you’d expect. Search engine-friendly by design.
  • Supports mobile apps. Adapters for iOS and Android let you build hybrid applications using native navigation controls.

Supported Browsers

Turbolinks works in all modern desktop and mobile browsers. It depends on the HTML5 History API and Window.requestAnimationFrame. In unsupported browsers, Turbolinks gracefully degrades to standard navigation.

Installation

Turbolinks automatically initializes itself when loaded via a standalone <script> tag or a traditional concatenated JavaScript bundle. If you load Turbolinks as a CommonJS or AMD module, first require the module, then call the provided start() function.

Installation Using Ruby on Rails

Your Ruby on Rails application can use the turbolinks RubyGem to install Turbolinks. This gem contains a Rails engine which integrates seamlessly with the Rails asset pipeline.

  1. Add the turbolinks gem, version 5, to your Gemfile: gem 'turbolinks', '~> 5.2.0'
  2. Run bundle install.
  3. Add //= require turbolinks to your JavaScript manifest file (usually found at app/assets/javascripts/application.js).

The gem also provides server-side support for Turbolinks redirection, which can be used without the asset pipeline.

Installation Using npm

Your application can use the turbolinks npm package to install Turbolinks as a module for build tools like webpack.

  1. Add the turbolinks package to your application: npm install --save turbolinks.

  2. Require and start Turbolinks in your JavaScript bundle:

    var Turbolinks = require("turbolinks")
    Turbolinks.start()

The npm package alone does not provide server-side support for Turbolinks redirection. See Following Redirects for details on adding support.

Table of Contents

Navigating with Turbolinks

Building Your Turbolinks Application

Advanced Usage

API Reference

Contributing to Turbolinks

Navigating with Turbolinks

Turbolinks intercepts all clicks on <a href> links to the same domain. When you click an eligible link, Turbolinks prevents the browser from following it. Instead, Turbolinks changes the browser’s URL using the History API, requests the new page using XMLHttpRequest, and then renders the HTML response.

During rendering, Turbolinks replaces the current <body> element outright and merges the contents of the <head> element. The JavaScript window and document objects, and the HTML <html> element, persist from one rendering to the next.

Each Navigation is a Visit

Turbolinks models navigation as a visit to a location (URL) with an action.

Visits represent the entire navigation lifecycle from click to render. That includes changing browser history, issuing the network request, restoring a copy of the page from cache, rendering the final response, and updating the scroll position.

There are two types of visit: an application visit, which has an action of advance or replace, and a restoration visit, which has an action of restore.

Application Visits

Application visits are initiated by clicking a Turbolinks-enabled link, or programmatically by calling Turbolinks.visit(location).

An application visit always issues a network request. When the response arrives, Turbolinks renders its HTML and completes the visit.

If possible, Turbolinks will render a preview of the page from cache immediately after the visit starts. This improves the perceived speed of frequent navigation between the same pages.

If the visit’s location includes an anchor, Turbolinks will attempt to scroll to the anchored element. Otherwise, it will scroll to the top of the page.

Application visits result in a change to the browser’s history; the visit’s action determines how.

Advance visit action

The default visit action is advance. During an advance visit, Turbolinks pushes a new entry onto the browser’s history stack using history.pushState.

Applications using the Turbolinks iOS adapter typically handle advance visits by pushing a new view controller onto the navigation stack. Similarly, applications using the Android adapter typically push a new activity onto the back stack.

Replace visit action

You may wish to visit a location without pushing a new history entry onto the stack. The replace visit action uses history.replaceState to discard the topmost history entry and replace it with the new location.

To specify that following a link should trigger a replace visit, annotate the link with data-turbolinks-action="replace":

<a href="/edit" data-turbolinks-action="replace">Edit</a>

To programmatically visit a location with the replace action, pass the action: "replace" option to Turbolinks.visit:

Turbolinks.visit("/edit", { action: "replace" })

Applications using the Turbolinks iOS adapter typically handle replace visits by dismissing the topmost view controller and pushing a new view controller onto the navigation stack without animation.

Restoration Visits

Turbolinks automatically initiates a restoration visit when you navigate with the browser’s Back or Forward buttons. Applications using the iOS or Android adapters initiate a restoration visit when moving backward in the navigation stack.

Restore visit action

If possible, Turbolinks will render a copy of the page from cache without making a request. Otherwise, it will retrieve a fresh copy of the page over the network. See Understanding Caching for more details.

Turbolinks saves the scroll position of each page before navigating away and automatically returns to this saved position on restoration visits.

Restoration visits have an action of restore and Turbolinks reserves them for internal use. You should not attempt to annotate links or invoke Turbolinks.visit with an action of restore.

Canceling Visits Before They Start

Application visits can be canceled before they start, regardless of whether they were initiated by a link click or a call to Turbolinks.visit.

Listen for the turbolinks:before-visit event to be notified when a visit is about to start, and use event.data.url (or $event.originalEvent.data.url, when using jQuery) to check the visit’s location. Then cancel the visit by calling event.preventDefault().

Restoration visits cannot be canceled and do not fire turbolinks:before-visit. Turbolinks issues restoration visits in response to history navigation that has already taken place, typically via the browser’s Back or Forward buttons.

Disabling Turbolinks on Specific Links

Turbolinks can be disabled on a per-link basis by annotating a link or any of its ancestors with data-turbolinks="false".

<a href="/" data-turbolinks="false">Disabled</a>

<div data-turbolinks="false">
  <a href="/">Disabled</a>
</div>

To reenable when an ancestor has opted out, use data-turbolinks="true":

<div data-turbolinks="false">
  <a href="/" data-turbolinks="true">Enabled</a>
</div>

Links with Turbolinks disabled will be handled normally by the browser.

Building Your Turbolinks Application

Turbolinks is fast because it doesn’t reload the page when you follow a link. Instead, your application becomes a persistent, long-running process in the browser. This requires you to rethink the way you structure your JavaScript.

In particular, you can no longer depend on a full page load to reset your environment every time you navigate. The JavaScript window and document objects retain their state across page changes, and any other objects you leave in memory will stay in memory.

With awareness and a little extra care, you can design your application to gracefully handle this constraint without tightly coupling it to Turbolinks.

Working with Script Elements

Your browser automatically loads and evaluates any <script> elements present on the initial page load.

When you navigate to a new page, Turbolinks looks for any <script> elements in the new page’s <head> which aren’t present on the current page. Then it appends them to the current <head> where they’re loaded and evaluated by the browser. You can use this to load additional JavaScript files on-demand.

Turbolinks evaluates <script> elements in a page’s <body> each time it renders the page. You can use inline body scripts to set up per-page JavaScript state or bootstrap client-side models. To install behavior, or to perform more complex operations when the page changes, avoid script elements and use the turbolinks:load event instead.

Annotate <script> elements with data-turbolinks-eval="false" if you do not want Turbolinks to evaluate them after rendering. Note that this annotation will not prevent your browser from evaluating scripts on the initial page load.

Loading Your Application’s JavaScript Bundle

Always make sure to load your application’s JavaScript bundle using <script> elements in the <head> of your document. Otherwise, Turbolinks will reload the bundle with every page change.

<head>
  ...
  <script src="/application-cbd3cd4.js" defer></script>
</head>

If you have traditionally placed application scripts at the end of <body> for performance reasons, consider using the <script defer> attribute instead. It has widespread browser support and allows you to keep your scripts in <head> for Turbolinks compatibility.

You should also consider configuring your asset packaging system to fingerprint each script so it has a new URL when its contents change. Then you can use the data-turbolinks-track attribute to force a full page reload when you deploy a new JavaScript bundle. See Reloading When Assets Change for information.

Understanding Caching

Turbolinks maintains a cache of recently visited pages. This cache serves two purposes: to display pages without accessing the network during restoration visits, and to improve perceived performance by showing temporary previews during application visits.

When navigating by history (via Restoration Visits), Turbolinks will restore the page from cache without loading a fresh copy from the network, if possible.

Otherwise, during standard navigation (via Application Visits), Turbolinks will immediately restore the page from cache and display it as a preview while simultaneously loading a fresh copy from the network. This gives the illusion of instantaneous page loads for frequently accessed locations.

Turbolinks saves a copy of the current page to its cache just before rendering a new page. Note that Turbolinks copies the page using cloneNode(true), which means any attached event listeners and associated data are discarded.

Preparing the Page to be Cached

Listen for the turbolinks:before-cache event if you need to prepare the document before Turbolinks caches it. You can use this event to reset forms, collapse expanded UI elements, or tear down any third-party widgets so the page is ready to be displayed again.

document.addEventListener("turbolinks:before-cache", function() {
  // ...
})

Detecting When a Preview is Visible

Turbolinks adds a data-turbolinks-preview attribute to the <html> element when it displays a preview from cache. You can check for the presence of this attribute to selectively enable or disable behavior when a preview is visible.

if (document.documentElement.hasAttribute("data-turbolinks-preview")) {
  // Turbolinks is displaying a preview
}

Opting Out of Caching

You can control caching behavior on a per-page basis by including a <meta name="turbolinks-cache-control"> element in your page’s <head> and declaring a caching directive.

Use the no-preview directive to specify that a cached version of the page should not be shown as a preview during an application visit. Pages marked no-preview will only be used for restoration visits.

To specify that a page should not be cached at all, use the no-cache directive. Pages marked no-cache will always be fetched over the network, including during restoration visits.

<head>
  ...
  <meta name="turbolinks-cache-control" content="no-cache">
</head>

To completely disable caching in your application, ensure every page contains a no-cache directive.

Installing JavaScript Behavior

You may be used to installing JavaScript behavior in response to the window.onload, DOMContentLoaded, or jQuery ready events. With Turbolinks, these events will fire only in response to the initial page load, not after any subsequent page changes. We compare two strategies for connecting JavaScript behavior to the DOM below.

Observing Navigation Events

Turbolinks triggers a series of events during navigation. The most significant of these is the turbolinks:load event, which fires once on the initial page load, and again after every Turbolinks visit.

You can observe the turbolinks:load event in place of DOMContentLoaded to set up JavaScript behavior after every page change:

document.addEventListener("turbolinks:load", function() {
  // ...
})

Keep in mind that your application will not always be in a pristine state when this event is fired, and you may need to clean up behavior installed for the previous page.

Also note that Turbolinks navigation may not be the only source of page updates in your application, so you may wish to move your initialization code into a separate function which you can call from turbolinks:load and anywhere else you may change the DOM.

When possible, avoid using the turbolinks:load event to add other event listeners directly to elements on the page body. Instead, consider using event delegation to register event listeners once on document or window.

See the Full List of Events for more information.

Attaching Behavior With Stimulus

New DOM elements can appear on the page at any time by way of Ajax request handlers, WebSocket handlers, or client-side rendering operations, and these elements often need to be initialized as if they came from a fresh page load.

You can handle all of these updates, including updates from Turbolinks page loads, in a single place with the conventions and lifecycle callbacks provided by Turbolinks’ sister framework, Stimulus.

Stimulus lets you annotate your HTML with controller, action, and target attributes:

<div data-controller="hello">
  <input data-target="hello.name" type="text">
  <button data-action="click->hello#greet">Greet</button>
</div>

Implement a compatible controller and Stimulus connects it automatically:

// hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  greet() {
    console.log(`Hello, ${this.name}!`)
  }

  get name() {
    return this.targets.find("name").value
  }
}

Stimulus connects and disconnects these controllers and their associated event handlers whenever the document changes using the MutationObserver API. As a result, it handles Turbolinks page changes the same way it handles any other type of DOM update.

See the Stimulus repository on GitHub for more information.

Making Transformations Idempotent

Often you’ll want to perform client-side transformations to HTML received from the server. For example, you might want to use the browser’s knowledge of the user’s current time zone to group a collection of elements by date.

Suppose you have annotated a set of elements with data-timestamp attributes indicating the elements’ creation times in UTC. You have a JavaScript function that queries the document for all such elements, converts the timestamps to local time, and inserts date headers before each element that occurs on a new day.

Consider what happens if you’ve configured this function to run on turbolinks:load. When you navigate to the page, your function inserts date headers. Navigate away, and Turbolinks saves a copy of the transformed page to its cache. Now press the Back button—Turbolinks restores the page, fires turbolinks:load again, and your function inserts a second set of date headers.

To avoid this problem, make your transformation function idempotent. An idempotent transformation is safe to apply multiple times without changing the result beyond its initial application.

One technique for making a transformation idempotent is to keep track of whether you’ve already performed it by setting a data attribute on each processed element. When Turbolinks restores your page from cache, these attributes will still be present. Detect these attributes in your transformation function to determine which elements have already been processed.

A more robust technique is simply to detect the transformation itself. In the date grouping example above, that means checking for the presence of a date divider before inserting a new one. This approach gracefully handles newly inserted elements that weren’t processed by the original transformation.

Persisting Elements Across Page Loads

Turbolinks allows you to mark certain elements as permanent. Permanent elements persist across page loads, so that any changes you make to those elements do not need to be reapplied after navigation.

Consider a Turbolinks application with a shopping cart. At the top of each page is an icon with the number of items currently in the cart. This counter is updated dynamically with JavaScript as items are added and removed.

Now imagine a user who has navigated to several pages in this application. She adds an item to her cart, then presses the Back button in her browser. Upon navigation, Turbolinks restores the previous page’s state from cache, and the cart item count erroneously changes from 1 to 0.

You can avoid this problem by marking the counter element as permanent. Designate permanent elements by giving them an HTML id and annotating them with data-turbolinks-permanent.

<div id="cart-counter" data-turbolinks-permanent>1 item</div>

Before each render, Turbolinks matches all permanent elements by id and transfers them from the original page to the new page, preserving their data and event listeners.

Advanced Usage

Displaying Progress

During Turbolinks navigation, the browser will not display its native progress indicator. Turbolinks installs a CSS-based progress bar to provide feedback while issuing a request.

The progress bar is enabled by default. It appears automatically for any page that takes longer than 500ms to load. (You can change this delay with the Turbolinks.setProgressBarDelay method.)

The progress bar is a <div> element with the class name turbolinks-progress-bar. Its default styles appear first in the document and can be overridden by rules that come later.

For example, the following CSS will result in a thick green progress bar:

.turbolinks-progress-bar {
  height: 5px;
  background-color: green;
}

To disable the progress bar entirely, set its visibility style to hidden:

.turbolinks-progress-bar {
  visibility: hidden;
}

Reloading When Assets Change

Turbolinks can track the URLs of asset elements in <head> from one page to the next and automatically issue a full reload if they change. This ensures that users always have the latest versions of your application’s scripts and styles.

Annotate asset elements with data-turbolinks-track="reload" and include a version identifier in your asset URLs. The identifier could be a number, a last-modified timestamp, or better, a digest of the asset’s contents, as in the following example.

<head>
  ...
  <link rel="stylesheet" href="/application-258e88d.css" data-turbolinks-track="reload">
  <script src="/application-cbd3cd4.js" data-turbolinks-track="reload"></script>
</head>

Ensuring Specific Pages Trigger a Full Reload

You can ensure visits to a certain page will always trigger a full reload by including a <meta name="turbolinks-visit-control"> element in the page’s <head>.

<head>
  ...
  <meta name="turbolinks-visit-control" content="reload">
</head>

This setting may be useful as a workaround for third-party JavaScript libraries that don’t interact well with Turbolinks page changes.

Setting a Root Location

By default, Turbolinks only loads URLs with the same origin—i.e. the same protocol, domain name, and port—as the current document. A visit to any other URL falls back to a full page load.

In some cases, you may want to further scope Turbolinks to a path on the same origin. For example, if your Turbolinks application lives at /app, and the non-Turbolinks help site lives at /help, links from the app to the help site shouldn’t use Turbolinks.

Include a <meta name="turbolinks-root"> element in your pages’ <head> to scope Turbolinks to a particular root location. Turbolinks will only load same-origin URLs that are prefixed with this path.

<head>
  ...
  <meta name="turbolinks-root" content="/app">
</head>

Following Redirects

When you visit location /one and the server redirects you to location /two, you expect the browser’s address bar to display the redirected URL.

However, Turbolinks makes requests using XMLHttpRequest, which transparently follows redirects. There’s no way for Turbolinks to tell whether a request resulted in a redirect without additional cooperation from the server.

To work around this problem, send the Turbolinks-Location header in the final response to a visit that was redirected, and Turbolinks will replace the browser’s topmost history entry with the value you provide.

The Turbolinks Rails engine sets Turbolinks-Location automatically when using redirect_to in response to a Turbolinks visit.

Redirecting After a Form Submission

Submitting an HTML form to the server and redirecting in response is a common pattern in web applications. Standard form submission is similar to navigation, resulting in a full page load. Using Turbolinks you can improve the performance of form submission without complicating your server-side code.

Instead of submitting forms normally, submit them with XHR. In response to an XHR submit on the server, return JavaScript that performs a Turbolinks.visit to be evaluated by the browser.

If form submission results in a state change on the server that affects cached pages, consider clearing Turbolinks’ cache with Turbolinks.clearCache().

The Turbolinks Rails engine performs this optimization automatically for non-GET XHR requests that redirect with the redirect_to helper.

Setting Custom HTTP Headers

You can observe the turbolinks:request-start event to set custom headers on Turbolinks requests. Access the request’s XMLHttpRequest object via event.data.xhr, then call the setRequestHeader method as many times as you wish.

For example, you might want to include a request ID with every Turbolinks link click and programmatic visit.

document.addEventListener("turbolinks:request-start", function(event) {
  var xhr = event.data.xhr
  xhr.setRequestHeader("X-Request-Id", "123...")
})

API Reference

Turbolinks.visit

Usage:

Turbolinks.visit(location)
Turbolinks.visit(location, { action: action })

Performs an Application Visit to the given location (a string containing a URL or path) with the specified action (a string, either "advance" or "replace").

If location is a cross-origin URL, or falls outside of the specified root (see Setting a Root Location), or if the value of Turbolinks.supported is false, Turbolinks performs a full page load by setting window.location.

If action is unspecified, Turbolinks assumes a value of "advance".

Before performing the visit, Turbolinks fires a turbolinks:before-visit event on document. Your application can listen for this event and cancel the visit with event.preventDefault() (see Canceling Visits Before They Start).

Turbolinks.clearCache

Usage:

Turbolinks.clearCache()

Removes all entries from the Turbolinks page cache. Call this when state has changed on the server that may affect cached pages.

Turbolinks.setProgressBarDelay

Usage:

Turbolinks.setProgressBarDelay(delayInMilliseconds)

Sets the delay after which the progress bar will appear during navigation, in milliseconds. The progress bar appears after 500ms by default.

Note that this method has no effect when used with the iOS or Android adapters.

Turbolinks.supported

Usage:

if (Turbolinks.supported) {
  // ...
}

Detects whether Turbolinks is supported in the current browser (see Supported Browsers).

Full List of Events

Turbolinks emits events that allow you to track the navigation lifecycle and respond to page loading. Except where noted, Turbolinks fires events on the document object.
Note that when using jQuery, the data on the event must be accessed as $event.originalEvent.data.

  • turbolinks:click fires when you click a Turbolinks-enabled link. The clicked element is the event target. Access the requested location with event.data.url. Cancel this event to let the click fall through to the browser as normal navigation.

  • turbolinks:before-visit fires before visiting a location, except when navigating by history. Access the requested location with event.data.url. Cancel this event to prevent navigation.

  • turbolinks:visit fires immediately after a visit starts.

  • turbolinks:request-start fires before Turbolinks issues a network request to fetch the page. Access the XMLHttpRequest object with event.data.xhr.

  • turbolinks:request-end fires after the network request completes. Access the XMLHttpRequest object with event.data.xhr.

  • turbolinks:before-cache fires before Turbolinks saves the current page to cache.

  • turbolinks:before-render fires before rendering the page. Access the new <body> element with event.data.newBody.

  • turbolinks:render fires after Turbolinks renders the page. This event fires twice during an application visit to a cached location: once after rendering the cached version, and again after rendering the fresh version.

  • turbolinks:load fires once after the initial page load, and again after every Turbolinks visit. Access visit timing metrics with the event.data.timing object.

Contributing to Turbolinks

Turbolinks is open-source software, freely distributable under the terms of an MIT-style license. The source code is hosted on GitHub. Development is sponsored by Basecamp.

We welcome contributions in the form of bug reports, pull requests, or thoughtful discussions in the GitHub issue tracker.

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

Building From Source

Turbolinks is written in TypeScript. To build from source, first make sure you have the Yarn package manager installed. Then issue the following commands to build the distributable files in dist/:

$ yarn install
$ yarn build

Include the resulting dist/turbolinks.js file in your application’s JavaScript bundle.

Running Tests

Turbolinks is tested with the Intern testing library.

To run the test suite, follow the instructions for Building From Source above, then run:

$ yarn test

If you are testing changes to the Turbolinks source, remember to run yarn build before each test run. Or, run yarn watch to build automatically as you work.


© 2019 Basecamp, LLC.

turbolinks's People

Contributors

bnjamin avatar brian-kephart avatar brock-noah avatar dependabot[bot] avatar dhh avatar domchristie avatar henrik avatar javan avatar jmbejar avatar joemasilotti avatar johanhalse avatar justin808 avatar maxlap avatar nanaya avatar nateberkopec avatar noeldemartin avatar packagethief avatar petermumford avatar raulbrito avatar reed avatar sstephenson avatar tom-lord avatar tompedals avatar trueheart78 avatar vdel26 avatar ypresto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

turbolinks's Issues

rendering twice via cache + reloaded

Hi,
is there a way to off the render from cache?
because the cached version is rendered while its trying to load the latest version.
its not good when we have a form in place, whereby user may started to type in some input in the text field and the server seems to take sometime to load, and causes the form to entirely rerendered.

yes. im using git version of turbolinks 5
thanks

Writing a library to support both turbolinks classic and new

Is there any harm in setting the data-turbolinks-track when using Turbolinks 5?

Better to omit it for Turbolinks 5?

   <%= env_stylesheet_link_tag(static: 'application_static',
                               hot: 'application_non_webpack',
                               media: 'all',
                               'data-turbolinks-track' => true)  %>

     <%= env_javascript_include_tag(static: 'application_static',
                                    hot: 'application_non_webpack',
                                    'data-turbolinks-track' => true) %>

`link_to :back` does not work

I'm using Turbolinks 5.0.0.beta3 and = link_to 'Back', :back: does not work for me. It generates a link to the current page instead of the previous one.

Re-evaling Javascript..

Is there a way in TL 5.0 to ensure that a block of javascript is re-evaluated at each load?

I'm using gon in my application to write data from the controller to an object in javascript and pages that depend on it throw an error if another page is loaded first which doesn't include the same data.

It seems that classic has a feature data-turbolinks-eval that I don't see in the 5.0 codebase.

Thanks,
Stephen

fixed navigation menu

Hi,

im thinking to render side menu and top bar menu as fix.
am i suppose to use it with data-turbolinks-permanent ?

im thinking to highlight active menu after a page is loaded via javascript.
can anyone suggest a good solution to this?

and is there any header indication that this page is being load via turbolinks?
because if the page is loaded via turbolinks, i will output without the side menu and top menu, making it as content only.
im thinking to also trigger turbolink to replace header title and meta tag after loading.

Sorry im still new with turbolinks 5..'

thank you :)

Does Turbolinks have to check location's extension before visiting it?

Hello! I've got the following issue with Turbolinks: if I try to visit a URL like /chris.martin, Turbolinks doesn't perform a Visit and the whole page gets reloaded. If I remove the dot from this URL (/chrismartin), Turbolinks performs a Visit as it should.

In the Turbolinks source code:

# controller.coffee
locationIsVisitable: (location) ->
  location.isPrefixedBy(@getRootLocation()) and location.isHTML()

# location.coffee
getExtension: ->
  @getLastPathComponent().match(/\.[^.]*$/)?[0]

isHTML: ->
  extension = @getExtension()
  extension is ".html" or not extension?

So my question is: Why does Turbolinks check URL's extension before visiting it? Maybe we need to add an optional attribute like data-turbolinks-extension="false"? Or is there a better way do solve this problem?

EDIT: Now that I think about it, this extension check makes sense (what if I request a raw .js file, for example?). But I think we need an option to disable it on specific links.

Save data and eventHandlers before cache

A few JS libraries set the guard condition based on .data() & if I call their init() function on turbolinks:load it leads to duplicate elements/eventHandlers as associated data is discarded from Turbolinks.Cache
e.g.,

function init(){
  if(!elem.data('initialized')){
    // Create a few hidden/ support elements
    // Initialize event handlers
  }
}

Since this behaviour is different from normal browser behaviour i.e., when I press browser back, both data and eventHandlers are also restored, would it be possible to correct it.

  1. Is it an acceptable approach to use jQuery(element).clone(true).get(0) instead of element.cloneNode(true) when jQuery is defined, so that atleast the users who already use jQuery can take advantage of it?
  2. What is the recommended way to handle above cases - with or without editing the library code?

Original issue I faced - the library checks whether to initialize an element or not based on data("mdproc") which is not set when I visit the same page via browse back/forward

What will be included in the iOS and Android "adapters"?

I'm curious to know what is coming out with the "adapters." I'm super excited to see what's there. Are these projects with a shell that we can compile and run in the simulator? Or is this just a framework that we can build into a shell app?

Any thoughts on timing? I.e., before or after Turbolinks 5 is final?

Capybara testing inline javascript / Is the use of inline javascript recommended?

I have inline javascript in my view that I'm loading with

document.addEventListener("turbolinks:load", function() {});

When I try to write an integration test using capybara (with javascript enabled), this code isn't running for some reason. It is running correctly in if I test it manually, not in an automated test.

Should I really be registering ALL the javascript of my entire app once using $(document).on? Wouldn't this potentially decrease performance with all of the javascript loaded at once, even if its only needed for one page/small use case? I can see the js is being used in multiple places or used commonly, but if I have page specific javascript that a user might not hit very often, I thought that it would make more sense to keep that inline.

But as of now I can't seem to write a feature test that is able to test inline js.

data-turbolinks-permanent does not maintain value after application visit occurs to a page without the element ID

Perhaps this is not the intention of data-turbolinks-permanent, but here is a use case to consider:

  • Page 1 contains a dropdown marked with data-turbolinks-permanent. When the page loads it contains the value "A".
  • User changes the dropdown value to "B".
  • User performs an application visit to Page 2 that does not contain that dropdown.
  • User clicks on the back button. Page 1 is restored from the cache. The value of the dropdown is loaded from the cache as "A", effectively reverting the changes made by the user.

I get that idempotent JS code needs to be reapplied on restoration visits. It would be really nice if there was also an easy way to maintain value of elements that are not intended to be JS-controlled. Applying such value maintenance behavior for elements marked as permanent seems to be a pretty intuitive way to go.

Here is a repo that contains a basic example to play with:
https://github.com/vaharoni/turbolinks5-permanent-issue

Replace a dom element other than body?

Would it be possible to add an option to replace something other than the <body>?

I've built many site where the header / footer needed to stick around and only the content inbetween needs to be replaced. I've tried using data-turbolinks-permanent in beta 3 but it doesn't seem to have the same effect.

I'm thinking something like this:

<head>
  ...
  <meta name="turbolinks-container" content="#main">
</head>

Touch event passes through to the next page and triggers a focus/active event in iOS 9.3

This was similar to something that was happening to me in Turbolinks Classic, where links would actually get triggered on the next page. This is at least not a showstopper like that, but similar: If I tap a link in iOS 9.3 in safari, if there is another element with a focus/active CSS class (common for mobile) in the same location on the screen on the next page, it will get that style applied to it.

Option to set the cache size

I didn't want to use the cache option as it is taking more time to debug the two renders (one from cache and other normal). It would be good to have an option similar to Turbolinks.pagesCached(size) to restrict the size.

Turbolinks beta 4 shows [object Object] in between page loads

After upgrading my Rails 5 app to use turbolinks-source 5.0.0.beta4, [object Object] flashes on the page in between page loads:

turbolinks [object Object]

I've confirmed that this does not occur after downgrading back to turbolinks-source 5.0.0.beta3. No errors show up in the browser console and the issue occurs on both Google Chrome and Firefox (the latest versions).

Events to simulate jQuery $(document).ready()

In previous versions of Turbolinks, you could replace calls to

$(document).ready ->

with

$(document).on 'ready page:load', () ->

which would allow you to bind events to page elements on the both initial page load and subsequent Turbolinks-driven page loads. Worked as you'd expect ready() to on non-Turbolinks sites; you could bind click events to dom elements and whatnot.

Trying to figure out how to do this with Turbolinks 5 -- I tried both:

$(document).on 'ready turbolinks:load', () ->

and, alternately, after adding the compatibility shim:

$(document).on 'ready page:load', () ->

However, neither worked as expected. Specifically, when I'd trigger history back or forward via the browser buttons, the ready callback would fire, which is not what happened in previous Turbolinks version.

What events should I be binding to here? Or should I look at replacing this pattern with something else?

URLs with decimals do not get rendered

This might be a unique case, but I found this difference when migrating an existing project from classic that uses decimals in the URL path. Turbolinks 5 seems to have dropped support for that.

Example use case:

/products/canon-85mm-f-1.2-portrait-lens

The above URL will not render and instead reload.

I found the difference in the regex used to parse the URL extension.

https://github.com/aaronvb/turbolinks/blob/master/src/turbolinks/location.coffee#L34

/\.[^.]*$/

Which will match.2-portrait-lens as the extension.

https://github.com/turbolinks/turbolinks-classic/blob/master/lib/assets/javascripts/turbolinks.coffee#L462

/\.[a-z]+$/

Which will not have a match.

Was this change intended?

I've created a branch using the classic regex and supporting tests: https://github.com/aaronvb/turbolinks/tree/fix_get_extension_for_decimals

Remove dynamically injected CSS

I don't think the current way of injecting the style for the progressbar follows the css/javascript/rails guidelines.

Wouldn't it be better to create a sass file using an $animation-duration variable with a default value so that it can be overriden easily ?

Then change the rails new generator to insert a line in the application.scss file for the turbolink-rails engine like the javascript part in the application.js

Is there any reason to use the current hackish technique I am not seeing ?

Transition page, tinming for transition out.

Hello,
I would like to know if it is possible to add animation with timing before loading the new page in order to have more fluid page transitions.

This is my code, but I do not know where to put the timer .

$(document).on('turbolinks:load', function() {
    $('body').addClass('loaded');
});

$(document).on('turbolinks:fetch', function() {
   $('body').addClass('loading');
});

Thanks

Event naming is inconsistent

The events are:

  • click
  • before-visit
  • visit
  • request-start
  • request-end
  • before-cache
  • before-render
  • render
  • load

The pattern here is:

  • <action>
  • before-<action>

The only exceptions are request-start and request-end. This is the inconsistency.

Wouldn't it be better if they were named before-request and after-request? Or, alternatively, request and receive.

Option to access the newBody in before-cache event callback

This question is slightly related to another issue, where to avoid double initialization - I could add just destroy the initialized widget manually before it is saved to cache. And reinitialize it from scratch next time the page is loaded (from back button or @cache )

But in the following simplified use-case, it would cause flickering/sudden DOM changes:
Lets say I click on a link(coming from external library) in a dropdown inside a permanent element, I want to destroy the current dropdown object only if it is not present in the next page as this is inside permanent element. This would avoid the flickering behaviour or sudden closing of dropdown. (Though I still have to solve some event listeners related issues)

I am currently using existing request-end event and modify the current DOM based on xhr.response - but I would have to construct DOM manually (which would be discarded immediately after the callback) - newBody attribute similar to before-render would be more handy.

Another alternative is to triggering before-render before before-cache, but it may have some side-effects for people already using the library

Angular

Hello everyone,
First thank you for this amazing project.

I had an old turbolinks project with some angularjs.

When I migrated to turbolinks 5, the angular code runs when I load a page from http, but when I click on a link inside my project, the ng-app and ng-controller doesn't do anything.

Thank you for your help.

Rails installation: a simple question

In the installation section it is suggested to Include dist/turbolinks.js in the application’s JavaScript bundle. What a 'JavaScript bundle' is?
I need turbolinks for a Ruby on Rails application. Is the above requirement also necessary for Rails or should I instead refer only to the 'Rails integration' section?

Easy way to keep overflow:scroll element scrolling position during changes?

In the following scenario, is there now a way to preserve the <aside> element's scrollbar position?

HTML:

%body
  %aside{'data-turbolinks-permanent': true}
    %ul
      - @lotsofthingies.each do |thingy|
        -# First, this:
        %li= link_to(thingy)
  %article{'data-turbolinks-temporary': true}
    -# Remote-submitted form be here, which is cool. But then, also this:
    = link_to 'Delete thingy!', @thingy, method: :delete, remote: true

CSS:

body, aside
  height: 100%
body
  overflow-y: hidden
aside
  overflow-y: scroll

I'm aware of turbolinks/turbolinks-classic#528, so my question is mostly whether the data-turbolinks-scroll="false" idea mentioned there has been implemented in some way meanwhile?

On the other hand, using TurboLinks.visit(thingy_url, {scroll: false}) still resets the <aside>'s scrollbar position, so perhaps what I'm trying to achieve doesn't work that way, either...

Javascript not executing

After upgrading to Turbolinks 5, the javascript in the new page is no longer executing. (Only the javascript in the original fully loaded page is executing). Has javascript execution in turbolinks 5 changed?

5.x: POST to create causes URL change even if form errors occured

Running Rails 5.0.0beta3 + TurboLinks 5.x

When I post a form for "create", and the create action re-renders the "new" action, turbolinks is updating the URL to the POST url.

If the user then reloads the page, they end up with the index action rather than back at "new"...

`event.data` is undefined in `turbolinks:request-end`

According to the doc I should be able to access XMLHttpRequest object with event.data.xhr, but event.data is undefined. This is the code which I use:

$(document).on "turbolinks:request-end", (event) ->
  console.log(event.data)

It is the same in Google Chrome, Safari and Firefox with 5.0.0.beta2.

Am I doing something wrong here, or is it a bug?

Upgrade guide

I just finished reading the README (it looks great!), and Turbolinks 5 seems pretty similar to Turbolinks 2. However, I'd love to be able to upgrade by following a simple checklist. Is this something that's on the horizon for a stable release?

Can turbolinks-root be used to include subdomains?

Hi, can we use turbolinks-root options to let Turbolinks intercept links with specific url pattern (might be a Regex - or "All domain and subdomain related to x".
I tried "*" with no success so far
Thanks in advance
Andrea

rack-mini-profiler doesn't display on Turbolinks page loads

I just added the rack-mini-profiler gem and everything displays fine on full page loads (hitting ENTER in the URL bar, or reloading) but on Turbolinks-loaded pages the profiler never shows.

If you navigate a few pages, then do a full page load, the profiler will show an "x 4" or similar letting you know there were four total pages that were captured since the last time you saw it:

image

And then you can expand it to see details for each page:

image

But ideally it should show the profiler on every load.

Is there maybe a combination of data-turbolinks attributes I can drop into my page that'll allow the profiler to show every time? If so I'd happily pull-request some changes to the README to let others know how to do the same.

EDIT

Here's the script include that's appended by rack-mini-profiler before the closing body tag:

<script async type="text/javascript" id="mini-profiler"
  src="/mini-profiler-resources/includes.js?v=12b4b45a3c42e6e15503d7a03810ff33" 
  data-version="12b4b45a3c42e6e15503d7a03810ff33" data-path="/mini-profiler-resources/" 
  data-current-id="v83213qtk53ogdtcmqkt" data-ids="v83213qtk53ogdtcmqkt" 
  data-position="left" data-trivial="false" data-children="false" data-max-traces="10" 
  data-controls="false" data-authorized="true" data-toggle-shortcut="Alt+P" 
  data-start-hidden="false" data-collapse-results="true">
</script>

How to display Progress

Is Turbolinks 5 progress bar enable by default ? In turbolinks-classic,we can set Turbolinks.ProgressBar.enable(); for Rails App ,but for Turbolink 5 ,official document just mention how to overridden default progress bar style, I don't know how to display it for my Rails App , who can help me ,thx. 😊

turbolinks:load and async script loading

Hi guys,
currently i'm trying to implement async script loading like that:

<%= javascript_include_tag 'application', async: true %>

It seems the turbolinks:load event does not fire on initial page load in this case. Everything works fine when navigating through pages.
I have the following code in my application.js:

document.addEventListener("turbolinks:load", function() {
  console.log("turbolinks:load fired!")
});

What am i missing?

Thanks a lot for your help.

turbolinks:load for 1 time

Hi
is there a way to call turbolink for 1time when the page matches the current action?
it seems that when ive this added, it will always call this method no matter which page it is loading..

jQuery(document).on('turbolinks:load', function() {
//do something
});

thank you

is it possible to pause render before everything is loaded?

I'm animating page loads using turbolinks and animate.css.

I've got it setup pretty nicely:

$(document).on('turbolinks:before-render', function(e) {
  var aIn = $('body').data('animation-in');
  $(e.originalEvent.data.newBody).addClass('animated u-noneInteractive ' + aIn)
    .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(){ $('body').removeAttr('class'); });
});

I want to move this 1 step ahead and add loading animation.

So in theory I'd wait for

$(window).load(function(){
  //your code here
});

Is this possible with turbolinks available events?

turblinks not traps link with method DELETE

I'm using rails helper and it looks like this

link_to message, @message, method: :delete

But it performed without turbolinks, even if I specify

link_to message, @message, method: :delete, data: {turbolinks: true}

Is turbolinks skips this kind of links? How can I force this link to perform by turbolinks?

Redirect and reload full page

I have a turbolink-enabled link that, on condition, should be redirected to a fully reloaded page (different layout). When I do:

redirect_to path, turbolinks: false

the request is still a XHR request though and it doesn't like the response which is a full HTML document (the layout gets corrupted). Bug?

An example for this is when a users session is destroyed any link will redirect them to the sign in page, which has a different layout and should have a full page reload.

Turbolinks:load not fired after first page load

In Chrome and Firefox (not Safari) the turbolinks:load event isn't fired when running in production on the first page load.

schermafbeelding 2016-03-30 om 13 50 21

Turbolinks.controller exists and seems to have started, but only after manually running the pageLoaded() function does it trigger my JavaScript. When doing it manually, JavaScript execution is awfully slow in Chrome.

Turbolinks 5.0.0.beta3 and Rails 4.2.6

Make `turbolinks:request-end` event cancelable

(Forgive me if I don't use Rails terminology. I'm coming at this from a Drupal background.)

Some sites want to have multiple themes (different layout + CSS) — e.g. when switching from "the front end" to "the back end". Whenever this happens, you don't want a Turbolinks page load, you want a full page load.

The obvious answer: data-turbolinks="false" should be sufficient.

But it's not always so simple. Sometimes, the theme may be selected/negotiated based on complex conditions. Not just based on the path prefix (/admin/foo -> the admin/ prefix) or some similarly simple condition. But perhaps it's based on permissions, or even time of day (think e-commerce site with time-limited promotions).

In this case, it'd be helpful if the turbolinks:request-end event was cancelable. Upon detecting a theme change (through some header, or the presence of some HTML) in a listener for that event, the event could be canceled and Turbolinks would perform a full page load.

Thanks for considering to support this.

How to hack on Turbolinks

I got this feedback on the turbolinks-source gem and and wanted to share it:

From @sstephenson

Hey Justin,

Easiest way to hack on Turbolinks in a Rails app is to add the src directory to your asset paths in config/application.rb:

module Foo
  class Application < Rails::Application
    config.assets.paths.unshift("/path/to/turbolinks/src")
    # …
  end
end

Make sure you restart the app for the change to take effect.

Error while trying to load the gem 'turbolinks'

Hi,

I just ran into a problem when using the 5.0.0.beta.1 in combination with an middleman project. Is this a gem-related problem or do I have to contact the middleman team?

Edit: I'm using ruby 2.3.0p0 with rubygems 2.5.2.

excpt$ bundle exec middleman
/usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:80:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'turbolinks'. (Bundler::GemRequireError)
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:76:in `block (2 levels) in require'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:72:in `each'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:72:in `block in require'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:61:in `each'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:61:in `require'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bundler-1.11.2/lib/bundler.rb:99:in `require'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/middleman-core-4.1.1/lib/middleman-core/load_paths.rb:33:in `setup_bundler'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/middleman-core-4.1.1/lib/middleman-core/load_paths.rb:15:in `setup_load_paths'
    from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/middleman-cli-4.1.1/bin/middleman:10:in `<top (required)>'
    from /usr/local/var/rbenv/versions/2.3.0/bin/middleman:22:in `load'
    from /usr/local/var/rbenv/versions/2.3.0/bin/middleman:22:in `<main>'

Gemfile:

source 'https://rubygems.org'

gem 'middleman', '~> 4.0'
gem 'middleman-livereload'
gem 'middleman-minify-html'
gem 'middleman-imageoptim'

gem 'nokogiri'

gem 'turbolinks', '~> 5.0.0.beta'

Gemfile.lock

GEM
  remote: https://rubygems.org/
  specs:
    activesupport (4.2.5.1)
      i18n (~> 0.7)
      json (~> 1.7, >= 1.7.7)
      minitest (~> 5.1)
      thread_safe (~> 0.3, >= 0.3.4)
      tzinfo (~> 1.1)
    addressable (2.4.0)
    backports (3.6.8)
    capybara (2.5.0)
      mime-types (>= 1.16)
      nokogiri (>= 1.3.3)
      rack (>= 1.0.0)
      rack-test (>= 0.5.4)
      xpath (~> 2.0)
    coffee-script (2.4.1)
      coffee-script-source
      execjs
    coffee-script-source (1.10.0)
    compass-import-once (1.0.5)
      sass (>= 3.2, < 3.5)
    concurrent-ruby (0.9.2)
    contracts (0.12.0)
    em-websocket (0.5.1)
      eventmachine (>= 0.12.9)
      http_parser.rb (~> 0.6.0)
    erubis (2.7.0)
    eventmachine (1.0.9.1)
    execjs (2.6.0)
    exifr (1.2.4)
    fast_blank (1.0.0)
    fastimage (1.8.1)
      addressable (~> 2.3, >= 2.3.5)
    ffi (1.9.10)
    fspath (2.1.1)
    haml (4.0.7)
      tilt
    hamster (2.0.0)
      concurrent-ruby (~> 0.8)
    hashie (3.4.3)
    htmlcompressor (0.2.0)
    http_parser.rb (0.6.0)
    i18n (0.7.0)
    image_optim (0.20.2)
      exifr (~> 1.1, >= 1.1.3)
      fspath (~> 2.1)
      image_size (~> 1.3)
      in_threads (~> 1.3)
      progress (~> 3.0, >= 3.0.1)
    image_optim_pack (0.2.1.20160119)
      fspath (~> 2.1)
      image_optim (~> 0.19)
    image_size (1.4.2)
    in_threads (1.3.1)
    json (1.8.3)
    kramdown (1.9.0)
    listen (3.0.6)
      rb-fsevent (>= 0.9.3)
      rb-inotify (>= 0.9.7)
    middleman (4.1.1)
      coffee-script (~> 2.2)
      compass-import-once (= 1.0.5)
      haml (>= 4.0.5)
      kramdown (~> 1.2)
      middleman-cli (= 4.1.1)
      middleman-core (= 4.1.1)
      sass (>= 3.4.0, < 4.0)
    middleman-cli (4.1.1)
      thor (>= 0.17.0, < 2.0)
    middleman-core (4.1.1)
      activesupport (~> 4.2)
      addressable (~> 2.4.0)
      backports (~> 3.6)
      bundler (~> 1.1)
      capybara (~> 2.5.0)
      contracts (~> 0.12.0)
      erubis
      execjs (~> 2.0)
      fast_blank
      fastimage (~> 1.8)
      hamster (~> 2.0)
      hashie (~> 3.4)
      i18n (~> 0.7.0)
      listen (~> 3.0)
      padrino-helpers (~> 0.13.0)
      parallel
      rack (>= 1.4.5, < 2.0)
      sass (>= 3.4)
      tilt (~> 1.4.1)
      uglifier (~> 2.6)
    middleman-imageoptim (0.2.1)
      image_optim (~> 0.20.2)
      image_optim_pack (~> 0.2.1)
      middleman-core (>= 3.1)
    middleman-livereload (3.4.6)
      em-websocket (~> 0.5.1)
      middleman-core (>= 3.3)
      rack-livereload (~> 0.3.15)
    middleman-minify-html (3.4.1)
      htmlcompressor (~> 0.2.0)
      middleman-core (>= 3.2)
    mime-types (3.0)
      mime-types-data (~> 3.2015)
    mime-types-data (3.2015.1120)
    mini_portile2 (2.0.0)
    minitest (5.8.4)
    nokogiri (1.6.7.2)
      mini_portile2 (~> 2.0.0.rc2)
    padrino-helpers (0.13.1)
      i18n (~> 0.6, >= 0.6.7)
      padrino-support (= 0.13.1)
      tilt (~> 1.4.1)
    padrino-support (0.13.1)
      activesupport (>= 3.1)
    parallel (1.6.1)
    progress (3.1.1)
    rack (1.6.4)
    rack-livereload (0.3.16)
      rack
    rack-test (0.6.3)
      rack (>= 1.0)
    rb-fsevent (0.9.7)
    rb-inotify (0.9.7)
      ffi (>= 0.5.0)
    sass (3.4.21)
    thor (0.19.1)
    thread_safe (0.3.5)
    tilt (1.4.1)
    turbolinks (5.0.0.beta1)
      turbolinks-source
    turbolinks-source (5.0.0.beta1.1)
    tzinfo (1.2.2)
      thread_safe (~> 0.1)
    uglifier (2.7.2)
      execjs (>= 0.3.0)
      json (>= 1.8.0)
    xpath (2.0.0)
      nokogiri (~> 1.3)

PLATFORMS
  ruby

DEPENDENCIES
  middleman (~> 4.0)
  middleman-imageoptim
  middleman-livereload
  middleman-minify-html
  nokogiri
  turbolinks (~> 5.0.0.beta)

BUNDLED WITH
   1.11.2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.