JSFiddle - React, Tailwind, and code Playground
HTML
<script src="http://documentcloud.github.com/underscore/underscore.js"></script>
<script src="https://raw.github.com/gist/1618986/6ca0aa728079dd843058ba1129bf39e88579b5c3/cartModule.js"></script>
<script src="https://gist.github.com/raw/1618986/551ed5b83afff9faaabd67696ee4f6827f1ca44c/productModule.js"></script>
<script src="https://gist.github.com/raw/1618986/093e9465337cea5b45b982c52144b543457aa34d/repositoryModule.js"></script>
<script src="https://gist.github.com/raw/1618986/d42f11f2efeca85ef68af3752ceed37657b377d2/summaryModule.js"></script>
<script src="https://raw.github.com/postaljs/postal.js/master/lib/postal.js"></script>
<script src="https://raw.github.com/postaljs/postal.js/master/example/standard/js/postal.diagnostics.js"></script>
<script src="https://raw.github.com/appendto/amplify/master/lib/amplify.core.js"></script>
<script src="https://raw.github.com/appendto/amplify/master/lib/amplify.request.js"></script>
<script type="text/html" id="product-list-tmpl">
<div class="header">Products</div>
<ul id="products">
<% _.each(items, function(item) { %>
<li id="<%= item.id %>"><%= item.description %> ($<%= parseFloat(item.price).toFixed(2) %>)</li>
<% }); %>
</ul>
</script>
<script type="text/html" id="cart-tmpl">
<div class="header">Shopping Cart</div>
<ul id="cart-list"></ul>
</script>
<script type="text/html" id="cart-item-tmpl">
<li id="<%= cartId %>-<%= productId %>">
<%= description %>
<span class='qty'>(qty: <span class='qty-val'><%= qty %> = $<%= (qty * price).toFixed(2) %></span>)</span>
</li>
</script>
<script type="text/html" id="summary-tmpl">
<div class="header">Summary</div>
<ul id="summary-list"></ul>
</script>
<script type="text/html" id="summary-item-tmpl">
<div id="<%= id %>" class="msg"><%= msg %></div>
</script>
<script type="text/html" id="application-tmpl">
<h4><%= title %></h4>
...
CSS
body {
font-family: Tahoma, Arial, sans-serif;
}
h3 {
margin-bottom: 10px;
}
h4 {
text-align: center;
width:510px;
margin-bottom:15px;
}
#products-wrapper {
float:left;
margin-right: 10px;
width:250px;
margin-bottom: 25px;
}
#products li {
margin-left: 5px;
}
.tool-tip {
display: none;
font-style: italic;
color: silver;
font-size: 10pt;
float: right;
margin-right: 5px;
}
.qty {
font-style: italic;
color: dimGray;
float: right;
margin-right: 5px;
}
#cart-wrapper {
float:left;
width: 250px;
}
.header {
background-color: lightsteelblue;
text-align: center;
font-size: 14pt;
border-radius: 5px;
padding: 2px;
font-variant: small-caps;
}
#ctrls {
clear:both;
text-align: center;
width: 510px;
}
#ctrls input[type=button] {
margin: 5px;
padding: 5px;
width: 100px;
}
#summary {
clear:both;
width:510px;
text-align: center;
margin-bottom: 10px;
}
#summary .msg {
padding: 5px;
}
JavaScript
// this function will eventually be moved into postal as a new feature
var linkExchanges = function(source, destination) {
if(!_.isArray(destination)) {
destination = [destination];
}
_.each(destination, function(item) {
postal.subscribe(source.exchange, source.topic, function(msg) {
postal.publish(item.exchange, item.topic, msg);
});
});
};
linkExchanges({ exchange: "productlist", topic: "request.getproductlist" },
{ exchange: "repository", topic: "get.productlist" });
linkExchanges({ exchange: "repository", topic: "response.productlist" },
{ exchange: "productlist", topic: "populate" });
linkExchanges( { exchange: "productlist", topic: "item.selected" },
[{ exchange: "cart", topic: "item.add" },
{ exchange: "summary", topic: "qtychange" },
{ exchange: "summary", topic: "ordertotalchange" }]);
linkExchanges( { exchange: "application", topic: "init" },
[{ exchange: "productlist", topic: "init" },
{ exchange: "cart", topic: "init" },
{ exchange: "summary", topic: "init" }]);
var cartApplication = (function(postal, $){
var cartAppTemplate = "#application-tmpl",
CartApplicationPrototype = {
init: function() {
if(!this.modulesLoaded) {
this.productList = productListModule.start("#products-wrapper");
this.cart = cartModule.start("#cart-wrapper");
this.summary = summaryModule.start("#summary");
this.modulesLoaded = true;
}
this.proxy.render(this);
postal.publish("application", "init", {});
}
},
CartApplication = function(domProxy) {
this.title = "A Contrived Shopping Cart";
this.modulesLoaded = false;
this.productList = null;
...