1. Kevin Wetzels
  2. resourcetory

Overview

HTTPS SSH

Resourcetory

Resourcetory is a simple and small JavaScript library that allows you to keep track of the X resources most recently visited by a user.

Let's say you have a webapp in which users view information about customers and products and enter notes about them through forms. You typically don't want those users to use the Back button to get back to the page of the customer they were viewing before they looked at their most recent order, eventually ending up updating the description of one of the products.

Instead, you want to keep track of the important pages they looked at, providing them with a list of recently viewed pages somewhere in the webapp UI. And to make it a bit easier to manage, you want to keep the state client side.

That's what Resourcetory was created for (and not to replace the History API).

Example

The first thing we need to do is configure resourcetory to use a JS storage library. The tests use store.js, but you can use any storage library you prefer:

# my.js
resourcetory.store = store;
// When you're using another library, you'll probably have to do something
// along the lines of this:
resourcetory.store = {
    get: function(key) {
        return mystoragelib.getValue(key);
    },
    set: function(key, value) {
        mystoragelib.setValue(key, value);
    },
    remove: function(key) {
        mystoragelib.removeValue(key);
    }
};

The second thing you'll want to do is start adding the visited pages. We'll use our CRM tracking system from above as an example (using Django templates):

# product.html
<script type="text/javascript">
    resourcetory.add('product', {
        id: '{{ product.id|escapejs }}',
        name: '{{ product.name|escapejs }}',
        color: '{{ product.color|escapejs }}'
    });
</script>

# customer.html
<script type="text/javascript">
    resourcetory.add('customer', {
        id: '{{ customer.id|escapejs }}',
        name: '{{ customer.name|escapejs }}',
        email: '{{ customer.email|escapejs }}'
    });
</script>

# order.html
<script type="text/javascript">
    resourcetory.add('order', {
        id: '{{ order.id|escapejs }}',
        customer: {
            id: '{{ order.customer.id|escapejs }}',
            name: '{{ order.customer.name|escapejs }}'
        },
        total: '{{ order.total|escapejs }}'
    });
</script>

You can add any data you like, it's up to you to decide the amount of data you need for your purposes.

The above won't really do what you'd expect. By default resourcetory will want to keep track of the ten most recent items for each type. But if you're viewing the product page of a yellow iPhone, you want it to end up in that list only once. Even when you keep reloading the page. Therefor, everytime you add an item, resourcetory will check whether that item is already a part of the list. If it is, it's removed and added to the back, ending up as the most recently viewed item of that type.

And that's where the second part of configuration steps in -- determining whether two values are equal to prevent duplicate values:

# my.js
resourcetory.typeConfig.equalsFunction = function(type) {
    return function(a, b) {
        return a.id == b.id;
    };
};

Now you have configured resourcetory to always use the id property of a value to determine whether a value is already listed.

What good does it do if you can't read what you added? Let's build a list of recently viewed items with JS (and Mustache and jQuery):

# my.html
<script type="text/html" id="recently_viewed_template">
    <h3>Recently viewed</h3>
    <ul>
        {{#products}}
            <li><a href="/products/{{id}}">{{name}} ({{color}})</a></li>
        {{/products}}
        {{#customers}}
            <li><a href="/customers/{{id}}">{{name}} ({{email}})</a></li>
        {{/customers}}
        {{#orders}}
            <li><a href="/orders/{{id}}">{{customer.name}} ({{total}})</a></li>
        {{/orders}}
    </ul>
</script>

# my.js
$(document).ready(function() {
    var placeholder = $('#recently-viewed');
    if (placeholder.length) {
        // Made sure the placeholder exists, build a context for the
        // Mustache template
        var context = {
            products: resourcetory.recent('product', 5),
            customers: resourcetory.recent('customer', 5),
            orders: resourcetory.recent('order', 5)
        };
        // Get the template from the HTML source
        var template = $('#recently_viewed_template').html();
        // And add the results to the placeholder
        placeholders.append($.mustache(template, context));
    }
});

API

resourcetory.registeredTypes
Provides you with the list of types registered with resourcetory. When the last value of a type is removed, it will no longer be apart of this array.
resourcetory.add(type, value, eq)
Add a value of the given type, optionally using a custom eq function to find duplicates in the already registered values.
resourcetory.remove(type, value, eq)
Remove the value of the given type, optionally using a custom eq function to find it in the list already registered values.
resourcetory.clear(type)
Remove all values of the given types, which will also remove the type from the list of registered types.
resourcetory.all(type)
Retrieve all values of the given type. If no values are available, an empty array will be returned.
resourcetory.recent(type, n)
Retrieve all recent values of the given type. If n is falsy or negative, an empty array will be returned. Note: the most recent value is the last item of the array.
resourcetory.mostRecent(type)
Find the most recent value of the given type (i.e. the item in the array returned by resourcetory.recent(type, 1)), or null if no such value exists.
resourcetory.store

Wraps the storage library of your choice with following signature:

var store = {
    get: function(key) {
        // Example
        return mystore.getValue(key);
    },
    set: function(key, value) {
        // Example
        mystore.setValue(key, value);
    },
    remove: function(key) {
        // Example
        mystore.removeValue(key);
    }
}
resourcetory.typeConfig

Provides a convenient way to alter the behavior of resourcetory:

var typeConfig = {
    maxItems: function(type) {
        // Returns the max amount of items to retain for a certain type.
        // For example:
        return (type == 'people' ? 5 : 10);
        // Default:
        // return 10;
    },
    equalsFunction: function(type) {
        // Returns a function to check whether two values of a certain
        // type are equal.
        // Default:
        return function(a, b) {
            return a == b;
        };
    },
    storageKey: function(type) {
        // Creates a unique storage key for the type, avoiding naming
        // collisions with other stuff you're storing.
        // Default:
        return 'resourcetory_' + type;
    }
}