Archive for the ‘ Code Samples’ Category

Fight Score Single Page UI App

I asked a question on Twitter last week about jQuery and async calls (thanks @jeefy & @codepo8) – a few folks asked me to post the code I was working on so here it goes.

The app is an HTML page for scoring boxing and MMA fights.

You can play with the UI here : http://koscience.com/apps/scorecard/

This is a mile stone of code samples I’ve posted recently on Dynamic jQuery Accordions and Event Handler Delegation for Dynamic Content.

scorecard-1

The events represented in the application’s UI are data driven by the retrieved JSON file (sample data below)


data.json


[
    {
        "bluefighter"       : "Fighter 1",
        "blueimage"         : "images/silhouette.jpg",
        "redfighter"        : "Fighter 2",
        "redimage"          : "images/silhouette.jpg",
        "numberofrounds"    : "10"
    },
    {
        "bluefighter"       : "Fighter 3",
        "blueimage"         : "images/silhouette.jpg",
        "redfighter"        : "Fighter 4",
        "redimage"          : "images/silhouette.jpg",
        "numberofrounds"    : "3"
    },
    {
        "bluefighter"       : "Fighter 5",
        "blueimage"         : "images/silhouette.jpg",
        "redfighter"        : "Fighter 6",
        "redimage"          : "images/silhouette.jpg",
        "numberofrounds"    : "5"
    },
    {
        "bluefighter"       : "Fighter 7",
        "blueimage"         : "images/silhouette.jpg",
        "redfighter"        : "Fighter 8",
        "redimage"          : "images/silhouette.jpg",
        "numberofrounds"    : "12"
    }
]

You’ll note that the number of bouts in the event are not determined until run time.

Also, for each bout, the number of rounds is not fixed. Each is determined by an entry in the JSON data.

Since each bout will have a VARIABLE number of inputs and outputs, if you think about the mechanics of accomplishing this, it’s an interesting HTML issue.

There are lots of other, perhaps more elegant ways I might have done this (whenever I post code someone comments to tell me I suck :) )

I didn’t use MVC, Event Delegation, or an Object Oriented Data Model. Each of those that I considered just didn’t seem to offer the value in return for the added abstraction or complexity.

(I used to do some work for the SIG Sauer Firearms Academy where we used the acronym SIG = Simple Is Good !)

So I took the simple route. The loop that iterates through the JSON data dynamically adds each content section and since I’m not using event delegation, each “bout” (accordion tab) gets it’s own version of the event handling code to score the bout.

Since the number of bouts is relatively small (less than 15) the performance hit won’t ever be an issue, but if we were dealing with a large number of sections we would need to use delegation of some sort to attach a single event handler at the containing element or document level to avoid unnecessarily duplicating code by hooking the click event for every object / button.


index.html


<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Fight Card</title>
<link rel="stylesheet"
 href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<link rel="stylesheet" href="css/style.css">
<style>
	/* IE has layout problems when sorting accordion items  */
	.group { zoom: 1 }
</style>

<script>

//-----------------------------------------------------------------------+
// addBout() - Add a single node to the accordion
//-----------------------------------------------------------------------+

