• Stars
    star
    836
  • Rank 54,534 (Top 2 %)
  • Language
    JavaScript
  • Created over 14 years ago
  • Updated almost 12 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A jQuery plugin to facilitate conveniently working with local storage

jQuery Offline Plugin

Web applications that wish to work robustly in flaky or offline scenarios can use client-side persistence to serve stale data while transparently trying to reconnect for more up-to-date data if possible.

In a mobile scenario, the user may consider himself “connected” when in fact he has dropped out of connectivity for a moment (for instance, he may have gone under a tunnel). Because of this, and because latency on mobile devices can be quite high, a well-behaved mobile web application (or even simple website) will serve up content out of a local cache, so the user can see it quickly, before trying to make a connection to retrieve new content.

This completely eliminates the fear of pressing the back button on mobile devices, since the user will be able to see the “index” page on a content site, for instance, even if their connection has temporarily dropped.

The jQuery offline plugin provides an easy mechanism for retrieving JSON data from a remote server, and then caching it. Subsequent requests for the same URL will retrieve the data from the cache, rather than the remote server.

If the user is online, the plugin will transparently request new content from the remote server, firing the callback again if the content has changed. If the user is offline, the plugin will request the data from the remote server for the most recent request when the user comes back online.

jQuery Offline uses the HTML5 localStorage API for persistence. You can use the same API for browsers that do not support localStorage, jQuery Offline will simply fall back to making a request to the server each time. As a result jQuery.retrieveJSON is a portable way to make a request for JSON that should be cached if possible.

For more information on the basic strategy used here and the rationale for it, check out Rack::Offline, starting from “Application Cache”.

Recommended With

jQuery Offline can be used standalone with jQuery 1.4.2 and above. However, it is best used in conjunction with Rack::Offline and jquery-tmpl.

Rack::Offline automates the process of generating a cache manifest, and jquery-tmpl automates the process of taking a JavaScript object retrieved via jQuery.retrieveJSON and making HTML out of it.

Note that neither iPhone OS 3.1 and earlier nor jQuery have native JSON serialization tools. You can grab json.js from the lib directory of this repository. It’s a small library, and it will not override the native serialization and deserialization if they exist (such as on recent versions of Firefox, Safari, and iPhone OS 3.2 and later).

Usage

You can find jQuery Offline in the lib directory of this repository.

jQuery Offline provides two methods:

jQuery.retrieveJSON

jQuery.retrieveJSON("/url", {data: "toSend"}, function(json, status, data) {
  // json will be the same whether or not the cache was hit
  // status will be either "success" or "cached"
})

jQuery.retrieveJSON has the same API as jQuery.getJSON with one exception: if the data is available in the cache, the callback will be called twice. First, it will be called with the object retrieved from the cache with the status “cached”. Next, once the JSON request succeeds, it will be called with the object returned from the Ajax request with the status “success”.

Returning false from the callback when the status is cached will cause jQuery Offline to skip the Ajax request. You can use this if you don’t want to refresh the content if there is local content available.

If the contents come from the cache, the third parameter will be an object containing the time that jQuery Offline originally cached the content: { cachedAt: originalTime }.

If the Ajax request was made after a successful hit from the cache, jQuery Offline will make the third parameter to the function { cachedAt: originalTime, retrievedAt: timeRetrieved }. The originalTime is the original time that the item was put into the cache. The timeRetrieved is the time that the data was originally retrieved from the cache (in this session). You might use this third parameter to alert the user that a change is about to happen, if such a change would be particularly jarring.

Note that this third parameter is also supplied for a cache hit; if you want to determine whether a particular response is a follow-up to a cache hit, use: if( status == "success" && data ).

Additionally, jQuery.retrieveJSON will not make a new request if a request is in-process for a particular URL/query-string combination.

jQuery.clearJSON

jQuery.clearJSON takes the same first two parameters as jQuery.retrieveJSON and clears the associated key. You can use this function to forcibly purge the cache for a particular URL and set of data. In general, you should not have to do this, as jQuery Offline will always make a follow-up request for content from the server if possible.

Suggested Strategy for Offline Apps

You should use jQuery Offline in combination with an HTML5 cache manifest and jquery-tmpl for the best effect. An example follows.

First, the HTML:

<html manifest="application.manifest">
  <head>
    <link rel="stylesheet" href="/stylesheets/application.css" />
    <script src="/javascripts/jquery.js"></script>
    <script src="/javascripts/template.jquery.js"></script>
    <script src="/javascripts/jquery.offline.js"></script>
    <script id="articleTemplate" type="text/html">
      {{each(article) articles}}
      <article>
        <header>
          <h1>${article.title}</h1>
          <h2>By ${article.byline}</h2>
        </header>
        ${article.body}
      </article>
      {{/each}}
    </script>
  </head>
  <body>
    <div id="loading"><img src="loading.png" /></div>
    <header>
      <img src="masthead.png" />
      <nav><ul>
        <li>Main List</li>
        <li>Recommended</li>
      </ul></nav>
    </header>
    <div id="articles">
    </div>
    <footer>
      Copyright Me, Inc.
    </footer>
  </body>
</html>

And the JavaScript using jquery-tmpl and jQuery Offline:

jQuery(document).ready(function($) {
  // Since jQuery.retrieveJSON delegates to jQuery's Ajax
  // to make requests, we can just set up normal jQuery
  // Ajax listeners.
  $("#loading").ajaxStart(function() { $(this).show(); });
  $("#loading").ajaxStop(function() {  $(this).hide(); });

  var updateArticles = function(callback) {
    $.retrieveJSON("/article_list.json", function(json, status) {
      var content = $("#articleTemplate").render( json );
      $("#articles").empty().append(content);

      // If this *isn't* a cache hit, but rather a
      // successful Ajax request, queue an update task
      // for five minutes from now.
      if( status == "success" ) { setTimeout( callback, 300000 ); }
    });
  };

  // In five minutes, kick off a background request for
  // more data. If the user is online, it will be processed
  // immediately. If the user is not, it will queue the
  // request for when the user comes online
  setTimeout(function periodicUpdater() {
    // Pass in this function as the callback to updateArticles
    updateArticles(periodicUpdater);
  }, 300000)

  // Immediately try to retrieve the data. If the cached
  // data is available, it will be used.
  //
  // If the user is online, it will kick off a request for
  // updated content in the background. If not, it will
  // queue the request for later.
  updateArticles();
});

Because of the architecture of jQuery Offline, you can call $.retrieveJSON whether or not the user is online, and the plugin will either make an immediate request or kick off a request when the user comes back online. This also means you can kick off a timer for a request for new content in the callback to jQuery.retrieveJSON. If the user is online, it’ll get new content every N minutes, like clockwork. If the user is offline, jQuery Offline will simply wait until the user comes back online to make the request.

The above JavaScript is roughly equivalent to the JavaScript strategy I used in Rack::Offline, but most of the dirty work is handled for you.

Tests

jQuery Offline has a test suite that tests functionality in browsers with localStorage and browsers without localStorage. To run the tests, you will need Ruby on your system. Then, follow these instructions in a checked out copy of this app:

$ gem install bundler
$ bundle install
$ bundle exec rackup &
$ open http://localhost:9292/index.html # to run the localStorage-enabled tests
$ open http://localhost:9292/index-fallback.html # to run the localStorage-disabled tests

jQuery Offline also passes JSLint.

TODO

  • Remember the time that the original content was cached; then provide it to follow-up calls
  • Provide better fallback for browsers without localStorage
  • Provide more integrated support for jquery-templ?
  • Deal with error messages because the cache is full
  • LRU + preferred key algorithm
  • Add TTL to the caching mechanism (each key or global?)

More Repositories

1

javascript-decorators

2,397
star
2

rack-offline

A Rack and Rails plugin for building offline web applications
Ruby
670
star
3

moneta

a unified interface to key/value stores
Ruby
476
star
4

merb-core

Merb Core: All you need. None you don't.
Ruby
437
star
5

bundler

Ruby
408
star
6

merb

master merb branch
Ruby
339
star
7

starbeam-historical

TypeScript
269
star
8

artifice

Replaces Net::HTTP with a subclass that routes all requests to a Rack application
Ruby
216
star
9

merb-plugins

Merb Plugins: Even more modules to hook up your Merb installation
JavaScript
208
star
10

merb-more

Merb More: The Full Stack. Take what you need; leave what you don't.
187
star
11

minispade

JavaScript
175
star
12

textmate

Command-line package manager for textmate
Ruby
160
star
13

rust-activesupport

Small demos that illustrate Rust's expressiveness
Rust
132
star
14

rake-pipeline-web-filters

Ruby
116
star
15

newgem-template

A basic template for building a brand new gem
Ruby
106
star
16

guides

Build your own Rails guides
JavaScript
92
star
17

javascript-private-state

A proposal for private state in JavaScript (Stage 0)
92
star
18

jspec

A JavaScript BDD Testing Library
JavaScript
83
star
19

mio-book

62
star
20

dbmonster

A demo of dbmon running on the idempotent-rerender branches of Ember and HTMLBars
JavaScript
61
star
21

handlebars-site

HTML
56
star
22

net-http

Ruby
43
star
23

rust-civet

A Rust web server
Rust
43
star
24

osx-window-sizing

AppleScripts to resize windows simply
42
star
25

js_module_transpiler

JS Module Transpiler is an experimental compiler that allows you to write your JavaScript using a subset of the current ES6 module syntax, and compile it into AMD modules (and soon, CommonJS modules)
Ruby
42
star
26

irb2

Ruby
37
star
27

hammer.rs

An option parsing library that deserializes flags into structs
Rust
37
star
28

asdf

Make the current directory available on port 9292
Ruby
35
star
29

parsejs

JavaScript
34
star
30

railsnatra

Ruby
32
star
31

benchwarmer

Prettier Benchmarking for Ruby
Ruby
32
star
32

