Compare commits
No commits in common. "master" and "834316a5a4150e1ea55c1868dd56349a0d1f834d" have entirely different histories.
master
...
834316a5a4
|
@ -1,29 +0,0 @@
|
||||||
.container-fluid {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.cart-unit {
|
|
||||||
width: 80px;
|
|
||||||
}
|
|
||||||
.btn-xs {
|
|
||||||
font-size: 8px;
|
|
||||||
}
|
|
||||||
.list-group-item {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.list-group-item h4 {
|
|
||||||
float: left;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
.list-group-item .input-group-addon {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.btn-group-vertical > .btn-default {
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
.form-control[disabled],
|
|
||||||
.form-control[readonly] {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
|
@ -4,32 +4,93 @@
|
||||||
<title>KO Shopping Cart</title>
|
<title>KO Shopping Cart</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
|
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="css/style.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid">
|
<h1>Catalog</h1>
|
||||||
<div class="row" data-bind="if: visibleCatalog">
|
|
||||||
<div class="col-xs-12" data-bind="template:{name:'header'}"></div>
|
<h2>Insert</h2>
|
||||||
<div class="col-xs-6" data-bind="template:{name:'catalog'}"></div>
|
<form class="form-horizontal" role="form" data-bind="with:newProduct">
|
||||||
<div class="col-xs-6" data-bind="if: visibleCart">
|
<div class="form-group">
|
||||||
<div class="well" data-bind="template:{name:'cart'}"></div>
|
<div class="col-sm-12">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Name"
|
||||||
|
data-bind="textInput:name"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="row" data-bind="ifnot: visibleCatalog">
|
<div class="form-group">
|
||||||
<div data-bind="template:{name:'order'}"></div>
|
<div class="col-sm-12">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Price"
|
||||||
|
data-bind="textInput:price"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div data-bind="template: {name:'add-to-catalog-modal'}"></div>
|
</div>
|
||||||
<div data-bind="template: {name:'finish-order-modal'}"></div> -->
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Stock"
|
||||||
|
data-bind="textInput:stock"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-default"
|
||||||
|
data-bind="{click:$parent.addProduct}"
|
||||||
|
>
|
||||||
|
<i class="glyphicon glyphicon-plus-sign"> </i> Add Product
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Search</h2>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<i class="glyphicon glyphicon-search"></i> Search</span
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
data-bind="textInput: searchTerm"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2>Products</h2>
|
||||||
|
<strong>Items:</strong>
|
||||||
|
<span data-bind="text:catalog().length"></span>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Stock</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody data-bind="foreach:catalog">
|
||||||
|
<tr>
|
||||||
|
<td data-bind="text:name"></td>
|
||||||
|
<td data-bind="text:price"></td>
|
||||||
|
<td data-bind="text:stock"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<!-- vendor library -->
|
<!-- vendor library -->
|
||||||
<script type="text/javascript" src="js/vendors/jquery-2.1.1.min.js"></script>
|
<script type="text/javascript" src="js/jquery-2.1.1.min.js"></script>
|
||||||
<script type="text/javascript" src="js/vendors/bootstrap.js"></script>
|
<script type="text/javascript" src="js/bootstrap.js"></script>
|
||||||
<script type="text/javascript" src="js/vendors/knockout-3.2.0.js"></script>
|
<script type="text/javascript" src="js/knockout-3.2.0.js"></script>
|
||||||
<script type="text/javascript" src="js/vendors/koExternalTemplateEngine_all.js"></script>
|
|
||||||
<!-- app -->
|
<!-- app -->
|
||||||
<script type="text/javascript" src="js/models/Product.js"></script>
|
<script type="text/javascript" src="js/models/product.js"></script>
|
||||||
<script type="text/javascript" src="js/models/CartProduct.js"></script>
|
|
||||||
<script type="text/javascript" src="js/viewmodel.js"></script>
|
<script type="text/javascript" src="js/viewmodel.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
var CartProduct = function (product, units) {
|
|
||||||
"unit strict";
|
|
||||||
|
|
||||||
// Each cart product is composed of the product and units of the product we want to buy
|
|
||||||
var _product = product,
|
|
||||||
_units = ko.observable(units); // number of units to add or remove
|
|
||||||
|
|
||||||
var subtotal = ko.computed(function() {
|
|
||||||
return _product.price() * _units();
|
|
||||||
});
|
|
||||||
|
|
||||||
var addUnit = function () {
|
|
||||||
var u = _units(); // read number of units from observable
|
|
||||||
var _stock = product.stock();
|
|
||||||
if (_stock === 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_units(u + 1); // assign observable with new unit
|
|
||||||
_product.stock(--_stock); // reduce inventory of stock by decrease unit from product
|
|
||||||
};
|
|
||||||
|
|
||||||
var removeUnit = function () {
|
|
||||||
var u = _units();
|
|
||||||
var _stock = _product.stock();
|
|
||||||
if (u === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_units(u - 1);
|
|
||||||
_product.stock(++_stock);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
product: _product,
|
|
||||||
units: _units,
|
|
||||||
subtotal: subtotal,
|
|
||||||
addUnit: addUnit,
|
|
||||||
removeUnit: removeUnit
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,14 +5,10 @@ var Product = function (id, name, price, stock) {
|
||||||
_name = ko.observable(name),
|
_name = ko.observable(name),
|
||||||
_price = ko.observable(price),
|
_price = ko.observable(price),
|
||||||
_stock = ko.observable(stock);
|
_stock = ko.observable(stock);
|
||||||
var _lineColor = ko.computed(function() {
|
|
||||||
return (_stock() < 5) ? 'red' : 'black';
|
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
id: _id,
|
id: _id,
|
||||||
name: _name,
|
name: _name,
|
||||||
price: _price,
|
price: _price,
|
||||||
stock: _stock,
|
stock: _stock,
|
||||||
lineColor: _lineColor
|
|
||||||
};
|
};
|
||||||
};
|
};
|
|
@ -1,317 +0,0 @@
|
||||||
(function($, undefined) {
|
|
||||||
/*
|
|
||||||
TrafficCop
|
|
||||||
Author: Jim Cowart
|
|
||||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
|
||||||
Version 0.3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
var inProgress = {};
|
|
||||||
|
|
||||||
$.trafficCop = function(url, options) {
|
|
||||||
var reqOptions = url, key;
|
|
||||||
if(arguments.length === 2) {
|
|
||||||
reqOptions = $.extend(true, options, { url: url });
|
|
||||||
}
|
|
||||||
key = JSON.stringify(reqOptions);
|
|
||||||
if (key in inProgress) {
|
|
||||||
for (var i in {success: 1, error: 1, complete: 1}) {
|
|
||||||
inProgress[key][i](reqOptions[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inProgress[key] = $.ajax(reqOptions).always(function () { delete inProgress[key]; });
|
|
||||||
}
|
|
||||||
return inProgress[key];
|
|
||||||
};
|
|
||||||
|
|
||||||
})(jQuery);
|
|
||||||
|
|
||||||
(function($, global, undefined) {
|
|
||||||
/*
|
|
||||||
infuser.js
|
|
||||||
Author: Jim Cowart
|
|
||||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
|
||||||
Version 0.2.0
|
|
||||||
*/
|
|
||||||
var hashStorage = {
|
|
||||||
templates: {},
|
|
||||||
|
|
||||||
storeTemplate: function(templateId, templateContent) {
|
|
||||||
this.templates[templateId] = templateContent;
|
|
||||||
},
|
|
||||||
|
|
||||||
getTemplate: function(templateId) {
|
|
||||||
return this.templates[templateId];
|
|
||||||
},
|
|
||||||
|
|
||||||
purge: function() {
|
|
||||||
this.templates = {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var scriptStorage = {
|
|
||||||
templateIds: [],
|
|
||||||
storeTemplate: function(templateId, templateContent) {
|
|
||||||
var node = document.getElementById(templateId);
|
|
||||||
if(node === null) {
|
|
||||||
this.templateIds.push(templateId);
|
|
||||||
node = document.createElement("script");
|
|
||||||
node.type = "text/html";
|
|
||||||
node.id = templateId;
|
|
||||||
document.body.appendChild(node);
|
|
||||||
}
|
|
||||||
node.text = templateContent;
|
|
||||||
},
|
|
||||||
|
|
||||||
getTemplate: function(templateId) {
|
|
||||||
return document.getElementById(templateId);
|
|
||||||
},
|
|
||||||
|
|
||||||
purge: function() {
|
|
||||||
for(var i = 0; i < this.templateIds.length; i++) {
|
|
||||||
document.body.removeChild(document.getElementById(this.templateIds[i]));
|
|
||||||
}
|
|
||||||
this.templateIds = [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var errorHtml = "<div class='infuser-error'>The template <a href='{TEMPLATEURL}'>{TEMPLATEID}</a> could not be loaded. {STATUS}</div>",
|
|
||||||
returnErrorTemplate = function(status, templateId, templatePath) {
|
|
||||||
return errorHtml.replace('{STATUS}', status).replace('{TEMPLATEID}', templateId).replace('{TEMPLATEURL}', templatePath);
|
|
||||||
},
|
|
||||||
errors = [];
|
|
||||||
var helpers = {
|
|
||||||
getTemplatePath: function(templateOptions) {
|
|
||||||
var templateFile = templateOptions.templatePrefix + templateOptions.templateId + templateOptions.templateSuffix;
|
|
||||||
return templateOptions.templateUrl === undefined || templateOptions.templateUrl === "" ?
|
|
||||||
templateFile : templateOptions.templateUrl + "/" + templateFile;
|
|
||||||
},
|
|
||||||
templateGetSuccess: function(templateId, callback) {
|
|
||||||
return function(response) {
|
|
||||||
infuser.store.storeTemplate(templateId, response);
|
|
||||||
callback(infuser.store.getTemplate(templateId));
|
|
||||||
};
|
|
||||||
},
|
|
||||||
templateGetError: function(templateId, templatePath, callback) {
|
|
||||||
return function(exception) {
|
|
||||||
if($.inArray(templateId, errors) === -1) {
|
|
||||||
errors.push(templateId);
|
|
||||||
}
|
|
||||||
var templateHtml = returnErrorTemplate("HTTP Status code: " + exception.status, templateId, templatePath);
|
|
||||||
infuser.store.storeTemplate(templateId, templateHtml);
|
|
||||||
callback(infuser.store.getTemplate(templateId));
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getAjaxOptions: function(templateOptions) {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
infuserOptions = ['target','loadingTemplate','postRender','preRender','render','bindingInstruction','useLoadingTemplate','model','templateUrl','templateSuffix','templatePrefix',''];
|
|
||||||
var infuser = {
|
|
||||||
storageOptions: {
|
|
||||||
hash: hashStorage,
|
|
||||||
script: scriptStorage
|
|
||||||
},
|
|
||||||
|
|
||||||
store: hashStorage,
|
|
||||||
|
|
||||||
defaults: {
|
|
||||||
// Template name conventions
|
|
||||||
templateUrl: "",
|
|
||||||
templateSuffix: ".html",
|
|
||||||
templatePrefix: "",
|
|
||||||
// AJAX Options
|
|
||||||
ajax: {
|
|
||||||
"async": true,
|
|
||||||
"dataType": "html",
|
|
||||||
"type": "GET"
|
|
||||||
},
|
|
||||||
// infuse() specific options - NOT used for "get" or "getSync"
|
|
||||||
target: function(templateId) { return "#" + templateId; }, // DEFAULT MAPPING
|
|
||||||
loadingTemplate: {
|
|
||||||
content: '<div class="infuser-loading">Loading...</div>',
|
|
||||||
transitionIn: function(target, content) {
|
|
||||||
var tgt = $(target);
|
|
||||||
tgt.hide();
|
|
||||||
tgt.html(content);
|
|
||||||
tgt.fadeIn();
|
|
||||||
},
|
|
||||||
transitionOut: function(target) {
|
|
||||||
$(target).html("");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postRender: function(targetElement) { }, // NO_OP effectively by default
|
|
||||||
preRender: function(targetElement, template) { }, // NO_OP effectively by default
|
|
||||||
render: function(target, template) {
|
|
||||||
var tgt = $(target);
|
|
||||||
if(tgt.children().length === 0) {
|
|
||||||
tgt.append($(template));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tgt.children().replaceWith($(template));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bindingInstruction: function(template, model) { return template; }, // NO_OP effectively by default
|
|
||||||
useLoadingTemplate: true // true/false
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function(options, callback) {
|
|
||||||
var templateOptions = $.extend({}, infuser.defaults, (typeof options === "object" ? options : { templateId: options })),
|
|
||||||
template;
|
|
||||||
templateOptions.ajax.url = helpers.getTemplatePath(templateOptions);
|
|
||||||
template = infuser.store.getTemplate(templateOptions.ajax.url);
|
|
||||||
if(!template || $.inArray(templateOptions.ajax.url, errors) !== -1) {
|
|
||||||
templateOptions.ajax.success = helpers.templateGetSuccess(templateOptions.ajax.url, callback);
|
|
||||||
templateOptions.ajax.error = helpers.templateGetError(templateOptions.templateId, templateOptions.ajax.url, callback);
|
|
||||||
$.trafficCop(templateOptions.ajax);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback(template);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getSync: function(options) {
|
|
||||||
var templateOptions = $.extend({}, infuser.defaults, (typeof options === "object" ? options : { templateId: options }), { ajax: { async: false } }),
|
|
||||||
template,
|
|
||||||
templateHtml;
|
|
||||||
templateOptions.ajax.url = helpers.getTemplatePath(templateOptions);
|
|
||||||
template = infuser.store.getTemplate(templateOptions.ajax.url);
|
|
||||||
if(!template || $.inArray(templateOptions.ajax.url, errors) !== -1) {
|
|
||||||
templateHtml = null;
|
|
||||||
templateOptions.ajax.success = function(response) { templateHtml = response; };
|
|
||||||
templateOptions.ajax.error = function(exception) {
|
|
||||||
if($.inArray(templateOptions.ajax.url) === -1) {
|
|
||||||
errors.push(templateOptions.ajax.url);
|
|
||||||
}
|
|
||||||
templateHtml = returnErrorTemplate("HTTP Status code: exception.status", templateOptions.templateId, templateOptions.ajax.url);
|
|
||||||
};
|
|
||||||
$.ajax(templateOptions.ajax);
|
|
||||||
if(templateHtml === null) {
|
|
||||||
templateHtml = returnErrorTemplate("An unknown error occurred.", templateOptions.templateId, templateOptions.ajax.url);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
infuser.store.storeTemplate(templateOptions.ajax.url, templateHtml);
|
|
||||||
template = infuser.store.getTemplate(templateOptions.ajax.url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return template;
|
|
||||||
},
|
|
||||||
|
|
||||||
infuse: function(templateId, renderOptions) {
|
|
||||||
var templateOptions = $.extend({}, infuser.defaults, (typeof templateId === "object" ? templateId : renderOptions), (typeof templateId === "string" ? { templateId: templateId } : undefined )),
|
|
||||||
targetElement = typeof templateOptions.target === 'function' ? templateOptions.target(templateId) : templateOptions.target;
|
|
||||||
if(templateOptions.useLoadingTemplate) {
|
|
||||||
templateOptions.loadingTemplate.transitionIn(targetElement, templateOptions.loadingTemplate.content);
|
|
||||||
}
|
|
||||||
infuser.get(templateOptions, function(template) {
|
|
||||||
var _template = template;
|
|
||||||
templateOptions.preRender(targetElement, _template);
|
|
||||||
_template = templateOptions.bindingInstruction(_template, templateOptions.model);
|
|
||||||
if(templateOptions.useLoadingTemplate) {
|
|
||||||
templateOptions.loadingTemplate.transitionOut(targetElement);
|
|
||||||
}
|
|
||||||
templateOptions.render(targetElement, _template);
|
|
||||||
templateOptions.postRender(targetElement);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
global.infuser = infuser; })(jQuery, window);
|
|
||||||
|
|
||||||
|
|
||||||
// Knockout External Template Engine
|
|
||||||
// Author: Jim Cowart
|
|
||||||
// License: MIT (http://www.opensource.org/licenses/mit-license)
|
|
||||||
// Version 2.0.5
|
|
||||||
|
|
||||||
|
|
||||||
(function ( global, ko, jQuery, infuser, undefined ) {
|
|
||||||
|
|
||||||
var ExternalTemplateSource = function(templateId, options) {
|
|
||||||
var self = this, origAfterRender;
|
|
||||||
self.templateId = templateId;
|
|
||||||
self.loaded = false;
|
|
||||||
self.template = ko.observable(infuser.defaults.useLoadingTemplate ? infuser.defaults.loadingTemplate.content : undefined);
|
|
||||||
self.template.data = {};
|
|
||||||
self.options = ko.utils.extend({},options);
|
|
||||||
self.options.templateId = templateId;
|
|
||||||
if(self.options && self.options.afterRender) {
|
|
||||||
origAfterRender = self.options.afterRender;
|
|
||||||
self.options.afterRender = function() {
|
|
||||||
if(self.loaded) {
|
|
||||||
origAfterRender.apply(self.options, arguments);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ko.utils.extend(ExternalTemplateSource.prototype, {
|
|
||||||
data: function(key, value) {
|
|
||||||
if (arguments.length === 1) {
|
|
||||||
if(key === "precompiled") {
|
|
||||||
this.template();
|
|
||||||
}
|
|
||||||
return this.template.data[key];
|
|
||||||
}
|
|
||||||
this.template.data[key] = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
text: function(value) {
|
|
||||||
if (!this.loaded) {
|
|
||||||
this.getTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
return this.template();
|
|
||||||
} else {
|
|
||||||
this.template(arguments[0]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getTemplate: function() {
|
|
||||||
var self = this;
|
|
||||||
infuser.get(self.options, function(tmpl) {
|
|
||||||
self.data("precompiled",null);
|
|
||||||
self.template(tmpl);
|
|
||||||
self.loaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var KoExternalTemplateEngine = function(koEngineType) {
|
|
||||||
var engine = koEngineType ? new koEngineType() : new ko.nativeTemplateEngine();
|
|
||||||
engine.templates = {};
|
|
||||||
engine.makeTemplateSource = function(template, bindingContext, options) {
|
|
||||||
// Named template
|
|
||||||
if (typeof template == "string") {
|
|
||||||
var elem = document.getElementById(template);
|
|
||||||
if (elem)
|
|
||||||
return new ko.templateSources.domElement(elem);
|
|
||||||
else {
|
|
||||||
if(!engine.templates[template]) {
|
|
||||||
engine.templates[template] = new ExternalTemplateSource(template, options);
|
|
||||||
}
|
|
||||||
return engine.templates[template];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((template.nodeType == 1) || (template.nodeType == 8)) {
|
|
||||||
// Anonymous template
|
|
||||||
return new ko.templateSources.anonymousTemplate(template);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
engine.renderTemplate = function (template, bindingContext, options) {
|
|
||||||
var templateSource = engine.makeTemplateSource(template, bindingContext, options);
|
|
||||||
return engine.renderTemplateSource(templateSource, bindingContext, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
return engine;
|
|
||||||
};
|
|
||||||
|
|
||||||
ko.KoExternalTemplateEngine = KoExternalTemplateEngine;
|
|
||||||
|
|
||||||
if (jQuery.tmpl && jQuery.tmpl.tag.tmpl.open.toString().indexOf('__') >= 0) {
|
|
||||||
ko.setTemplateEngine(new KoExternalTemplateEngine(ko.jqueryTmplTemplateEngine));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ko.setTemplateEngine(new KoExternalTemplateEngine());
|
|
||||||
}
|
|
||||||
|
|
||||||
})( window, ko, jQuery, infuser );
|
|
|
@ -1,9 +1,5 @@
|
||||||
var vm = (function () {
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
var vm = (function () {
|
||||||
var visibleCatalog = ko.observable(true);
|
|
||||||
var visibleCart = ko.observable(false);
|
|
||||||
|
|
||||||
var catalog = ko.observableArray([
|
var catalog = ko.observableArray([
|
||||||
Product(1, "T-Shirt", 10.0, 20),
|
Product(1, "T-Shirt", 10.0, 20),
|
||||||
Product(2, "Trousers", 20.0, 10),
|
Product(2, "Trousers", 20.0, 10),
|
||||||
|
@ -21,16 +17,9 @@ var vm = (function () {
|
||||||
var addProduct = function (context) {
|
var addProduct = function (context) {
|
||||||
var id = new Date().valueOf(); // random id from time
|
var id = new Date().valueOf(); // random id from time
|
||||||
|
|
||||||
var product = Product(
|
var product = Product(id, context.name(), context.price(), context.stock());
|
||||||
id,
|
|
||||||
context.name(),
|
|
||||||
context.price(),
|
|
||||||
context.stock()
|
|
||||||
);
|
|
||||||
catalog.push(product);
|
catalog.push(product);
|
||||||
clearNewProduct();
|
clearNewProduct();
|
||||||
$('#addToCatalogModal').modal('hide');
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var searchTerm = ko.observable("");
|
var searchTerm = ko.observable("");
|
||||||
|
@ -47,106 +36,16 @@ var vm = (function () {
|
||||||
//filter data
|
//filter data
|
||||||
var filtered = ko.utils.arrayFilter(catalog(), function (item) {
|
var filtered = ko.utils.arrayFilter(catalog(), function (item) {
|
||||||
var strProp = ko.unwrap(item["name"]).toLocaleLowerCase();
|
var strProp = ko.unwrap(item["name"]).toLocaleLowerCase();
|
||||||
return strProp.indexOf(filter) > -1;
|
return (strProp.indexOf(filter) > -1);
|
||||||
});
|
});
|
||||||
return filtered;
|
return filtered;
|
||||||
});
|
});
|
||||||
|
|
||||||
var cart = ko.observableArray([]);
|
|
||||||
var showCartDetails = function () {
|
|
||||||
if (cart().length > 0) {
|
|
||||||
visibleCart(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var addToCart = function (data) {
|
|
||||||
var item = null;
|
|
||||||
var tmpCart = cart();
|
|
||||||
var n = tmpCart.length;
|
|
||||||
while (n--) {
|
|
||||||
if (tmpCart[n].product.id() === data.id()) {
|
|
||||||
item = tmpCart[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item) {
|
|
||||||
item.addUnit();
|
|
||||||
} else {
|
|
||||||
item = new CartProduct(data, 0);
|
|
||||||
item.addUnit();
|
|
||||||
tmpCart.push(item);
|
|
||||||
}
|
|
||||||
cart(tmpCart);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The cart-widget template
|
|
||||||
var totalItems = ko.computed(function () {
|
|
||||||
var tmpCart = cart();
|
|
||||||
var total = 0;
|
|
||||||
tmpCart.forEach(function (item) {
|
|
||||||
total += parseInt(item.units(), 10);
|
|
||||||
});
|
|
||||||
return total;
|
|
||||||
});
|
|
||||||
var grandTotal = ko.computed(function () {
|
|
||||||
var tmpCart = cart();
|
|
||||||
var total = 0;
|
|
||||||
tmpCart.forEach(function (item) {
|
|
||||||
total += item.units() * item.product.price();
|
|
||||||
});
|
|
||||||
return total;
|
|
||||||
});
|
|
||||||
|
|
||||||
// The cart-item template
|
|
||||||
var removeFromCart = function (data) {
|
|
||||||
var units = data.units();
|
|
||||||
var stock = data.product.stock();
|
|
||||||
data.product.stock(units + stock);
|
|
||||||
cart.remove(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The cart template
|
|
||||||
var hideCartDetails = function () {
|
|
||||||
visibleCart(false);
|
|
||||||
};
|
|
||||||
var showOrder = function () {
|
|
||||||
visibleCatalog(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The order template
|
|
||||||
var showCatalog = function () {
|
|
||||||
visibleCatalog(true);
|
|
||||||
};
|
|
||||||
var finishOrder = function () {
|
|
||||||
cart([]);
|
|
||||||
hideCartDetails();
|
|
||||||
showCatalog();
|
|
||||||
$("#finishOrderModal").modal("show");
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// first chapter
|
|
||||||
searchTerm: searchTerm,
|
searchTerm: searchTerm,
|
||||||
catalog: filteredCatalog,
|
catalog: filteredCatalog,
|
||||||
newProduct: newProduct,
|
newProduct: newProduct,
|
||||||
addProduct: addProduct,
|
addProduct: addProduct,
|
||||||
// second chapter
|
|
||||||
cart: cart,
|
|
||||||
showCartDetails: showCartDetails,
|
|
||||||
addToCart: addToCart,
|
|
||||||
totalItems: totalItems,
|
|
||||||
grandTotal: grandTotal,
|
|
||||||
removeFromCart: removeFromCart,
|
|
||||||
hideCartDetails: hideCartDetails,
|
|
||||||
showOrder: showOrder,
|
|
||||||
showCatalog: showCatalog,
|
|
||||||
finishOrder: finishOrder,
|
|
||||||
visibleCatalog: visibleCatalog,
|
|
||||||
visibleCart: visibleCart,
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// ko External Template Settings
|
|
||||||
infuser.defaults.templateSuffix = ".html";
|
|
||||||
infuser.defaults.templateUrl = "views";
|
|
||||||
|
|
||||||
ko.applyBindings(vm);
|
ko.applyBindings(vm);
|
|
@ -1,74 +0,0 @@
|
||||||
<div class="modal fade" id="addToCatalogModal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<form
|
|
||||||
class="form-horizontal"
|
|
||||||
role="form"
|
|
||||||
data-bind="with:newProduct"
|
|
||||||
>
|
|
||||||
<div class="modal-header">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
</button>
|
|
||||||
<h3>Add New Product to the Catalog</h3>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Name"
|
|
||||||
data-bind="textInput:name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Price"
|
|
||||||
data-bind="textInput:price"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Stock"
|
|
||||||
data-bind="textInput:stock"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-default"
|
|
||||||
data-bind="{click:$parent.addProduct}"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="glyphicon glyphicon-plus-sign"
|
|
||||||
></i>
|
|
||||||
Add Product
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- /.modal-content -->
|
|
||||||
</div>
|
|
||||||
<!-- /.modal-diaglog -->
|
|
||||||
</div>
|
|
||||||
<!-- /.modal -->
|
|
|
@ -1,34 +0,0 @@
|
||||||
<div class="list-group-item" style="overflow: hidden">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close pull-right"
|
|
||||||
data-bind="click:$root.removeFromCart"
|
|
||||||
>
|
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
<h4 class="" data-bind="text:product.name"></h4>
|
|
||||||
<div class="input-group cart-unit">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
data-bind="textInput:units"
|
|
||||||
readonly
|
|
||||||
/>
|
|
||||||
<span class="input-group-addon">
|
|
||||||
<div class="btn-group-vertical">
|
|
||||||
<button
|
|
||||||
class="btn btn-default btn-xs"
|
|
||||||
data-bind="click:addUnit"
|
|
||||||
>
|
|
||||||
<i class="glyphicon glyphicon-chevron-up"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-default btn-xs"
|
|
||||||
data-bind="click:removeUnit"
|
|
||||||
>
|
|
||||||
<i class="glyphicon glyphicon-chevron-down"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,2 +0,0 @@
|
||||||
Total Items: <span data-bind="text:totalItems"></span> Price:
|
|
||||||
<span data-bind="text:grandTotal"></span>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close pull-right"
|
|
||||||
data-bind="click:hideCartDetails"
|
|
||||||
>
|
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
<h1>Cart</h1>
|
|
||||||
<div
|
|
||||||
data-bind="template: {name: 'cart-item', foreach:cart}"
|
|
||||||
class="list-group"
|
|
||||||
></div>
|
|
||||||
<div data-bind="template: {name: 'cart-widget'}"></div>
|
|
||||||
<button class="btn btn-primary btn-sm" data-bind="click:showOrder">
|
|
||||||
Confirm Order
|
|
||||||
</button>
|
|
|
@ -1,46 +0,0 @@
|
||||||
<div class="input-group">
|
|
||||||
<span class="input-group-addon">
|
|
||||||
<i class="glyphicon glyphicon-search"></i> Search
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
data-bind="textInput:searchTerm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Price</th>
|
|
||||||
<th>Stock</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody data-bind="foreach:catalog">
|
|
||||||
<tr data-bind="style:lineColor">
|
|
||||||
<td data-bind="text:name"></td>
|
|
||||||
<td data-bind="text:price"></td>
|
|
||||||
<td data-bind="text:stock"></td>
|
|
||||||
<td>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary"
|
|
||||||
data-bind="click:$parent.addToCart"
|
|
||||||
>
|
|
||||||
<i class="glyphicon glyphicon-plus-sign"></i> Add
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan="3">
|
|
||||||
<strong>Items:</strong
|
|
||||||
><span data-bind="text:catalog().length"></span>
|
|
||||||
</td>
|
|
||||||
<td colspan="1">
|
|
||||||
<span data-bind="template:{name: 'cart-widget'}"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
|
@ -1,22 +0,0 @@
|
||||||
<div class="modal fade" id="finishOrderModal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-body">
|
|
||||||
<h2>Your order has been completed!</h2>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-success"
|
|
||||||
data-dismiss="modal"
|
|
||||||
>
|
|
||||||
Continue Shopping
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<h1>Catalog</h1>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-primary btn-sm"
|
|
||||||
data-toggle="modal"
|
|
||||||
data-target="#addToCatalogModal"
|
|
||||||
>
|
|
||||||
Add New Product
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary btn-sm"
|
|
||||||
data-bind="click: showCartDetails, css:{ disabled: cart().length < 1}"
|
|
||||||
>
|
|
||||||
Show Cart Details
|
|
||||||
</button>
|
|
||||||
<hr />
|
|
|
@ -1,34 +0,0 @@
|
||||||
<div class="col-xs-12">
|
|
||||||
<button class="btn btn-sm btn-primary" data-bind="click:showCatalog">
|
|
||||||
Back to catalog
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-primary" data-bind="click:finishOrder">
|
|
||||||
Buy & finish
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-6">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Price</th>
|
|
||||||
<th>Units</th>
|
|
||||||
<th>Subtotal</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody data-bind="foreach:cart">
|
|
||||||
<tr>
|
|
||||||
<td data-bind="text:product.name"></td>
|
|
||||||
<td data-bind="text:product.price"></td>
|
|
||||||
<td data-bind="text:units"></td>
|
|
||||||
<td data-bind="text:subtotal"></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan="3"></td>
|
|
||||||
<td>Total: <span data-bind="text:grandTotal"></span></td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue