Draggable, droppable, and sortable list

http://www.knockmeout.net/2011/05/dragging-dropping-and-sorting-with.html

by Sameer Shemna

HTML

<script src="http://knockoutjs.com/downloads/knockout-2.2.1.debug.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout-sortable/0.8.1/knockout-sortable.min.js"></script>
<h2>Multi-select sortable with Knockout.js</h2>
<p><code>Click</code> to select individual items</p>
<p><code>Ctrl + Click</code> to select multiple items</p>
<div class="list">
    <h2>Stored procedures In DB 1</h2>
    <ul class="list" id="sortableForDB_1" data-bind="sortable: { data: storedProceduresInDB1, beforeMove: checkAndCopy, options: multiSortableOptions }">
        <li class="item" data-bind="attr: { id: 'sp_'+Id }, text: Name, click: $root.selectProcedure.bind($data, $parent.storedProceduresInDB1()), css: { selected: Selected }"></li>
    </ul>
</div><br>
<div class="list">
    <h2>Stored procedures In DB 2</h2>
    <ul class="list" id="sortableForDB_2" data-bind="sortable:  { data: storedProceduresInDB2, beforeMove: checkAndCopy, options: multiSortableOptions }">
        <li class="item" data-bind="attr: { id: 'sp_'+Id }, text: Name, click: $root.selectProcedure.bind($data, $parent.storedProceduresInDB2()), css: { selected: Selected }"></li>
    </ul>
</div>

CSS

h2 {
    font-weight: bold;
}
div, li {
    padding: 5px;
    margin: 5px 0;
    border: black 1px solid;
}
.helper {
    padding:0;
    margin:0;
    border:0;
}
ul {
    padding-bottom: 10px;
}
.item {
    background-color: #DDD;
    cursor: default;
}
.item.selected {
    background-color:#FED;
    cursor: move;
}
body {
    font-family: arial;
    font-size: 14px;
}
.list {
    padding: 1em;
    background-color: #EEEEDD;
    border: 1px solid #CCC;
    max-width: 600px;
}
.list input {
    font-family: Arial;
}
.list b {
    font-weight: bold;
}
.list p {
    margin-top: 0.9em;
    margin-bottom: 0.9em;
}
.list-select {
    width: 100%;
    height: 8em;
}
.list h2 {
    margin-top: 0.4em;
}
p, a {
    font-size: .8em;
}
.trash { background-color: #000; }

JavaScript

var multiSortableOptions =  {
    delay: 150,
    revert: 0,
    stop: function(event, ui) {
        // Force lists to refresh all items
        var db1 = myViewModel.storedProceduresInDB1,
            db2 = myViewModel.storedProceduresInDB2,
            temp1 = db1(),
            temp2 = db2();
        ui.item.remove();
        db1([]);
        db1(temp1);
        db2([]);
        db2(temp2);
    },
    helper: function(event, $item) {
        // probably a better way to pass these around than in id attributes, but it works
        var dbId = $item.parent().attr('id').split('_')[1],
            itemId = $item.attr('id').split('_')[1],
            db = myViewModel['storedProceduresInDB'+dbId];
        
        // If you grab an unhighlighted item to drag, then deselect (unhighlight) everything else
        if(!$item.hasClass('selected')) {
            ko.utils.arrayForEach(db(), function(item) {
                if(item.Id == itemId) {
                    item.Selected(true);
                } else {
                    item.Selected(false);
                }
            });
        }
        
        // Create a helper object with all currently selected items
        var $selected = $item.parent().find('.selected');
        var $helper = $('<li class="helper"/>');
        $helper.append($selected.clone());
        $selected.not($item).remove();
        return $helper;
    }
};
var ViewModel = function () {
    var self = this;
    self.storedProceduresInDB1 = ko.observableArray([
        { Name: 'SP1', Id: 1, Selected: ko.observable(false) },
        { Name: 'SP2', Id: 2, Selected: ko.observable(false) },
        { Name: 'SP3', Id: 3, Selected: ko.observable(false) }
    ]);
    self.storedProceduresInDB2 = ko.observableArray([
        { Name: 'SP3', Id: 3, Selected: ko.observable(false) },
        { Name: 'SP4', Id: 4, Selected: ko.observable(false) },
        { Name: 'SP5', Id: 5, Selected: ko.observable(false) }
    ]);
    self.checkAndCopy = function(event)...