Draggable, droppable, and sortable list

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

by g33ky

HTML

<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.debug.js"></script>
 <a href="#" data-bind="click: addTask">Add Task</a>

<table border=1>
    <tr>
        <td class='leftCol'>
             <h2>Factors</h2>

            <div class="container" data-bind="template: { name: 'taskTmpl', foreach: normalPriorityTasks, templateOptions: { parentList: normalPriorityTasks} }, sortableList: normalPriorityTasks"></div>
        </td>
        <td class='rightCol'>
             <h2>Selected factors</h2>

            <div class="container high" data-bind="template: { name: 'taskTmpl', foreach: highPriorityTasks, templateOptions: { parentList: highPriorityTasks} }, sortableList: highPriorityTasks"></div>
        </td>
    </tr>
</table>
<div id="main">
    <script id="taskTmpl" type="text/html">
        <div class="item" data-bind="sortableItem: { item: $data, parentList: $item.parentList }" >
            <a href="#" data-bind="text: name, click: function() { viewModel.selectTask($data); }, visible: $data !== viewModel.selectedTask()"></a>
            <input data-bind="value: name, visibleAndSelect: $data === viewModel.selectedTask(), event: { blur: function() { viewModel.selectTask(''); } }" />
        </div>
    </script>
</div>
<script id="resultTmpl" type="text/html">
    < li data - bind = "text: name" > < /li>
</script>

CSS

body {
    font-family: arial;
}
h2 {
    font-weight: bold;
}
div {
    padding:0;
    margin:0;
}
p {
    font-size: .8em;
}
.rightCol {
    background:#dddddd;
}
a {
    font-size: .8em;
    color:#ffffff;
    text-decoration:none;
    background:#006699;
}
ul {
    padding-bottom: 10px;
}
td {
    vertical-align:top;
}
.container {
    width: 340px;
    height:300px;
}
.high {
}
.trash {
    background-color: #000;
}
.item {
    background-color: #006699;
    color: #ffffff;
    border:1px solid #000000;
    cursor: move;
    margin: 5px;
    text-align:center;
}
.item input {
    width: 100px;
}
#main {
    float: left;
}
#results {
    margin-left: 175px;
    width: 150px;
}

JavaScript

function Task(name) {
    this.name = ko.observable(name);
}
var viewModel = {
    highPriorityTasks: ko.observableArray([]),
    normalPriorityTasks: ko.observableArray([
  new Task("Get dog food"),
     new Task("Mow lawn"),
        new Task("Fix fence"),
    new Task("Walk dog"),
    new Task("Read book")]),
    selectedTask: ko.observable(),
    selectTask: function (task) {
        this.selectedTask(task);
    },
    addTask: function () {
        var task = new Task("new");
        this.selectedTask(task);
        this.normalPriorityTasks.push(task);
    },
    trash: []
};

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        $(element).data("sortList", valueAccessor()); //attach meta-data
        $(element).sortable({
            update: function (event, ui) {
                var item = ui.item.data("sortItem");
                if (item) {
                    //identify parents
                    var originalParent = ui.item.data("parentList");
                    var newParent = ui.item.parent().data("sortList");
                    //figure out its new position
                    var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                    if (position >= 0) {
                        originalParent.remove(item);
                        newParent.splice(position, 0, item);
                    }

                    ui.item.remove();
                }
            },
            connectWith: '.container'
        });
    }
};

//attach meta-data
ko.bindingHandlers.sortableItem = {
    init: function (element, valueAccessor) {
        var options = valueAccessor();
        $(element).data("sortItem", options.item);
        $(element).data("parentList", options.parentList);
    }
};

//control visibility, give element focus, and select the contents (in order)
ko.bindingHandlers.visibleAndSelect = {
    update:...