muse

A library that can create HTML or PDF books from an extended Markdown format
Ruby
29
star
33

rust-bridge

Ruby
27
star
34

rust-arel

An in-progress port of the Ruby SQL building library arel
Rust
25
star
35

vigilo

A lightweight, simple API for watching the file system
Ruby
25
star
36

rails_assets

temporary home of Rails 3.1 assets until it's moved into master (in short order)
Ruby
24
star
37

dm-adapters

DataMapper Adapters
Ruby
22
star
38

ember-next-experiments

Shell
19
star
39

merb-extlib

Ruby core extensions library extracted from Merb core.
Ruby
19
star
40

experimental-amber-todos

The SproutCore todos tutorial using the single-file alpha Amber distribution
JavaScript
18
star
41

looper

Static analysis tools for ES6
JavaScript
16
star
42

rufo

a Ruby bridge to flying saucer
Ruby
16
star
43

ruby-spidermonkey

A Ruby Binding to Spidermonkey
C
15
star
44

shimmer

An experimental attempt to restructure Glimmer's concepts around a smaller set of primitives.
TypeScript
15
star
45

jquery-governance

Ruby
15
star
46

language-reporting

Rust
13
star
47

argon

Rust
13
star
48

chainable_compare

Ruby
12
star
49

net2-reactor

Ruby
12
star
50

hbs-parser-next

Messing around with a combinator-based approach to parser while stuck at home
TypeScript
12
star
51

prometheus

11
star
52

everafter

An experiment with making the Glimmer reactivity system more general
TypeScript
11
star
53

github-issues-demo

The code for the Github Issues demo I presented at EmberCamp UK
CSS
11
star
54

alexandria

Ruby bindings to the Google Data API
Ruby
11
star
55

rails-simple-benches

Some Rails benches that work across Rails versions and can be used to compare perf progress
Ruby
10
star
56

html5-parser

JavaScript
10
star
57

Unix.rb

C
10
star
58

sparta

Javascript running on Rubinius VM
Ruby
10
star
59

polaris-sketchwork

A collection of proposals that I (@wycats) am working on fleshing out for inclusion in Polaris.
JavaScript
10
star
60

rails-extension-tutorial

Ruby
9
star
61

rails-benches

Application benchmarks to confirm that optimization impact real-world performance
9
star
62

merb_dm_rest

A HTTP DataMapper bridge served via Merb
Ruby
9
star
63

syntax-highlighter

A web syntax highlighter to make it easy to write code to be pasted into keynote
JavaScript
9
star
64

nnhistory

HTML
9
star
65

rails2_on_rails3

Ruby
9
star
66

abbot-from-scratch

Ruby
9
star
67

jetty-rb

Ruby
9
star
68

allocation_counter

Ruby
8
star
69

active_params

8
star
70

software-patent-petition

8
star
71

w3c-dom

A W3C-compliant DOM written on top of libxml2 (very early stages)
Ruby
8
star
72

jsmodules

CSS
8
star
73

sproutcore-chrome-extension

A Chrome DevTools extension for SproutCore
JavaScript
8
star
74

indexeddb-experiment

This is an experiment building an IndexedDB adapter for Ember. It is very experimental and is tracking changes we are making in ember-data, so it is also unstable.
JavaScript
8
star
75

proc-macro-workshop

Rust
8
star
76

ember-future

Some experiments
JavaScript
7
star
77

rails-api

Ruby
7
star
78

cargo-website

CSS
7
star
79

jquery-ui-sproutcore20

JavaScript
7
star
80

modularity_olympics

My entry to Nick Kallen's modularity olympics (using the Middleware pattern as seen in Rack)
Ruby
7
star
81

simple-callbacks-refactor

Quick demo of how to make callbacks faster
Ruby
7
star
82

joy-ide

An IDE inspired by Rich Kilmer's Talk at Gogaruco
6
star
83

routed-http.rs

Combining rust-http with route-recognizer.rs to create routable HTTP
Rust
6
star
84

rake-pipeline-rails

Ruby
6
star
85

slicehub

An application for managing slices
6
star
86

wand

Rust
5
star
87

core-storage

TypeScript
5
star
88

merb-docs

Ruby
5
star
89

railsconf-rust-demo

Rust
5
star
90

sproutcore-interactive-tutorials

JavaScript
5
star
91

js_tabs_example

An example (not production code!) of how to write evented JS using tabs as an example
JavaScript
5
star
92

rdoc

rdoc fork to support arbitrary API level filtering
5
star
93

laszlo_post_api

Laszlo POST-API
Java
5
star
94

rust-experiments

Rust
5
star
95

rubygems-bug-demo

Ruby
5
star
96

handlebars-parser

An experimental rewrite of the Handlebars parser using Jison
JavaScript
5
star
97

at-media

The code from my @media talk
4
star
98

gkc

Rust
4
star
99

stalkr

Ruby
4
star
100

css_to_xpath

Extracted from Nokogiri in anticipation of merging into Merb
Ruby
4
star