function addBout(bout, key){

//-----------------------------------------------------------------------+
// Build the display element for the the scoring display based on the 
// number of rounds for this bout.
//-----------------------------------------------------------------------+
var round_numbers = "";
var round_scores = "";
var round_totals = "";
for (var i=0; i < bout.numberofrounds; i++)
{
	round_numbers += "<td id='roundTitle_" + (key+1) + "_" + (i+1) 
				   + "'>Round #" + (i+1) + "</td>";
	round_scores  += "<td id='bout_" +  (key+1) + "_round_" + (i+1) 
				   + "_roundbox'><span id='bout_" + (key+1) + "_round_" 
				   + (i+1) +  "_blueScore'>00</span>-<span id='bout_" 
				   + (key+1) + "_round_" + (i+1) 
				   +  "_redScore'>00</span></td>";

	round_totals  += "<td id='bout_" +  (key+1) + "_round_" + (i+1) 
				   + "_totalbox'><span id='bout_" + (key+1) + "_round_" 
				   + (i+1) +  "_blueTotal'>00</span>-<span id='bout_" 
				   + (key+1) + "_round_" + (i+1) 
				   +  "_redTotal'>00</span></td>";
}

//-----------------------------------------------------------------------+
// Build the display element for the rest of the current bout.
//-----------------------------------------------------------------------+
var bout =
	"<div id=\"bout_" + (key+1) + "\" class=\"group\">" +
		"<span class=\"handle\"> " +
			"<div class=\"table\">" +
				"<div class=\"row\">" +
					"<span class=\"cell width33pct align-left\">" +
						"<strong>" + bout.bluefighter + "</strong>" +
					"</span>" +
					"<span class=\"cell align-center valign-middle\">" +
						"vs." +
					"</span>" +
					"<span class=\"cell width33pct align-right\">" +
					"<strong>" + bout.redfighter + "</strong>" +
					"</span>" +
				"</div>" +
			"</div>" +
		"</span>" +
		"<div>" +
			"<div class=\"table\">" +
				"<div class=\"row\">" +
					"<span class=\"cell width150 align-center\">" +
						"<img src=\"" + bout.blueimage 
						+ "\" alt=\"\" style='border:2px solid blue'>"
						+ "<br />" +
					"</span>" +
					"<span id=\"test\" "  +
					"class=\"cell align-center valign-middle\">" +
						"<h2>" +
						"<span id='roundLabel_" + (key+1) + 
						"'>Round</span><br />" +
						"<span id=\"current_round_" + (key+1) + 
						"\">1</span><br />" +
						"<input type='number' id='blueRoundScore_" + 
						(key+1) + "' size='2' max='10' min='0' " +
						"value='10' " +
						"style='border:2px solid blue'>" +
						"<input type='number' id='redRoundScore_"  + 
						(key+1) + "' size='2' max='10' min='0' " +
						"value='10' style='border:2px solid red'>" +
						"<br /><br />" +
						"<button id='btnScore_" + (key+1)  + 
						"' class='score_button'>Score</button>" +
						"<span id='boutResult_" + (key+1)+ "' " +
						"class='boutResultClass'></span>" +
						"</h2>" +
					"</span>" +
					"<span class=\"cell width150 align-center\">" +
						"<img src=\"" + bout.redimage + "\" alt=\"\" " +
						"style='border:2px solid red'>" +
					"</span>" +
				"</div>" +
			"</div>" +
			"<div align=\"center\">" +
				"<table>" +
					"<tr>" +
						round_numbers +
					"</tr>" +
					"<tr>" +
						round_scores +
					"</tr>" +
					"<tr>" +
						round_totals +
					"</tr>" +
				"</table>" +
			"</div>" +
		"</div>" +
	"</div>";
return bout;
}

