JSFiddle - React, Tailwind, and code Playground

by Mark

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<fieldset>
					<legend>Settings</legend>
					<input type="radio" id="FieldSize" name="PointBasis" value="fieldsize" data-bind="checked: settings.points.basis" /><label for="FieldSize">Field Size</label>
					<input type="radio" id="Ceiling" name="PointBasis" value="ceiling" data-bind="checked: settings.points.basis" /><label for="Ceiling"><span data-bind="text: settings.points.ceiling"></span> Point Ceiling</label>
				</fieldset>
<fieldset>
    <legend>New Combatant</legend>
    ID: <input type="text" id="CombatantID" />
    <br />
    <br />
    Name: <input type="text" id="CombatantName" />
    <br />
    <br />
    Division: <select id="Division" data-bind="foreach: divisions">
    <option data-bind="text: name" />
    </select>
    <br />
    <br />
    <button id="Add" >Add Combatant</button>
</fieldset>
<table border="1">
    <thead>
        <th>Division</th>
        <th>ID</th>
        <th>Name</th>
        <th>Kills</th>
        <th>Time</th>
        <th>Points</th>
    </thead>
    <tbody data-bind="foreach: {data: divisions, as: 'div'}">
	    <tr class="divheader">
		    <td data-bind="text: name" colspan="6"></td>
		</tr>
		<!-- ko foreach: combatants -->
		<tr>
			<td></td>
			<td data-bind="text: ID"></td>
			<td data-bind="text: name"></td>
            <td class="align-right"><input type="text" data-bind="value: swords.kills"/></td>
			<td class="align-right"><input type="text" data-bind="value: swords.time"/></td>
		    <td class="align-right" data-bind="text: $root.updatePoints($data, 'swords', $index(), div)"></td>
		</tr>
		<!-- /ko -->
	</tbody>
</table>

JavaScript

ko.observableArray.fn.sortBy = function(evt, fld, direction){
		var isdesc = direction && direction.toLowerCase() == 'desc';
		return ko.observableArray(this.sort(function(a, b){
			a = ko.unwrap(evt ? a[evt][fld]() : a[fld]());
			b = ko.unwrap(evt ? b[evt][fld]() : b[fld]());
			return (a == b ? 0 : a < b ? -1 : 1) * (isdesc ? -1 : 1);
		}));
	}

function ViewModel(data){
	var self = this;
	self.divisions = ko.observableArray([]).extend({notify: 'always'});
	
	self.sortDivision = function(div, evt){
		return div.sortBy(evt,'time','asc').sortBy(evt, 'kills', 'desc')
	};
	
	self.updatePoints = function(combatant, evt, val, div){
		switch(combatant[evt].time().toLowerCase()){
			case '0:00:00':
			case 'none':
				val = 0;
				break;
			case 'died':
				val = 1;
				break;
			default:
				switch(self.settings.points.basis()){
					case 'ceiling':
						val = self.settings.points.ceiling() - val;
						if(val < self.settings.points.floor()){
							val = self.setting.points.floor();
						}	
						break;
					default:
						val = div.combatants().length - val;
				}
		}
		combatant[evt].points(val);
		return combatant[evt].points();
	}
	
	
	self.addCombatant = function(combatant){
			ko.utils.arrayForEach(self.divisions(), function(d){
				if(d.name() == combatant.division){
					d.combatants.push({name: combatant.name,
								ID: combatant.ID,
								swords:{points: 0, time:'none', kills: 0}
			                    });
                }
           console.log(ko.toJSON(self.divisions));
	    }
    )}.bind(this);
   ko.mapping.fromJS(data, {}, self);
}

var _vm;

$(function(){
    $('#Add').on('click', 
                 function(){
                      _vm.addCombatant({division: $('#Division option:selected').val(),
                      ID: $('#CombatantID').val(),
                      name: $('#CombatantName').val()});
                 });
});
                                  
$.getJSON('https://api.myjson.com/bins/3jack',
				  function(res){
					_vm = new...