Cart.ts
/*
A shopping cart implemented via the client-side in TypeScript.
Copyright 2015 Sam Saint-Pettersen.
Released under the MIT License.
*/
/// <reference path="typings/jquery.d.ts" />
/// <reference path="typings/jquery.cookie.d.ts" />
class Cart {
private static cart: string = 'Cart';
private static forceCookie: boolean = false;
private static cookie: string = 'cart';
private static cookieTTL: number = 365;
private static page: string;
private static currency: string;
private static bootstrap: boolean = false;
private static striped: boolean = false;
private static alerts: boolean = false;
private static items: string[];
private static prices: number[];
private static qtys: number[];
private static unrelated: string[];
// Is Storage object supported by user browser?
private static isStorageSupported(): boolean {
var storage: boolean = false;
if(typeof(Storage) != 'undefined') {
storage = true;
}
return storage;
}
// Show an alert.
private static showAlert(message: string): void {
if(Cart.alerts) {
$('#cart-alert').append('<div id="alert-for-cart" class="alert-success">' + message + '.</div>');
setTimeout(function() {
$('#alert-for-cart').remove();
}, 2500);
}
}
// Reset cart.
private static reset(): void {
Cart.items = new Array<string>();
Cart.prices = new Array<number>();
Cart.qtys = new Array<number>();
}
// Store an item to cart.
private static store(value: string): void {
// Use localStorage when possible.
if(!Cart.forceCookie && Cart.isStorageSupported()) {
localStorage.setItem('cart_item_' + localStorage.length, value.replace(/\:\s*/, ':'));
}
else {
// Otherwise, use a cookie.
var items: string = $.cookie(Cart.cookie);
if(items == undefined) items = '';
items += value + ',';
$.removeCookie(Cart.cookie);
$.cookie(Cart.cookie, items, { expires: Cart.cookieTTL, path: '/' });
}
}
// Remove an item from cart.
private static removeIt(value: string): void {
// Use localStorage when possible.
if(!Cart.forceCookie && Cart.isStorageSupported()) {
var a_value: string[] = value.split('(');
if(a_value[1] != null) value = a_value[0];
var i: number = localStorage.length - 1;
while((localStorage.length - 1) >= 0) {
var item: string = localStorage.getItem('cart_item_' + i);
if(item.search(value) != -1) {
localStorage.removeItem('cart_item_' + i);
break; // Remove only one at a time.
}
i--;
}
// Correct storage indexes and restore in localStorage.
var corrected: string[] = new Array<string>();
for(var key in localStorage) {
if(key.search('cart_item_') != -1) {
corrected.push(localStorage.getItem(key));
}
}
Cart.empty(true);
for(var i: number = 0; i < corrected.length; i++) {
Cart.store(corrected[i]);
}
}
else {
// Otherwise: use a cookie.
var items: string = $.cookie(Cart.cookie);
items = items.replace(value + ',', '');
Cart.empty(true);
$.cookie(Cart.cookie, items, { expires: Cart.cookieTTL, path: '/' });
if(items == '') Cart.empty(true);
}
}
// Push to arrays from localStorage.
private static pushFromStorage(): void {
for(var i: number = 0; i < localStorage.length; i++) {
var np: string[] = localStorage.getItem('cart_item_' + i).split(':');
var index: number = Cart.items.indexOf(np[0]);
if(index == -1) {
Cart.items.push(np[0]);
Cart.prices.push(parseFloat(np[1]));
Cart.qtys.push(1);
}
else Cart.qtys[index] = Cart.qtys[index] + 1;
}
}
// Push any unrelated data in localStorage back to array.
private static pushUnrelatedFromStorage(): void {
Cart.unrelated = new Array<string>();
for(var i: number = 0; i < localStorage.length; i++) {
if(localStorage.key(i).search('cart_item_') == - 1)
Cart.unrelated.push(localStorage.key(i) + '=>' +
localStorage.getItem(localStorage.key(i)));
}
}
// Restore any unrelated data to localStorage.
private static restoreUnrelated(): void {
for(var i = 0; i < Cart.unrelated.length; i++) {
var kv: string[] = Cart.unrelated[i].split('=>');
localStorage.setItem(kv[0], kv[1]);
}
}
// Push to arrays from cookie.
private static pushFromCookie(): void {
var cookie = $.cookie(Cart.cookie);
var values: string[] = new Array<string>();
if(cookie != undefined) values = cookie.split(',');
for(var i: number = 0; i < values.length; i++) {
if(values[i] == '') continue;
var np: string[] = values[i].split(':');
var index: number = Cart.items.indexOf(np[0]);
if(index == -1) {
Cart.items.push(np[0]);
Cart.prices.push(parseFloat(np[1]));
Cart.qtys.push(1);
}
else Cart.qtys[index] = Cart.qtys[index] + 1;
}
}
// Get number of items in cart
private static getNumberItems(): number {
Cart.reset();
if(!Cart.forceCookie && Cart.isStorageSupported()) {
Cart.pushFromStorage();
}
else {
Cart.pushFromCookie();
}
var items: string[] = new Array<string>();
if(!Cart.forceCookie && Cart.isStorageSupported()) {
for(var key in localStorage) {
if(key.search('cart_item_') != -1) {
items.push(localStorage.getItem(key));
}
}
}
else {
var stritems: string = $.cookie(Cart.cookie);
if(stritems != undefined) {
var i: string[] = stritems.split(',');
for(var x: number = 0; x < i.length; x++) {
if(i[x] != '') items.push(i[x]);
}
}
}
return items.length;
}
// Render items in cart.
public static renderItems(): string[] {
var total: number = 0;
var c: string = Cart.currency;
Cart.reset();
if(!Cart.forceCookie && Cart.isStorageSupported()) {
Cart.pushFromStorage();
}
else {
Cart.pushFromCookie();
}
if(Cart.items.length > 0) {
$('#cart').append('<table id="cart-contents"><tr><td>Item</td>'
+ '<td>Price</td><td>Qty</td><td>Subtotal</td><td> </td></tr>');
for(var i = 0; i < Cart.items.length; i++) {
$('#cart-contents').append('<tr><td class="item">' + Cart.items[i] + '</td>' +
'<td class="price">' + c + ' ' + Cart.prices[i].toFixed(2) + '</td><td class="qty">' +
Cart.qtys[i] + '</td><td class="subtotal">' + c + ' ' + (Cart.prices[i] * Cart.qtys[i]).toFixed(2) +
'</td><td><button onclick="Cart.removeItem(' + i + ')">X</button>' +
' <button onclick="Cart.changeQty(' + i + ',true);">+</button>' +
' <button onclick="Cart.changeQty(' + i + ',false);">-</button>' +
'</td></tr>');
total += Cart.prices[i] * Cart.qtys[i];
}
$('#cart-contents').append('</tr></table>');
$('#cart-contents').append('<p id="total"><br/><strong>Total: ' + c + ' ' + total.toFixed(2) + '</strong></p>');
$('#cart').append('<p><button onclick="Cart.empty();">Empty ' + Cart.cart + '</button></p>');
if(Cart.bootstrap) {
$('button').addClass('btn');
$('button').addClass('btn-default');
$('#cart-contents').addClass('table');
if(Cart.striped)
$('#cart-contents').addClass('table-striped');
}
}
else $('#cart').append('<p><em>Your ' + Cart.cart.toLowerCase() + ' is empty.</em></p>');
var at_price_list = new Array<string>();
for(var i: number = 0; i < Cart.items.length; i++) {
at_price_list.push(Cart.items[i] + '|' + Cart.currency + ' ' + Cart.prices[i].toFixed(2) + '|' + Cart.qtys[i]);
}
return at_price_list;
}
// Empty the cart.
public static empty(noprompt?: boolean): void {
if(!noprompt)
var empty: boolean = confirm('Really empty the ' + Cart.cart.toLowerCase() + '?');
if(empty || noprompt) {
if(!Cart.forceCookie && Cart.isStorageSupported()) {
for(var key in localStorage) {
if(key.search('cart_item_') != -1) {
localStorage.removeItem(key);
}
}
}
else {
$.removeCookie(Cart.cookie);
}
$('#cart').empty();
Cart.renderItems();
}
}
// Change quantity of an item in cart.
public static changeQty(index: number, increment: boolean): void {
var item: string = $('.item:eq(' + index + ')').text();
var price: string = $('.price:eq(' + index + ')').text();
var qty: string = $('.qty:eq(' + index + ')').text();
var pattern = new RegExp('\\' + Cart.currency + '\s*', 'ig');
if(increment) {
Cart.store(item + ':' + price.replace(pattern, ''));
}
else {
Cart.removeIt(item + ':' + price.replace(pattern, ''));
}
$('#cart').empty();
Cart.renderItems();
}
// Remove all occcurences of an item from cart.
public static removeItem(index: number): void {
Cart.reset();
if(!Cart.forceCookie && Cart.isStorageSupported()) {
Cart.pushFromStorage();
Cart.pushUnrelatedFromStorage();
localStorage.clear();
Cart.restoreUnrelated();
}
else {
Cart.pushFromCookie();
$.removeCookie(Cart.cookie);
}
Cart.items.splice(index, 1);
Cart.prices.splice(index, 1);
for(var i: number = 0; i < Cart.items.length; i++) {
Cart.store(Cart.items[i] + ':' + Cart.prices[i]);
}
$('#cart').empty();
Cart.renderItems();
}
// Add an item to cart.
public static addItem(id: number): void {
var pattern = new RegExp('\\' + Cart.currency + '\s*', 'ig');
var item: string = $('#product-' + id +'> .name').text();
var price: string = null;
$('#product-' + id).each(function() {
price = $('#product-' + id + '> .price').text();
Cart.store(item + ':' + price.replace(pattern, ''));
});
Cart.showAlert('Added ' + item + ' to ' + Cart.cart.toLowerCase());
Cart.renderCart(Cart.page);
}
// Configure cart instead of using defaults.
// Set name, force cookie use instead of localStorage,
// Set currency symbol, use bootstrap for tables and buttons,
// use striped tables, use alerts.
public static configure(name?: string, forceCookie?: boolean, currency?: string, bootstrap?: boolean, striped?: boolean, alerts?: boolean): void {
if(name != null) Cart.cart = name;
if(forceCookie != null) Cart.forceCookie = forceCookie;
if(currency != null) Cart.currency = currency;
else Cart.currency = '$';
if(bootstrap != null) Cart.bootstrap = bootstrap;
if(striped != null) Cart.striped = striped;
if(alerts != null) Cart.alerts = alerts;
}
// Render cart box with item count.
public static renderCart(page: string): string {
Cart.page = page;
var cart: string = '<div id="cart-items">';
cart += '<a href="' + Cart.page + '">' + Cart.cart + '</a>';
cart += ' [' + Cart.getNumberItems() + ']';
cart += '</div><br/><br/>';
$('#cart-box').empty();
$('#cart-box').append(cart);
return Cart.cart + ' => ' + Cart.getNumberItems().toString();
}
}