hierarchical data attributes

by rwaldin

HTML

<div data-wsi-assortment="{categoryId:'foo'}">
    <div data-wsi-assortment="{groupId:'bar'}">
        <div class="product" data-wsi-assortment="{sku:123456781}">Product 1</div>
        <div class="product" data-wsi-assortment="{sku:123456782}">Product 2</div>
        <div class="product" data-wsi-assortment="{sku:123456783}">Product 3</div>
    </div>
    <div data-wsi-assortment="{groupId:'baz'}">
        <div class="product" data-wsi-assortment="{categoryId:'faz',groupId:'boo',sku:123456784}">Product 4</div>
        <div class="product" data-wsi-assortment="{sku:123456785}">Product 5</div>
        <div class="product" data-wsi-assortment="{sku:123456786}">Product 6</div>
    </div>
</div>

JavaScript

var WSI = {};

/**
 * HTML5 dataset attribute helpers
 */
(function(dojo, undefined) {
    var dataPrefix = 'data-';

    function maybeJson(string) {
        return /^\s*{.*}\s*$/.test(string) ? dojo.fromJson(string) : (string || {});
    }

    function attrName(attr) {
        return dataPrefix + attr.toLowerCase();
    }

    WSI.dataset = dojo.mixin(dojo.provide("wsgc.js.Dataset"), {
        get: function(elem, attr, nested) {
            if(nested) {
                var name = attrName(attr),
                dataset = {};

                while(elem) {
                    if(dojo.hasAttr(elem, name)) {
                        dataset = dojo.mixin(this.get(elem, attr), dataset);
                    }
                    elem = elem.parentNode;
                }
                return dataset;
            } else {
                return maybeJson(elem.getAttribute(attrName(attr)) || "{}");
            }
        },
        set: function(elem, attr, value) {
            elem.setAttribute(attrName(attr), dojo.isObject(value) ? dojo.toJson(value) : value);
        },
        "delete": function(elem, attr) {
            elem.removeAttribute(attrName(attr));
        },
        keys: function(elem) {
            var keys = [], attrs = elem.attributes;
            for(var i = 0, len = attrs.length; i < len; ++i) {
                var name = attrs[i].name;
                if(!name.indexOf(dataPrefix)) {
                    keys.push(name.substr(dataPrefix.length));
                }
            }
            return keys;
        },
        mixin: function(elem, attr, obj) {
            this.set(elem, attr, dojo.mixin(this.get(elem, attr), obj));
        }
    });
}(dojo));

// use the inherit function to find complete data-assortment maps for all product
dojo.query('.product').forEach(function(p) {
    var dataset = WSI.dataset.get(p, 'wsi-assortment', true),
        props = [];

    for(prop in dataset) {
        if(dataset.hasOwnProperty(prop)) {
           ...