Permute

jQuery extension to permute order of sibling elements and to record and restore permutations.

by Tonio Loewald

HTML

<ul></ul>
<button id="reset">Reset</button>
<h3>These permutations are their own inverses</h3>
<button class="permute" name="switch first two elements">[1,0,2,3,4,5,6,7,8,9]</button>
<button class="permute" name="switch pairs">[1,0,3,2,5,4,7,6,9,8]</button>
<button class="permute" name="reverse">[9,8,7,6,5,4,3,2,1,0]</button>
<button class="permute" name="barrel shift five times">[5,6,7,8,9,0,1,2,3,4]</button>
<h3>These permutations are invert each other</h3>
<button class="permute" name="barrel shift left">[9,0,1,2,3,4,5,6,7,8]</button>
<button class="permute" name="barrel shift right">[1,2,3,4,5,6,7,8,9,0]</button>
<h3>Other Permutations</h3>
<button class="permute" name="interleave">[0,2,4,6,8,1,3,5,7,9]</button>
<h3>Save Current State as Permutation</h3>
<p>Note that to restore a state you need to reset first. The stored permutation is the inverse of the current permutation, so if you save and click the newly created button it's the same as a reset.</p>
<button id="save">Save</button>

CSS

body {
    font-family: sans-serif;
    font-size: 14px;
}
li {
    display: block;
    height: 18px;
    background-color: #69c;
    border: 1px solid white;
}
button {
    display: block;
}
button.permute:before {
    content: attr(name) " ";
}

JavaScript

function attributeComparer(attribute) {
    return function (a, b) {
        var a = parseInt($(a).attr(attribute));
        var b = parseInt($(b).attr(attribute));
        return a - b;
    }
}

$.fn.permute = function (permutation) {
    var set = this,
        parent = set.parent(),
        appendToParent = function(){ parent.append(this) };
    if (permutation === undefined) {
        if (this.length != this.filter('[data-initial-order]').length) {
            console.error('cannot determine permutation; no [data-initial-order] found');
        }
        permutation = [];
        set.each(function (idx) {
            permutation[parseInt($(this).attr('data-initial-order'))] = idx;
        });
        return permutation;
    } else if (permutation === "init") {
        set.each(function () {
            $(this).attr('data-initial-order', $(this).index());
        });
    } else if (permutation === "reset") {
        set.sort(attributeComparer('data-initial-order'))
            .each(appendToParent);
    } else if (permutation.constructor === Array && this.length === permutation.length) {
        set.each(function (idx) {
            $(this).attr('data-new-order', permutation[idx])
        })
            .sort(attributeComparer('data-new-order'))
            .removeAttr('data-new-order')
            .each(appendToParent);
    }
    return this;
}

for (var i = 0; i < 10; i++) {
    $('<li>').css('width', (i + 1) * 20 + 'px').appendTo('ul');
}

$('li').permute("init");
$('ul').attr('data-permutation', JSON.stringify($('li').permute()), false, 2);
$('body').on('click', '.permute', function (evt) {
    var permutation = JSON.parse($(evt.target).text());
    console.log(permutation);
    $('li').permute(permutation);
});
$('#reset').on('click', function () {
    $('li').permute("reset");
});
$('#save').on('click', function () {
    $('<button>').text(JSON.stringify($('li').permute()))
        .addClass('permute')
        .attr('name', 'restore')
       ...