ko-sortable - drag between lists

https://github.com/rniemeyer/knockout-sortable

by Asle Gundersby

HTML

<script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script src="https://rawgithub.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<ul data-bind="sortable: scenes">
  <li>
  <div class="header">

     <i class="fa fa-caret-down" data-bind="visible: !isVisible(), toggle: isVisible"></i>
     <i class="fa fa-caret-up" data-bind="visible: isVisible(), toggle: isVisible"></i>
      <a href="#" data-bind="click: $root.selectedScene">
          <span data-bind="text: sequenceNo"></span>:
          <span data-bind="text: title"></span>
      </a>
    </div>
    <div class="content" data-bind="visible: isVisible">
      <label>Purpose:</label>
      <input data-bind="textInput: purpose"/>
    </div>
  </li>
</ul>

<hr/>

<div data-bind="with: selectedScene">
  <h2><span data-bind="text: $data.title"></span> Texts</h2>

  <ul data-bind="sortable: { data: $root.currentSceneTexts, afterMove: $root.updateSceneID.bind($root, $data.sceneID) }">
    <li>
      <span data-bind="text: $parent.sequenceNo"></span>-<span data-bind="text: $data.sequenceNo"></span>:
      <span data-bind="text: title"></span>
    </li>
  </ul>

  <hr/>

  <h2>Available Texts</h2>
  <ul data-bind="sortable: { data: $root.availableTexts, afterMove: $root.updateSceneID.bind($root, null) }">
    <li>
      <span data-bind="text: sequenceNo"></span>:
      <span data-bind="text: title"></span>
    </li>
  </ul>
</div>

<hr style="clear:both" >
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

CSS

body { font-family: arial; }
ul { min-height: 50px; border: solid 1px #000; padding: 5px; list-style: none; }
/* li { padding: 5px; margin: 5px; background-color: #eee; border: solid 1px #ddd; } */
li { padding: 0px; margin: 5px;  border: solid 1px #ddd; } 

div.header{
  padding: 5px; background-color: #eee; border: solid 1px #ddd;
}
div.content{
  padding: 5px; margin-top:2px;
  background-color: #fff;

}

JavaScript

ko.bindingHandlers.toggle = {
    init: function (element, valueAccessor) {
        var value = valueAccessor();
        ko.applyBindingsToNode(element, {
            click: function () {
                value(!value());
            }
        });
    }
};
        
        var scenes = [
            { sceneID: 0, title: "First scene", sequenceNo: ko.observable(1), purpose: ko.observable(""),isVisible: ko.observable(false)},
            { sceneID: 1, title: "Second scene", sequenceNo: ko.observable(2), purpose: ko.observable(""),isVisible: ko.observable(false) },
            { sceneID: 2, title: "Third scene", sequenceNo: ko.observable(3), purpose: ko.observable(""),isVisible: ko.observable(false) }
        ];

        var texts = [
            { textID: 0, sceneID: ko.observable(0), title: "Text A", sequenceNo: ko.observable(1) },
            { textID: 1, sceneID: ko.observable(null), title: "Text B", sequenceNo: ko.observable(2) },
            { textID: 2, sceneID: ko.observable(null), title: "Text C", sequenceNo: ko.observable(3) },
            { textID: 3, sceneID: ko.observable(2), title: "Text D", sequenceNo: ko.observable(4) },
            { textID: 4, sceneID: ko.observable(null), title: "Text E", sequenceNo: ko.observable(5) },
            { textID: 5, sceneID: ko.observable(2), title: "Text F", sequenceNo: ko.observable(6) },
            { textID: 6, sceneID: ko.observable(1), title: "Text G", sequenceNo: ko.observable(7) },
            { textID: 7, sceneID: ko.observable(1), title: "Text H", sequenceNo: ko.observable(8) }
        ];

        function ViewModel(scenes, texts) {
            this.scenes = ko.observableArray(scenes);
            this.selectedScene = ko.observable();
            this.texts = ko.observableArray(texts);
            this.currentSceneTexts = ko.observableArray();

            // seed the available texts
            this.availableTexts = ko.observableArray(ko.utils.arrayFilter(this.texts(), function(text) {
                return...