//--------------------------------------------------------------------------+
// DOM READY()
//--------------------------------------------------------------------------+
$(function() {
	//----------------------------------------------------------------------+
	// Wire up the accordion div, specify the header/handle and make 
	// the sections sortable.
	//----------------------------------------------------------------------+
	$( "#accordion" )
			.accordion({
				header: "> .group > .handle",
				heightStyle: "content"
			})
			.sortable({
				axis: "y",
				handle: ".handle",
				stop: function( event, ui ) {
					// IE doesn't register the blur event when sorting
					// so trigger focusout handler to remove .ui-state-focus
					ui.item.children(".handle").triggerHandler("focusout");
				}
			});

	//----------------------------------------------------------------------+
	// Fetch the JSON configuration file and use it to build the 
	// accordion sections
	//----------------------------------------------------------------------+
	$.getJSON("data/event.json", function (data) {
		$.each(data, function (key, val) {
			$("#accordion").append(addBout(val, key)).accordion("refresh");

			//--------------------------------------------------------------+
			// For each bout - dynamically bind the score button event 
			// handler to this dynamic method
			//--------------------------------------------------------------+
			var BlueTotal = 0;
			var RedTotal = 0;
			var BoutNumber = (key+1);
			$( "#boutResult_"+(BoutNumber) ).hide();
			$( "#btnScore_"+(BoutNumber) ).click(function() {
			// Get the current round number.
			var currentRound = 0;
			currentRound = $("#current_round_" + (BoutNumber)).text();

			//----------------------------------------------------------+
			// Fetch the score for the current round and post to 
			// scoring table
			//----------------------------------------------------------+
			var BlueScoreThisRound =  $("#blueRoundScore_" + 
				                         BoutNumber).val();
			var RedScoreThisRound =   $("#redRoundScore_" + 
				                         BoutNumber).val();
			$("#bout_" + (BoutNumber) + "_round_" + currentRound + 
			   "_blueScore").text(BlueScoreThisRound);
			$("#bout_" + (BoutNumber) + "_round_" + currentRound + 
				"_redScore").text(RedScoreThisRound);
			// If it's the FIRST round.
			if (currentRound == 1) {
				$("#bout_" + (BoutNumber) + "_round_" + currentRound + 
				  "_blueTotal").text(BlueScoreThisRound);
				$("#bout_" + (BoutNumber) + "_round_" + currentRound + 
					"_redTotal").text(RedScoreThisRound);
				BlueTotal = BlueScoreThisRound;
				RedTotal = RedScoreThisRound;
			}
			// All except the first round.
			else {

				 BlueTotal = parseInt($("#bout_" + (BoutNumber) + 
				 	"_round_" + (currentRound-1)  + 
				 	"_blueTotal").text());
				 BlueTotal += parseInt(BlueScoreThisRound);
				 $("#bout_" + (BoutNumber) + 
				 	"_round_" + (currentRound)  + 
				 	"_blueTotal").text(BlueTotal);

				 RedTotal = parseInt($("#bout_" + (BoutNumber) + 
				 	"_round_" + (currentRound-1)  + 
				 	"_redTotal").text());
				 RedTotal += parseInt(RedScoreThisRound);
				 $("#bout_" + (BoutNumber) + "_round_" + 
				 	(currentRound)  + "_redTotal").text(RedTotal);
			}

			// Blue wins the round.
			if (BlueScoreThisRound < RedScoreThisRound) {
			  $("#bout_" + (BoutNumber) + "_round_" + currentRound + 
			  	"_roundbox").css("border", "2px solid blue");
			}
			// Red wins the round.
			else if (BlueScoreThisRound > RedScoreThisRound) {
				$("#bout_" + (BoutNumber) + "_round_" + currentRound + 
					"_roundbox").css("border", "2px solid red");
			}
			// Round is a draw
			else if (BlueScoreThisRound == RedScoreThisRound) {
				$("#bout_" + (BoutNumber) + "_round_" + currentRound + 
					"_roundbox").css("border", "2px solid green");
			}

			// Who's winning the fight after this round ?
			if(RedTotal == BlueTotal) {
				$("#roundTitle_" + (BoutNumber) + "_" + 
					currentRound).css("border", "2px solid green");
				$("#bout_" + (BoutNumber) + "_round_" + 
					currentRound + 
					"_totalbox").css("border", "2px solid green");
			}
			if(BlueTotal < RedTotal) {
			   $("#roundTitle_" + (BoutNumber) + "_" + 
			   	currentRound).css("border", "2px solid red");
			   $("#bout_" + (BoutNumber) + "_round_" + 
			   	currentRound + "_totalbox").css("border", "2px solid red");
			}
			if (BlueTotal > RedTotal) {
				$("#roundTitle_" + (BoutNumber) + "_" + 
					currentRound).css("border", "2px solid blue");
				$("#bout_" + (BoutNumber) + "_round_" + 
					currentRound + 
					"_totalbox").css("border", "2px solid blue");
			}


			//----------------------------------------------------------+
			// Reset current round scores t 10-10, hide button if all 
			// rounds scored
			//----------------------------------------------------------+
			$("#blueRoundScore_" + BoutNumber).val(10);
			$("#redRoundScore_" + BoutNumber).val(10);
			currentRound++;
			// The fight is over.
			if (currentRound > val.numberofrounds) {
				$("#blueRoundScore_" + BoutNumber).val(BlueTotal);
				$("#blueRoundScore_" + BoutNumber).prop('disabled', true);
				$("#redRoundScore_" + BoutNumber).val(RedTotal);
				$("#redRoundScore_" + BoutNumber).prop('disabled', true);
				$( "#btnScore_"+(BoutNumber)).hide();
				if(BlueTotal == RedTotal) {
					$( "#boutResult_"+(BoutNumber) ).text("<<<  Draw  >>>");
				}
				else if (BlueTotal > RedTotal) {
					$( "#boutResult_"+(BoutNumber) ).html("&larr; WINNER");
					$( "#boutResult_"+
						(BoutNumber) ).css("background-color", "blue");
					$( "#boutResult_"+(BoutNumber) ).css("color", "white");
				}
				else if (RedTotal > BlueTotal) {
					$( "#boutResult_"+(BoutNumber) ).html("WINNER &rarr;");
					$( "#boutResult_"+
						(BoutNumber) ).css("background-color", "red");
					$( "#boutResult_"+(BoutNumber) ).css("color", "white");
				}

				$( "#roundLabel_"+(BoutNumber) ).text("Fight");
				$( "#current_round_"+(BoutNumber) ).text("Complete");
				$( "#boutResult_"+(BoutNumber) ).show();

			}
			// On to another round.
			else {
				$("#current_round_" + (key+1)).text(currentRound);
			}
			});
		});

	});

});

</script>
</head>

<body>
<div id="main-container" class="main-container">
	<div id="accordion">
	<!-- Bouts will be inserted here -->
	</div>


</div>
</body>
</html>


style.css


body {
	font-family: "Trebuchet MS","Helvetica","Arial","Verdana","sans-serif";
	font-size: 62.5%;
    background: url('../images/background.gif') repeat;
}

td {
    text-align: center;
    vertical-align: middle;
    border: 1px solid black;
    width: 90px;
}

input {
    text-align: center;
}


.main-container {
    width: 96%;
    max-width: 960px;
    min-width: 300px;
    height: 100px;
    position: absolute;
    top:20px;
    left: 0;
    right: 0;
    margin: auto;
}

