2013-03-27 11:21:47 -07:00

1707 lines
42 KiB
JavaScript

/**
*
* Crunchbutton
*
* @author: Devin Smith (http://devin.la)
* @date: 2012-06-20
*
*/
var App = {
cartHighlightEnabled: false,
currentPage: null,
tagline: '',
service: '/api/',
cached: {},
community: null,
page: {},
config: null,
forceHome: false,
cookieExpire: new Date(3000,01,01),
order: {
cardChanged: false,
pay_type: 'card',
delivery_type: 'delivery',
tip: '3'
},
signin : {},
suggestion : {},
restaurants: {
permalink : 'food-delivery',
list: false
},
defaultRange : 2,
modal: {},
hasBack: false,
_init: false,
_pageInit: false,
_identified: false,
isDeliveryAddressOk : false,
tips: [0,1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5,10,11,12,13,14,15,16,17,18,19,20]
};
App.loadRestaurant = function(id) {
App.cache('Restaurant', id,function() {
if (!this.open()) {
alert("This restaurant is currently closed. It will be open during the following hours (" + this._tzabbr + "):\n\n" + this.closedMessage());
App.busy.unBusy();
} else {
if (this.redirect) {
location.href = this.redirect;
return;
}
var loc = '/' + App.restaurants.permalink + '/' + this.permalink;
History.pushState({}, 'Crunchbutton - ' + this.name, loc);
}
});
};
/**
* Loads up "community" keyword pages
*/
App.routeAlias = function(id, success, error) {
id = id.toLowerCase();
alias = App.aliases[id] || false;
success = success || function(){};
error = error || function(){};
if (alias) {
// Get the location of the alias
var loc = App.locations[alias.id_community];
if (loc.loc_lat && loc.loc_lon) {
var res = {
lat: loc.loc_lat,
lon: loc.loc_lon,
prep: alias.prep,
city: alias.name_alt,
address: alias.name_alt
};
App.loc.range = loc.range || App.defaultRange;
success({alias: res});
return;
}
}
error();
};
App.loadHome = function(force) {
$('input').blur();
App.currentPage = 'home';
History.pushState({}, 'Crunchbutton', '/');
App.page.home(force);
};
App.render = function(template, data) {
var compiled = _.template($('.template-' + template).html());
return compiled(data);
};
App.showPage = function(params) {
// switch here for AB testing
App.currentPage = params.page;
if (params.title) {
document.title = params.title;
}
// track different AB pages
if (params.tracking) {
App.track(params.tracking.title, params.tracking.data);
}
$('.main-content').html(App.render(params.page, params.data));
};
/**
* Router init
* @todo replace with router
*/
App.loadPage = function() {
// If the user is using Chrome for iOS show the message:
if (App.isChromeForIOS() ){
App.message.chrome();
}
App.signin.checkUser();
// Force it!
setTimeout( function(){App.signin.checkUser()}, 500 );
var
url = History.getState().url.replace(/http(s)?:\/\/.*?\/(.*)/,'$2').replace('//','/'),
path = url.split('/');
if (!path[path.length-1]) {
delete path[path.length-1];
}
if (!App.config) {
return;
}
// hide whatever we have
if (App._pageInit) {
$('.main-content').css('visibility','0');
} else {
App._pageInit = true;
}
// check if the user clicked at the back button
if( !url && App.hasBack ){
App.forceHome = false;
App.page.home();
return;
}
// force to a specific community
if (!url) {
App.loadHome();
return;
}
var restaurantRegex = new RegExp('^\/(restaurant)|(' + App.restaurants.permalink + ')/', 'i');
var cleaned_url = $.trim( url.replace( '/', '' ) );
App.hasBack = true;
switch (true) {
case /^legal/i.test(url):
App.page.legal();
break;
case /^help/i.test(url):
App.page.help();
break;
case /^orders/i.test(url):
App.page.orders();
break;
case /^order/i.test(url):
App.page.order(path[1]);
break;
case /^reset/i.test(url):
App.page.resetPassword( path );
break;
case /^giftcard/i.test(url):
App.page.giftCard( path );
break;
case new RegExp( App.restaurants.permalink + '$', 'i' ).test( cleaned_url ):
App.page.foodDelivery();
break;
case restaurantRegex.test(url):
App.page.restaurant(path[1]);
break;
default:
App.routeAlias( path[ 0 ] );
$('.footer').removeClass('footer-hide');
setTimeout(scrollTo, 80, 0, 1);
setTimeout( function(){ App.signin.checkUser(); }, 100 );
break;
}
if (App.config.env == 'live') {
$('.footer').addClass('footer-hide');
}
App.refreshLayout();
$('.main-content').css('visibility','1');
setTimeout(scrollTo, 80, 0, 1);
setTimeout( function(){ App.signin.checkUser(); }, 300 );
};
/**
* Refresh the pages layout for a blank page
*/
App.refreshLayout = function() {
setTimeout(function() {
scrollTo(0, 1);
}, 80);
};
/**
* Sends a tracking item to mixpanel, or to google ads if its an order
*/
App.track = function() {
if (App.config.env != 'live') {
return;
}
if (arguments[0] == 'Ordered') {
$('img.conversion').remove();
mixpanel.people.track_charge(arguments[1].total);
var i = $('<img class="conversion" src="https://www.googleadservices.com/pagead/conversion/996753959/?value=' + Math.floor(arguments[1].total) + '&amp;label=-oawCPHy2gMQp4Sl2wM&amp;guid=ON&amp;script=0&url=' + History.getState().url + '">').appendTo($('body'));
}
if (arguments[1]) {
mixpanel.track(arguments[0],arguments[1]);
} else {
mixpanel.track(arguments[0]);
}
};
/**
* Tracks a property to mixpanel
*/
App.trackProperty = function(prop, value) {
if (!App.config || App.config.env != 'live') {
return;
}
var params = {};
params[prop] = value;
mixpanel.register_once(params);
};
/**
* Itendity the user to mixpanel
*/
App.identify = function() {
if (App.config.env != 'live') {
return;
}
if (!App._identified && App.config.user.uuid) {
mixpanel.identify(App.config.user.uuid);
App._identified = true;
}
};
/**
* generate ab formulas
*/
App.AB = {
options: {
tagline: [
{
name: 'tagline-for-free',
tagline: 'Order the top food %s. For free. <br /> After you order, everything is saved for future 1 click ordering. <br /><strong>Choose a restaurant:</strong>'
},
{
name: 'tagline-no-free',
tagline: 'Order the top food %s. <br /> After you order, everything is saved for future 1 click ordering. <br /><strong>Choose a restaurant:</strong>'
}
],
slogan: [
{
name: 'slogan-push-food',
slogan: 'Push a button. Get Food.'
}
],
restaurantPage: [
{
name: 'restaurant-page-noimage'
},
{
name: 'restaurant-page-image',
disabled: true
}
]
},
init: function() {
if (!App.config.ab) {
// we dont have ab variables. generate them
App.AB.create(true);
}
App.AB.load();
},
create: function(clear) {
if (clear) {
App.config.ab = {};
}
_.each(App.AB.options, function(option, key) {
if (App.config.ab[key]) {
return;
}
var opts = _.filter(App.AB.options[key], function(o) { return o.disabled ? false : true; });
var opt = opts[Math.floor(Math.random()*opts.length)];
App.config.ab[key] = opt.name
App.trackProperty('AB-' + key, opt.name);
});
App.AB.save();
console.log(App.config.ab);
},
load: function() {
App.slogan = _.findWhere(App.AB.options.slogan, {name: App.config.ab.slogan});
App.tagline = _.findWhere(App.AB.options.tagline, {name: App.config.ab.tagline});
if (!App.slogan || !App.tagline) {
App.AB.create(true);
App.AB.load(true);
}
},
save: function() {
$.ajax({
url: App.service + 'config',
data: {ab: App.config.ab},
dataType: 'json',
type: 'POST',
complete: function(json) {
}
});
}
};
App.cart = {
uuidInc: 0,
items: {},
uuid: function() {
var id = 'c-' + App.cart.uuidInc;
App.cart.uuidInc++;
return id;
},
add: function(item) {
var
id = App.cart.uuid(),
opt = App.cached['Dish'][item].options(),
options = [];
if (arguments[1]) {
options = arguments[1].options;
} else {
for (var x in opt) {
if (opt[x]['default'] == 1) {
options[options.length] = opt[x].id_option;
}
}
}
App.cart.items[id] = {
id: item,
options: options
};
var el = $('<div class="cart-item cart-item-dish" data-cart_id="' + id + '"></div>');
el.append('<div class="cart-button cart-button-remove"><span></span></div>');
el.append('<div class="cart-item-name">' + App.cache('Dish',item).name + ' <span class="cart-item-description">' + (App.cache('Dish',item).description != null ? App.cache('Dish',item).description : '') + '</span></div>');
if (App.cached['Dish'][item].options().length) {
el.append('<div class="cart-item-config"><a href="javascript:;">Customize</a></div>');
}
el.hide();
$('.cart-items-content').prepend(el);
//el.fadeIn();
el.show();
App.cart.updateTotal();
App.track('Dish added');
},
clone: function(item) {
var
cartid = item.attr('data-cart_id'),
cart = App.cart.items[cartid],
newoptions = [];
for (var x in cart.options) {
newoptions[newoptions.length] = cart.options[x];
}
App.cart.add(cart.id, {
options: newoptions
});
App.track('Dish cloned');
},
remove: function(item) {
var
cart = item.attr('data-cart_id');
App.track('Dish removed');
delete App.cart.items[cart];
item.remove();
$('.cart-item-customize[data-id_cart_item="' + cart + '"]').remove();
App.cart.updateTotal();
},
/**
* If the user changed the delivery method to takeout and the payment is card
* the default tip will be $0.
* If the delivery method is delivery and the payment is card
* the default tip will be $3.
* If the user had changed the tip value the default value will be chosen one.
*/
tip: function(){
// @URGENT temp fix
App.config.user.last_tip_takeout = 0;
App.config.user.last_tip_delivery = 0;
// It means the user did not change the tip yet - So let's calculate the default tip
if( typeof App.order.tipHasChanged == 'undefined' ){
var tip = 0;
if( App.order.delivery_type == 'takeout' && App.order['pay_type'] == 'card' ){
tip = ( App.config.user.last_tip_takeout || 0 );
} else if( App.order.delivery_type == 'delivery' && App.order['pay_type'] == 'card' ){
tip = ( App.config.user.last_tip_delivery || App.config.user.last_tip_takeout );
}
if( !tip && App.order.delivery_type != 'takeout' ){
tip = App.cart.calculateTip();
}
App.order.tip = tip;
$('[name="pay-tip"]').val( App.order.tip );
}
return App.order.tip;
},
calculateTip: function(){
var subtotal = App.cart.subtotal();
var porcent = 18; // % to calculate the tip
var tipPorcent = ( subtotal * porcent ) / 100;
var tip = parseInt( tipPorcent );
if( parseFloat( tipPorcent - tip ) <= 0.5 ){
var sum = 0.5;
} else {
var sum = 1;
}
tip += sum;
var maxTip = App.tips[ App.tips.length - 1 ];
var minTip = App.tips[ 1 ];
if( tip > maxTip ){
tip = maxTip;
}
if( tip < minTip ){
tip = minTip;
}
return tip;
},
/**
* Gets called after the cart is updarted to refresh the total
*
* @todo Gets called many times before the cart is updated, on load, and shouldn't
*
* @return void
*/
updateTotal: function() {
var
totalText = this.charged(),
tipText = '',
feesText = '',
totalItems = 0,
credit = 0,
hasFees = ((App.restaurant.delivery_fee && App.order.delivery_type == 'delivery') || App.restaurant.fee_customer) ? true : false;
if( App.credit.restaurant[ App.restaurant.id ] ){
credit = parseFloat( App.credit.restaurant[ App.restaurant.id ] );
}
for (var x in App.cart.items) {
totalItems++;
}
if (App.restaurant.meetDeliveryMin() && App.order.delivery_type == 'delivery') {
$('.delivery-minimum-error').show();
$('.delivery-min-diff').html(App.restaurant.deliveryDiff());
} else {
$('.delivery-minimum-error').hide();
}
$('.cart-summary-item-count span').html(totalItems);
/* If no items, hide payment line
* .payment-total line for new customers
* .dp-display-payment is for stored customers
*/
if (!this.subtotal()) {
$('.payment-total, .dp-display-payment').hide();
} else {
$('.payment-total, .dp-display-payment').show();
}
// Calculates the tip.
// If the tip was changed, force recalculation.
var tipBefore = App.order.tip;
if( tipBefore != App.cart.tip() ){
App.cart.updateTotal();
return;
}
var breakdown = App.cart.totalbreakdown();
var extraCharges = App.cart.extraChargesText(breakdown);
if (extraCharges) {
$('.cart-breakdownDescription').html('$' + this.subtotal().toFixed(2) + ' (+'+ extraCharges +')' );
} else {
$('.cart-breakdownDescription').html('$' + this.subtotal().toFixed(2));
}
if( credit > 0 ){
var creditLeft = '';
if( this.total() < credit ){
var creditLeft = '<span class="gift-left"> - You\'ll still have $' + App.ceil( ( credit - this.total() ) ).toFixed( 2 ) + ' gift card left </span>';
credit = this.total();
}
$('.cart-gift').html( '&nbsp;(- $' + credit + ' credit ' + creditLeft + ')' );
} else {
$('.cart-gift').html();
}
totalText = '$' + App.ceil( ( totalText ) ).toFixed( 2 );
$('.cart-total').html( totalText );
/**
* Crunchbutton doesnt collect the cash, the restaurant will ring up
* the order in their register, which may have different prices. The
* restaurant collects the cash, so its posible things may be
* different. This differs from when its card, crunchbutton collects
* the money directly so the price cant vary
*/
if (App.order['pay_type'] == 'card') {
$('.cash-order-aprox').html('');
$('.cart-paymentType').html('by card');
} else {
$('.cash-order-aprox').html('approximately');
$('.cart-paymentType').html('');
}
if (App.cartHighlightEnabled && $('.cart-summary').css('display') != 'none') {
$('.cart-summary').removeClass('cart-summary-detail');
$('.cart-summary').effect('highlight', {}, 500, function() {
$('.cart-summary').addClass('cart-summary-detail');
});
}
if ($('.cart-total').html() == totalText) {
//return;
}
if (!totalItems) {
$('.default-order-check').hide();
} else {
$('.default-order-check').show();
}
var
totalItems = {},
name,
text = '';
$('.cart-summary-items').html('');
for (var x in App.cart.items) {
name = App.cached['Dish'][App.cart.items[x].id].name;
if (totalItems[name]) {
totalItems[name]++;
} else {
totalItems[name] = 1;
}
}
for (x in totalItems) {
text += x;
if (totalItems[x] > 1) {
text += '&nbsp;(' + totalItems[x] + ')';
}
text += ',&nbsp;&nbsp;';
}
$('.cart-summary-items').html(text.substr(0,text.length-13));
$('.cart-item-customize-price').each(function() {
var dish = $(this).closest('.cart-item-customize').attr('data-id_cart_item'),
option = $(this).closest('.cart-item-customize-item').attr('data-id_option'),
cartitem = App.cart.items[dish],
opt = App.cached['Option'][option],
price = opt.optionPrice(cartitem.options);
$(this).html(App.cart.customizeItemPrice(price));
});
},
customizeItemPrice: function(price) {
return price != '0.00' ? '&nbsp;($' + price.toFixed(2) + ')' : '';
},
customize: function(item) {
var
cart = item.attr('data-cart_id'),
old = $('.cart-item-customize[data-id_cart_item="' + cart + '"]');
if (old.length) {
old.remove();
} else {
var
el = $('<div class="cart-item-customize" data-id_cart_item="' + cart + '"></div>').insertAfter(item),
cartitem = App.cart.items[cart],
obj = App.cached['Dish'][cartitem.id],
opt = obj.options();
// First the basic options
for (var x in opt) {
if (opt[x].id_option_parent) {
continue;
}
if (opt[x].type == 'check') {
var price = opt[x].optionPrice(cartitem.options);
var check = $('<input type="checkbox" class="cart-customize-check">');
if ($.inArray(opt[x].id_option, cartitem.options) !== -1) {
check.attr('checked','checked');
}
var option = $('<div class="cart-item-customize-item" data-id_option="' + opt[x].id_option + '"></div>')
.append(check)
.append('<label class="cart-item-customize-name">' +
opt[x].name + (opt[x].description || '') +
'</label><label class="cart-item-customize-price">' +
App.cart.customizeItemPrice(price) + '</label>'
);
el.append(option);
}
}
// Second the customizable options
for (var x in opt) {
if (opt[x].id_option_parent) {
continue;
}
if (opt[x].type == 'select') {
var select = $('<select class="cart-customize-select">');
for (var i in opt) {
if (opt[i].id_option_parent == opt[x].id_option) {
var option = $('<option value="' + opt[i].id_option + '">' + opt[i].name + (opt[i].description || '') + (opt[i].price != '0.00' || opt[x].price_linked == '1' ? (' ($' + (parseFloat(opt[i].price) + parseFloat(obj.price)).toFixed(2) + ')') : '') + '</option>');
if ($.inArray(opt[i].id_option, cartitem.options) !== -1) {
option.attr('selected','selected');
}
select.append(option);
}
}
var option = $('<div class="cart-item-customize-item" data-id_option="' + opt[x].id_option + '"></div>')
.append('<label class="cart-item-customize-select-name">' + opt[x].name + (opt[x].description || '') + '</label>')
.append(select);
el.append(option);
}
}
}
App.track('Dish customized');
},
customizeItem: function(item) {
var
cart = item.closest('.cart-item-customize').attr('data-id_cart_item'),
cartitem = App.cart.items[cart],
customitem = item.closest('.cart-item-customize-item'),
opt = customitem.attr('data-id_option');
if (opt) {
if (item.hasClass('cart-customize-select')) {
var obj = App.cached['Dish'][cartitem.id],
opts = obj.options();
for (var i in opts) {
if (opts[i].id_option_parent != opt) {
continue;
}
for (var x in cartitem.options) {
if (cartitem.options[x] == opts[i].id) {
cartitem.options.splice(x, 1);
break;
}
}
}
cartitem.options[cartitem.options.length] = item.val();
} else if(item.hasClass('cart-customize-check')) {
if (item.is(':checked')) {
cartitem.options[cartitem.options.length] = opt;
} else {
for (var x in cartitem.options) {
if (cartitem.options[x] == opt) {
cartitem.options.splice(x, 1);
break;
}
}
}
}
}
App.cart.updateTotal();
},
/**
* subtotal, delivery, fee, taxes and tip
*
* @category view
*/
extraChargesText: function(breakdown) {
var elements = [];
var text = '';
if (breakdown.delivery) {
elements.push('$' + breakdown.delivery.toFixed(2) + ' delivery');
}
if (breakdown.fee) {
elements.push('$' + breakdown.fee.toFixed(2) + ' fee');
}
if (breakdown.taxes) {
elements.push('$' + breakdown.taxes.toFixed(2) + ' taxes');
}
if (breakdown.tip && breakdown.tip > 0) {
elements.push('$' + breakdown.tip + ' tip');
}
if (elements.length) {
if (elements.length > 2) {
var lastOne = elements.pop();
var elements = [elements.join(', ')];
elements.push(lastOne);
}
var text = elements.join(' & ');
}
return text;
},
getCart: function() {
var cart = [];
for (x in App.cart.items) {
cart[cart.length] = App.cart.items[x];
}
return cart;
},
/**
* Submits the cart order
*
* @returns void
*/
submit: function() {
if (App.busy.isBusy()) {
return;
}
App.busy.makeBusy();
var read = $('.payment-form').length ? true : false;
if (read) {
App.config.user.name = $('[name="pay-name"]').val();
App.config.user.phone = $('[name="pay-phone"]').val().replace(/[^\d]*/gi,'');
if (App.order['delivery_type'] == 'delivery') {
App.config.user.address = $('[name="pay-address"]').val();
}
App.order.tip = $('[name="pay-tip"]').val();
}
var order = {
cart: App.cart.getCart(),
pay_type: App.order['pay_type'],
delivery_type: App.order['delivery_type'],
restaurant: App.restaurant.id,
make_default: $('#default-order-check').is(':checked'),
notes: $('[name="notes"]').val(),
lat: App.loc.lat,
lon: App.loc.lon
};
if (order.pay_type == 'card') {
order.tip = App.order.tip || '3';
}
if (read) {
order.address = App.config.user.address;
order.phone = App.config.user.phone;
order.name = App.config.user.name;
/*
Issue 13: Removed the password for while
order.password = $( 'input[name=pay-password]' ).val( );
*/
if (App.order.cardChanged) {
order.card = {
number: $('[name="pay-card-number"]').val(),
month: $('[name="pay-card-month"]').val(),
year: $('[name="pay-card-year"]').val()
};
} else {
order.card = {};
}
}
console.log('ORDER:',order);
var errors = {};
if (!order.name) {
errors['name'] = 'Please enter your name.';
}
if (!App.phone.validate(order.phone)) {
errors['phone'] = 'Please enter a valid phone #.';
}
if (order.delivery_type == 'delivery' && !order.address) {
errors['address'] = 'Please enter an address.';
}
if (order.pay_type == 'card' && ((App.order.cardChanged && !order.card.number) || (!App.config.user.id_user && !order.card.number))) {
errors['card'] = 'Please enter a valid card #.';
}
if (!App.cart.hasItems()) {
errors['noorder'] = 'Please add something to your order.';
}
if (!$.isEmptyObject(errors)) {
var error = '';
for (var x in errors) {
error += errors[x] + "\n";
}
$('body').scrollTop($('.payment-form').position().top-80);
alert(error);
App.busy.unBusy();
App.track('OrderError', errors);
return;
}
// verify the distance between the user and the restaurant
if (order.delivery_type == 'delivery' && !App.isDeliveryAddressOk) {
var success = function( results ) {
if( results.alias ){
var lat = results.alias.lat;
var lon = results.alias.lon;
} else {
var lat = results[0].geometry.location.lat();
var lon = results[0].geometry.location.lng();
}
if (!App.restaurant.deliveryHere({ lat: lat, lon: lon})) {
alert( 'Sorry, that address seems invalid to us. \nTry again, or order takeout' );
App.busy.unBusy();
} else {
App.busy.unBusy();
App.isDeliveryAddressOk = true;
App.cart.submit();
}
};
var error = function() {
App.busy.unBusy();
alert('Oops! We couldn\'t find that address!');
};
App.loc.doGeocode( order.address, success, error);
return;
}
if( order.delivery_type == 'takeout' ){
App.isDeliveryAddressOk = true;
}
if( !App.isDeliveryAddressOk ){
return;
}
$.ajax({
url: App.service + 'order',
data: order,
dataType: 'json',
type: 'POST',
complete: function(json) {
json = $.parseJSON(json.responseText);
if (json.status == 'false') {
var error = '';
for (x in json.errors) {
error += json.errors[x] + "\n";
}
App.track('OrderError', json.errors);
alert(error);
} else {
if (json.token) {
$.cookie('token', json.token, { expires: new Date(3000,01,01), path: '/'});
}
$('.link-orders').show();
order.cardChanged = false;
App.justCompleted = true;
var totalItems = 0;
for (var x in App.cart.items) {
totalItems++;
}
$.getJSON('/api/config', App.processConfig);
App.cache('Order',json.uuid,function() {
App.track('Ordered', {
'total':this.final_price,
'subtotal':this.price,
'tip':this.tip,
'restaurant': App.restaurant.name,
'paytype': this.pay_type,
'ordertype': this.order_type,
'user': this.user,
'items': totalItems
});
App.order.cardChanged = false;
delete App.order.tipHasChanged;
var loc = '/order/' + this.uuid;
History.pushState({},loc,loc);
});
}
App.busy.unBusy();
}
});
}, // end App.cart.submit()
subtotal: function() {
var
total = 0,
options;
for (var x in App.cart.items) {
total += parseFloat(App.cached['Dish'][App.cart.items[x].id].price);
options = App.cart.items[x].options;
for (var xx in options) {
var option = App.cached['Option'][options[xx]];
if (option === undefined) continue; // option does not exist anymore
total += parseFloat(option.optionPrice(options));
}
}
total = App.ceil(total);
return total;
},
/**
* delivery cost
*
* @return float
*/
_breackDownDelivery: function() {
var delivery = 0;
if (App.restaurant.delivery_fee && App.order.delivery_type == 'delivery') {
delivery = parseFloat(App.restaurant.delivery_fee);
}
delivery = App.ceil(delivery);
return delivery;
},
/**
* Crunchbutton service
*
* @return float
*/
_breackDownFee: function(feeTotal) {
var fee = 0;
if (App.restaurant.fee_customer) {
fee = (feeTotal * (parseFloat(App.restaurant.fee_customer)/100));
}
fee = App.ceil(fee);
return fee;
},
_breackDownTaxes: function(feeTotal) {
var taxes = (feeTotal * (App.restaurant.tax/100));
taxes = App.ceil(taxes);
return taxes;
},
_breakdownTip: function() {
var tip = 0;
if (App.order['pay_type'] == 'card') {
tip = App.order.tip;
}
return App.ceil(tip).toFixed(2);
},
total: function() {
var
total = 0,
dish,
options,
feeTotal = 0,
totalItems = 0,
finalAmount = 0
;
var breakdown = this.totalbreakdown();
total = breakdown.subtotal;
feeTotal = total;
feeTotal += breakdown.delivery;
feeTotal += breakdown.fee;
finalAmount = feeTotal + breakdown.taxes;
finalAmount += parseFloat( this._breakdownTip() );
return App.ceil(finalAmount).toFixed(2);
},
charged : function(){
var finalAmount = this.total();
if( App.credit.restaurant[ App.restaurant.id ] ){
finalAmount = finalAmount - App.ceil( App.credit.restaurant[ App.restaurant.id ] ).toFixed(2);
if( finalAmount < 0 ){
finalAmount = 0;
}
}
return finalAmount;
},
/**
* Returns the elements that calculates the total
*
* breakdown elements are: subtotal, delivery, fee, taxes and tip
*
* @return array
*/
totalbreakdown: function() {
var elements = {};
var total = this.subtotal();
var feeTotal = total;
elements['subtotal'] = this.subtotal();
elements['delivery'] = this._breackDownDelivery();
feeTotal += elements['delivery'];
elements['fee'] = this._breackDownFee(feeTotal);
feeTotal += elements['fee'];
elements['taxes'] = this._breackDownTaxes(feeTotal);
elements['tip'] = this._breakdownTip();
return elements;
},
resetOrder: function() {
App.cart.items = {};
$('.cart-items-content, .cart-total').html('');
},
reloadOrder: function() {
var cart = App.cart.items;
App.cart.resetOrder();
App.cart.loadFlatOrder(cart);
},
loadFlatOrder: function(cart) {
for (var x in cart) {
App.cart.add(cart[x].id,{
options: cart[x].options ? cart[x].options : []
});
}
},
loadOrder: function(order) {
// @todo: convert this to preset object
try {
if (order) {
var dishes = order['_dishes'];
for (var x in dishes) {
var options = [];
for (var xx in dishes[x]['_options']) {
options[options.length] = dishes[x]['_options'][xx].id_option;
}
if (App.cached.Dish[dishes[x].id_dish] != undefined) {
App.cart.add(dishes[x].id_dish,{
options: options
});
}
}
}
} catch (e) {
console.log(e.stack);
// throw e;
}
App.cart.updateTotal();
},
hasItems: function() {
if (!$.isEmptyObject(App.cart.items)) {
return true;
}
return false;
}
};
App.busy = {
isBusy: function() {
return $('.app-busy').length ? true : false;
},
makeBusy: function() {
//el.addClass('button-bottom-disabled');
var busy = $('<div class="app-busy"></div>')
.append($('<div class="app-busy-loader"><div class="app-busy-loader-icon"></div></div>'))
$('body').append(busy);
},
unBusy: function() {
$('.app-busy').remove();
//el.removeClass('button-bottom-disabled');
}
};
App.test = {
card: function() {
$('[name="pay-card-number"]').val('4242424242424242');
$('[name="pay-card-month"]').val('1');
$('[name="pay-card-year"]').val('2020');
$('[name="pay-name"]').val('MR TEST');
$('[name="pay-phone"]').val('***REMOVED***');
$('[name="pay-address"]').val( App.restaurant.address || "123 main\nsanta monica ca" );
App.order.cardChanged = true;
},
logout: function() {
$.getJSON('/api/logout',function(){ location.reload()});
},
cart: function() {
alert(JSON.stringify(App.cart.items));
},
clearloc: function() {
$.cookie('community', '', { expires: new Date(3000,01,01), path: '/'});
$.cookie('location_lat', '', { expires: new Date(3000,01,01), path: '/'});
$.cookie('location_lon', '', { expires: new Date(3000,01,01), path: '/'});
location.href = '/';
}
};
App.processConfig = function(json) {
App.config = json;
App.AB.init();
if (App.config.user) {
App.identify();
App.order['pay_type'] = App.config.user['pay_type'];
App.order['delivery_type'] = App.config.user['delivery_type'];
}
};
App.detectCardType = function(){
var cardvalue = $( '[name="pay-card-number"]' ).val();
var type = false;
if ( /^5[1-5]/.test( cardvalue ) ) {
type = "master-card";
}
else if ( /^4/.test( cardvalue ) ){
type = "visa";
}
else if ( /^3[47]/.test( cardvalue ) ){
type = "amex";
}
else if ( /^6/.test( cardvalue ) ){
type = "discover";
}
if( type ){
$( '.payment-card' ).css( 'backgroundPosition', '-40px 0' );
$( '.card-' + type ).css( 'backgroundPosition', '0 0' );
} else {
$( '.payment-card' ).css( 'backgroundPosition', '0 0' );
}
}
App.trigger = {
delivery: function() {
$('.delivery-toggle-takeout').removeClass('toggle-active');
$('.delivery-toggle-delivery').addClass('toggle-active');
$('.delivery-only').show();
App.order['delivery_type'] = 'delivery';
App.cart.updateTotal();
},
takeout: function() {
$('.delivery-toggle-delivery').removeClass('toggle-active');
$('.delivery-toggle-takeout').addClass('toggle-active');
$('.delivery-only, .field-error-zip').hide();
App.order['delivery_type'] = 'takeout';
App.cart.updateTotal();
},
credit: function() {
$('.pay-toggle-cash').removeClass('toggle-active');
$('.pay-toggle-credit').addClass('toggle-active');
$('.card-only').show();
App.order['pay_type'] = 'card';
App.cart.updateTotal();
},
cash: function() {
$('.pay-toggle-credit').removeClass('toggle-active');
$('.pay-toggle-cash').addClass('toggle-active');
$('.card-only').hide();
App.order['pay_type'] = 'cash';
App.cart.updateTotal();
}
}
/**
* global event binding and init
*/
$(function() {
$(document).on('click', '.signout-button', function() {
App.signin.signOut();
});
$(document).on('click', '.signup-add-facebook-button', function() {
App.signin.facebook.login();
});
$(document).on('click', '.change-location-inline', function() {
App.loadHome(true);
});
$(document).on('submit', '.button-letseat-formform', function() {
$('.button-letseat-form').click();
return false;
});
$(document).on('click', '.button-letseat-form', function() {
var success = function() {
App.page.foodDelivery(true);
};
var error = function() {
$('.location-address').val('').attr('placeholder','Oops! We couldn\'t find that address!');
};
var address = $.trim($('.location-address').val());
if (!address) {
// the user didnt enter any address
$('.location-address').val('').attr('placeholder','Please enter your address here');
} else if (address && address == App.loc.address()) {
// we already have a geocode result of that address. dont do it again
console.log('SAME')
success();
} else {
// we need a new geocode result set
console.log('DIFF')
App.loc.geocode(address, success, error);
}
});
$(document).on('click', '.delivery-toggle-delivery', function() {
App.trigger.delivery();
App.track('Switch to delivery');
});
$(document).on('click', '.delivery-toggle-takeout', function() {
App.trigger.takeout();
App.track('Switch to takeout');
});
$(document).on('click', '.pay-toggle-credit', function() {
App.trigger.credit();
App.track('Switch to card');
});
$(document).on('click', '.pay-toggle-cash', function() {
App.trigger.cash();
App.track('Switch to cash');
});
$(document).on('click', '.location-detect', function() {
// detect location from the browser
$('.location-detect-loader').show();
$('.location-detect-icon').hide();
var error = function() {
$('.location-address').val('Oh no! We couldn\'t locate you');
$('.location-detect-loader').hide();
$('.location-detect-icon').show();
};
var success = function() {
App.page.foodDelivery();
// $('.location-detect-loader').hide();
// $('.location-detect-icon').show();
// $('.button-letseat-form').click();
};
App.loc.getLocationByBrowser(success, error);
});
$(document).on({
mousedown: function() {
$(this).addClass('location-detect-click');
},
touchstart: function() {
$(this).addClass('location-detect-click');
},
mouseup: function() {
$(this).removeClass('location-detect-click');
},
touchend: function() {
$(this).removeClass('location-detect-click');
}
}, '.location-detect');
$(document).on({
mousedown: function() {
if (App.busy.isBusy()) {
return;
}
if (navigator.userAgent.toLowerCase().indexOf('ios') > -1) {
return;
}
$(this).addClass('meal-item-down');
var self = $(this);
var r = self.closest('.meal-item').attr('data-permalink');
var c = self.closest('.meal-item').attr('data-permalink-community');
setTimeout(function() {
if (r) {
App.loadRestaurant(r);
} else if (c) {
App.routeAlias(c);
}
},100);
},
mouseup: function() {
$(this).removeClass('meal-item-down');
},
touchstart: function(e) {
if (navigator.userAgent.toLowerCase().indexOf('android') > -1) {
//return;
}
App.startX = event.touches[0].pageX;
App.startY = event.touches[0].pageY;
$(this).addClass('meal-item-down');
},
touchmove: function(e) {
App.touchX = event.touches[0].pageX;
App.touchY = event.touches[0].pageY;
},
touchend: function(e) {
if (navigator.userAgent.toLowerCase().indexOf('android') > -1) {
//return;
}
if (App.busy.isBusy()) {
return;
}
var maxDistance = 10;
var r = $(this).closest('.meal-item').attr('data-permalink');
var c = $(this).closest('.meal-item').attr('data-permalink-community');
if (Math.abs(App.startX-App.touchX) < maxDistance && Math.abs(App.startY-App.touchY) < maxDistance) {
if (r) {
App.loadRestaurant(r);
} else if (c) {
History.pushState({},c,c);
App.routeAlias(c);
}
}
$(this).removeClass('meal-item-down');
}
}, '.meal-item-content');
$(document).on('click', '.resturant-dish-container a', function() {
if ($(this).attr('data-id_dish')) {
App.cart.add($(this).attr('data-id_dish'));
} else if ($(this).hasClass('restaurant-menu')) {
return;
}
});
$(document).on('click', '.your-orders a', function() {
if ($(this).attr('data-id_order')) {
History.pushState({},'Crunchbutton - Your Order', '/order/' + $(this).attr('data-id_order'));
}
});
$(document).on('click', '.cart-button-remove', function() {
App.cart.remove($(this).closest('.cart-item'));
});
$(document).on('click', '.cart-button-add', function() {
App.cart.clone($(this).closest('.cart-item'));
});
$(document).on('click', '.cart-item-config a', function() {
App.cart.customize($(this).closest('.cart-item'));
});
$(document).on('click', '.button-submitorder', function() {
App.isDeliveryAddressOk = false;
App.cart.submit($(this));
});
$(document).on('click', '.button-submitorder-form', function() {
App.isDeliveryAddressOk = false;
App.cart.submit($(this),true);
});
$(document).on('click', '.button-deliver-payment, .dp-display-item a', function() {
$('.payment-form').show();
$('.delivery-payment-info, .content-padder-before').hide();
});
$(document).on({
mousedown: function() {
$(this).addClass('button-bottom-click');
},
touchstart: function() {
$(this).addClass('button-bottom-click');
},
mouseup: function() {
$(this).removeClass('button-bottom-click');
},
touchend: function() {
$(this).removeClass('button-bottom-click');
}
}, '.button-bottom');
$(document).on('click', '.cart-customize-check', function() {
App.cart.customizeItem($(this));
});
$(document).on('change', '.cart-customize-select', function() {
App.cart.customizeItem($(this));
});
$(document).on('click', '.cart-item-customize-item label', function() {
$(this).prev('input').click();
});
$(document).on('change', '[name="pay-tip"]', function() {
App.order.tip = $(this).val();
App.order.tipHasChanged = true;
var total = App.cart.total();
App.cart.updateTotal();
});
$(document).on('click', '.nav-back', function() {
History.back();
});
$(document).on('click', '.link-home', function() {
if( App.restaurants.list && App.restaurants.list.length > 0 ){
App.page.foodDelivery();
} else {
App.loadHome(true);
}
});
$(document).on('change', '[name="pay-card-number"], [name="pay-card-month"], [name="pay-card-year"]', function() {
if( !App.order.cardChanged ){
var self = $( this );
var cardInfo = [ '[name="pay-card-number"]', '[name="pay-card-month"]', '[name="pay-card-year"]' ];
$( cardInfo ).each( function( index, value ){
var input = $( value );
if( self.attr( 'name' ) != input.attr( 'name' ) ){
input.val( '' );
}
} )
}
App.order.cardChanged = true;
});
$(document).on('change, keyup', '[name="pay-card-number"]', function() {
App.detectCardType();
} );
$(document).on('click', '.link-help', function() {
History.pushState({}, 'Crunchbutton - About', '/help');
});
$(document).on('click', '.link-legal', function() {
History.pushState({}, 'Crunchbutton - Legal', '/legal');
});
$(document).on('click', '.link-orders', function() {
History.pushState({}, 'Crunchbutton - Orders', '/orders');
});
$(document).on('keyup', '[name="pay-phone"]', function() {
$(this).val( App.phone.format($(this).val()) );
});
// make sure we have our config loaded
var haveConfig = function(json) {
$(document).trigger('have-config');
App.processConfig(json);
App._init = true;
App.loadPage();
};
if (App.config) {
haveConfig(App.config)
} else {
$.getJSON('/api/config', haveConfig);
}
$(document).on('click', '.cart-summary', function() {
$('body').scrollTop($('.cart-items').position().top-80);
});
var unHideBars = function() {
$('[data-position="fixed"]').show();
}
$(document).on('focus', 'select, input, textarea', function() {
if ($(window).width() >= 768 || navigator.userAgent.toLowerCase().indexOf('android') > -1 || $(this).hasClass('location-address')) {
return;
}
clearTimeout(App.unHideBars);
$('[data-position="fixed"]').hide();
});
$(document).on('blur', 'select, input, textarea', function() {
if ($(window).width() >= 768) {
return;
}
clearTimeout(App.unHideBars);
setTimeout(unHideBars, 100);
});
var checkForDistance = function() {
if (App.order['delivery_type'] == 'takeout') {
return;
}
};
$(document).on('blur', '[name="pay-address"]', function() {
clearTimeout(App.checkForDistance);
App.checkForDistance = setTimeout(checkForDistance, 100);
});
$(document).on('change', '[name="pay-address"]', function() {
clearTimeout(App.checkForDistance);
App.checkForDistance = setTimeout(checkForDistance, 1000);
});
$(document).on('click', '.config-icon', function() {
App.loadHome(true);
});
$(document).on('change', '[name="pay-address"], [name="pay-name"], [name="pay-phone"], [name="pay-card-number"], [name="notes"]', function() {
App.config.user.name = $('[name="pay-name"]').val();
App.config.user.phone = App.phone.format($('[name="pay-phone"]').val());
App.config.user.address = $('[name="pay-address"]').val();
App.config.user.card = $('[name="pay-card-number"]').val();
App.config.user.notes = $('[name="notes"]').val();
App.config.user.card_exp_month = $('[name="pay-card-month"]').val();
App.config.user.card_exp_year = $('[name="pay-card-year"]').val();
});
$(document).on('click', '.content-item-locations-city', function() {
$( '.main-content' ).html( '' );
var permalink = $( this ).attr( 'permalink' );
App.routeAlias( permalink, function( result ){
App.loc.realLoc = {
addressAlias: result.alias.address,
lat: result.alias.lat,
lon: result.alias.lon,
prep: result.alias.prep,
city: result.alias.city
};
App.loc.setFormattedLocFromResult();
App.page.foodDelivery( true );
});
});
App.signin.init();
App.signup.init();
App.suggestion.init();
App.recommend.init();
App.loc.init();
});
App.modal.contentWidth = function(){
if( $( window ).width() > 700 ){
return 280;
}
if( $( window ).width() <= 700 ){
return $( window ).width() - 50;
}
}
App.getCommunityById = function( id ){
for (x in App.communities) {
if( App.communities[x].id_community == id ){
return App.communities[x];
}
}
return false;
}
App.message = {};
App.message.show = function( title, message ) {
if( $( '.message-container' ).length > 0 ){
$( '.message-container' ).html( '<h1>' + title + '</h1><div class="message-container-content">' + message + '</div>' );
} else {
var html = '<div class="message-container">' +
'<h1>' + title + '</h1>' +
'<div class="message-container-content">' +
message +
'</div>' +
'</div>';
$('.wrapper').append(html);
}
$('.message-container')
.dialog({
modal: true,
dialogClass: 'modal-fixed-dialog',
width: App.modal.contentWidth(),
close: function( event, ui ) { App.modal.shield.close(); },
});
}
App.registerLocationsCookies = function() {
$.cookie('location_lat', App.loc.lat, { expires: new Date(3000,01,01), path: '/'});
$.cookie('location_lon', App.loc.lon, { expires: new Date(3000,01,01), path: '/'});
$.cookie('location_range', ( App.loc.range || App.defaultRange ), { expires: new Date(3000,01,01), path: '/'});
}
App.message.chrome = function( ){
var title = 'How to use Chrome',
message = '<p>' +
'Just tap "Request Desktop Site.' +
'</p>' +
'<p align="center">' +
'<img style="border:1px solid #000" src="/assets/images/chrome-options.png" />' +
'</p>';
App.message.show(title, message);
}