Sierra Trading Post: API Demo (recursive templates)

*Update 2011-10-07: Fix for API URLs that already contain querystring "?". Fix for current/previous URLs not using hash navigation.

by Adam Patridge

HTML

<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="http://jquery-jsonp.googlecode.com/files/jquery.jsonp-2.1.4.min.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script src="https://rawgithub.com/tkyk/jquery-history-plugin/master/jquery.history.js"></script>
<noscript>This demo is written completely in JavaScript. If you don't have it, or have it disabled, it will be far less exciting.</noscript>
<ul class="navigation">
    <li>Current API URL: <a class="currentUrl" href="#">n/a</a></li>
    <li>Previous API URL: <a class="previousUrl" href="#">n/a</a></li>
    <li><a class="homeUrl" href="#/api/1.0/">Home</a></li>
    <li><a href="#/api/1.0/brands/">Brands</a></li>
    <li><a href="#/api/1.0/products/newproducts~21/">New Items</a></li>
    <li><a href="#/api/1.0/products/clearance~1/">Clearance Items</a></li>
    <li><a href="#/api/1.0/products/bargain-barn/">Bargain Barn Items</a></li>
</ul>
<div class="results"></div>
<div class="loading">
    <img src="http://s.stpost.com/img/large-ajax-loader-circle.gif" alt="Loading..." />
    <p>
        Requesting: <span class="currentRequestUrl"></span>
    </p>
    <p>
        <a class="cancelRequest" href="#">Cancel Request</a>
    </p>
</div>
<script id="arrayItemTemplate" type="text/x-jquery-tmpl">
    <li>
        {{if null !== $data && "object" === typeof ($data)}}
        {{if $data instanceof Array}}
        [
        <ul>
            {{tmpl($data, { getTypeDisplay: $item.getTypeDisplay, getDataClass: $item.getDataClass, formatDisplay: $item.formatDisplay }) "arrayItemTemplate"}}
        </ul>
        ]
        {{else}}
        {{tmpl($data, { getTypeDisplay: $item.getTypeDisplay, getDataClass: $item.getDataClass, formatDisplay: $item.formatDisplay }) "reflectTemplate"}}
        {{/if}}
        {{else}}
        ${$item.formatDisplay($data)}
        {{/if}}
    </li>
</script>
<script id="reflectTemplate"...

CSS

ul { border-left: 1px solid #aaa; margin-left: 15px; list-style-type: none; }
ul ul { border-bottom: 1px solid #aaa; }
li li { margin-left: 5px; }
.type-string { color: #A31515; }
.type-string a, .type-string a:visited, .type-string a:hover { color: #A31515; }
.type-null, .type-number { color: #281; }
.navigation { list-style-type: none; border: 1px solid #666; margin: 0 0 5px 0; }

JavaScript

$(function() {
    var apiHost = "api.sierratradingpost.com",
        apiKey = "xe5s5rpag9y4ear9bwxsu48e",
        affiliateName = "CJ",
        affiliateId = "1935257",
        affiliateCjSid = "5456339",
        // Don't advertise your developer key. This one is heavily throttled and changed if abuse is discovered.
        startApiUrl = $(".homeUrl").attr("href").substring(1),
        $loading = $(".loading"),
        $currentRequestUrl = $(".currentRequestUrl"),
        arrayItemTemplateCompiled = $.template("arrayItemTemplate", $("#arrayItemTemplate")),
        reflectTemplateCompiled = $.template("reflectTemplate", $("#reflectTemplate")),
        $resultsDiv = $(".results"),
        $navigationList = $(".navigation"),
        $previousUrl = $(".previousUrl"),
        $currentUrl = $(".currentUrl"),
        templateFunctions = {
            getTypeDisplay: function(itemToCheck) {
                var itemType = null === itemToCheck ? "null" : typeof(itemToCheck); // typeof(null) === "object"
                itemType = "object" === itemType && itemToCheck instanceof Array ? "array" : itemType; // Arrays are objects first, must ask if it is an array.

                return itemType;
            },
            getDataClass: function(itemToCheck) {
                var itemType = typeof(itemToCheck),
                    result = itemType;

                if (null === itemToCheck) {
                    result = "null";
                }
                return result;
            },
            formatDisplay: function(propertyValue) {
                var toSmallDateString = function(value) {
                    var padTwo = function(num) {
                        return (num < 10) ? "0" + num : String("") + num;
                    };
                    return padTwo(value.getFullYear()) + "-" + padTwo(1 + value.getMonth()) + "-" + padTwo(value.getDate()) + "," + padTwo(value.getHours()) + ":" + padTwo(value.getMinutes()) + ":" + padTwo(value.getSeconds());
      ...