/*---------------------------------------*/
/*   Styles for table like formatting.   */
/*---------------------------------------*/
.table {
    display: table;
    width: 100%;
}
.row {
    display: table-row;
}
.cell {
    display: table-cell;
}

/*---------------------------------------*/
/*   Styles for score area.              */
/*---------------------------------------*/

.score_box {
    border:2px solid black;
    padding: 5px;
}

.score_button {
    width: 130px;
    height: 40px;
}

.boutResultClass {
    border: 2px solid black;
    background-color: yellow;
    padding-left: 20px;
    padding-right: 20px;
    padding-top: 10px;
    padding-bottom: 10px;
}

/*---------------------------------------*/
/*   Styles for width specification.     */
/*---------------------------------------*/
.width150 {
    width: 150px;
}

.width33pct {
    width: 33%;
}

.width34pct {
    width: 34%;
}

.width100pct {
    width: 100%;
}

/*---------------------------------------*/
/*   Styles for various alignments.      */
/*---------------------------------------*/
.align-right {
    text-align: right;
}
.align-left {
    text-align: left;
}
.align-center {
    text-align: center;
}

.valign-middle {
    vertical-align: middle;
}

/*-------------------------------------------*/
/*   Style overrides for jQuery accordion.   */
/*-------------------------------------------*/
.ui-state-default{
    background-color: #3173a5;
    background-image: none;
}
.ui-accordion-header {
    background-color: #333333;
    color:  #BDC3C7;
    font-size: 16;
}

.ui-accordion-header.ui-state-active {
    background-color: #BDC3C7;
    color: #333333;
    font-size: 16;
}

You can download a .zip file without the funky source code formatting that my blog requires [ HERE ]


So what’s next ?

Next I’ll create services so that users can all score an event in real time, collaboratively while the event is happening live.

After I create the services and update the client to send scores, I’ll build iOS, Android & Windows clients !

Tailspin Spyworks Ecommerce Application and Tutorial Updated on CodePlex !

Just a note to let folks know that I’ve updated both the code and the tutorial for the tailspin Spyworks sample app at http://tailspinspyworks.codeplex.com/.

It includes bug fixes, additional step details in the tutorial as well as complete database schema information.

Enjoy and feel free to send comments and suggestions.

Updating the Tailspin Spyworks Tutorial Application.

I’m planning an overdue update to the Tailspin Spyworks Tutorial and applicaiton.

[ Click HERE for the Tailspin Spyworks Home Page ]

I intend there to be two phases.

1.) Bug fixes

And then in a subsequent release….

2.) A few feature enhancements.

I’ll be reviewing all the comments and re-editing the .pdf.

If you have any additional requests / suggestions please send them to me.

[ Click HERE for my contact form. ]

THANKS !

Introducing Azure Issue Tracker Sample Application

The Azure Services Evangelism team is pleased to announce the release of the Azure Issue Tracker sample application.

The Azure Issue Tracker demonstrates a real-world ISV scenario where you want to create and host a SaaS application for your consumers. This sample is being released in two versions: Standard and Enterprise. The Standard version allows ad-hoc users to use Windows LiveID federation with the .NET Access Control Service and authorize other Windows LiveID users. This allows small groups of users to quickly provision projects and issue tracking capabilities while using the rich claims-based authorization model and flexible entity storage.

The Cloud is the NEW SOA :)

http://www.codeplex.com/azureissuetracker

.NET StockTrader Sample Application

An End-to-End Sample Application Illustrating Windows Communication Foundation and .NET Enterprise Technologies

This application is an end-to-end sample application for .NET Enterprise Application Server technologies. It is a service-oriented application based on Windows Communication Foundation (.NET 3.0) and ASP.NET, and illustrates many of the .NET enterprise development technologies for building highly scalable, rich “enterprise-connected” applications. It is designed as a benchmark kit to illustrate alternative technologies within .NET and their relative performance.

The application offers full interoperability with Java Enterprise, including IBM WebSphere’s Trade 6.1 sample application, and newly provided implementations on Oracle Application Server 10G (OC4J) and Oracle WebLogic Server 10.3 (Oracle implementations included with the download below). As such, the application offers an excellent opportunity for developers to learn about .NET and building interoperable, service-oriented applications.

Screenshot: .NET StockTrader Smart Client
.NET StockTrader Smart Client

Screenshot: .NET StockTrader Web Application
.NET StockTrader Web Application

Very Cool !

Click [ HERE ] to get it !

Code Snippets from DotNetSackers

DotNetSlackers.com

The slackers are at it again !

Check out their growing collection of swipe-able code !

 http://www.DotNetSlackers.com/CodeSnippets/

AJAX Predictive Fetch – Webcast Materials

Many thanks to everyone who attended.

Here is the source code and PowerPoint. Please feel free to post questions.

LFR-PredictiveFetch.zip