/** * * Crunchbutton * * @author: Devin Smith (http://devin.la) * @date: 2012-06-20 * */ var App = { cartHighlightEnabled: false, currentPage: null, slogans: ['Push a button. Get Food.'], tagline: '', service: '/api/', cached: {}, community: null, page: {}, config: null, forceHome: false, order: { cardChanged: false, pay_type: 'card', delivery_type: 'delivery', tip: '15' }, signin : {}, suggestion : {}, restaurants: { permalink : 'food-delivery' }, defaultRange : 2, modal: {}, hasBack: false, _init: false, _pageInit: false, _identified: false, isDeliveryAddressOk : false, tips: [0,5,10,15,20,25] }; App.loadRestaurant = function(id) { App.cache('Restaurant', id,function() { if (!this.open()) { var hours = ''; for (var x in this._hours) { hours += x + ': '; for (var xx in this._hours[x]) { hours += this._hours[x][xx][0] + ' - ' + this._hours[x][xx][1] + (xx == 0 ? ', ' : ''); } hours += "\n"; } alert("This restaurant is currently closed. It will be open during the following hours (" + this._tzabbr + "):\n\n" + hours); 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) { // Get the alias alias = App.aliases[ id ] || false; if( alias ){ // Get the location of the alias var loc = App.locations[ alias.id_community ]; if( loc.loc_lat && loc.loc_lon ){ App.loc.lat = loc.loc_lat; App.loc.lon = loc.loc_lon; App.loc.range = loc.range; App.loc.prep = alias.prep; App.loc.name_alt = alias.name_alt; $.cookie( 'location_prep', alias.prep, { expires: new Date(3000,01,01), path: '/'}); $.cookie( 'location_name_alt', alias.name_alt, { expires: new Date(3000,01,01), path: '/'}); App.registerLocationsCookies(); App.loc.range App.foodDelivery.preProcess(); return; } } // If the alias doesn't exist show the home with the error location message. App.forceHome = true; App.showErrorLocation = true; App.loadHome(); return; }; App.loadHome = function() { App.currentPage = 'home'; History.pushState({}, 'Crunchbutton', '/'); if( App.forceHome ){ App.page.home(); } }; App.page.resetPassword = function( path ){ if( !App.signin.passwordHelp.reset.hasStarted ){ App.signin.passwordHelp.reset.hasStarted = true; $( '.wrapper' ).append( App.signin.passwordHelp.reset.html( path ) ); App.showReset = true; App.page.home(); } } 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(); 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.loc.process(); 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 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 = $('').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 = function() { // random taglines App.taglines = [ { name: 'tagline-for-free', tagline: 'Order the top food %s. For free.
After you order, everything is saved for future 1 click ordering.
Choose a restaurant:' }, { name: 'tagline-no-free', tagline: 'Order the top food %s.
After you order, everything is saved for future 1 click ordering.
Choose a restaurant:' } ]; App.slogan = App.slogans[Math.floor(Math.random()*App.slogans.length)]; App.tagline = App.taglines[Math.floor(Math.random()*App.taglines.length)]; App.trackProperty('restaurant-tagline', App.tagline.name); }; 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 = $('
'); el.append('
'); el.append('
' + App.cache('Dish',item).name + ' ' + (App.cache('Dish',item).description != null ? App.cache('Dish',item).description : '') + '
'); if (App.cached['Dish'][item].options().length) { el.append('
Customize
'); } el.hide(); $('.cart-items-content').append(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(); }, /** * 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.total(), tipText = '', feesText = '', totalItems = 0, hasFees = ((App.restaurant.delivery_fee && App.order.delivery_type == 'delivery') || App.restaurant.fee_customer) ? true : false; for (var x in App.cart.items) { totalItems++; } /* 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 15% (variable App.order.tip). * If the user had changed the tip value the default value will be chosed one. */ var wasTipChanged = false; if( App.order.delivery_type == 'takeout' && App.order['pay_type'] == 'card' ){ if( typeof App.order.tipHasChanged == 'undefined' ){ App.order.tip = 0; wasTipChanged = true; } } else if( App.order.delivery_type == 'delivery' && App.order['pay_type'] == 'card' ){ if( typeof App.order.tipHasChanged == 'undefined' ){ App.order.tip = ( App.config.user.last_tip ) ? App.config.user.last_tip : 15; // Default value is 15 wasTipChanged = true; } } if( wasTipChanged ){ $('[name="pay-tip"]').val( App.order.tip ); // Forces the recalculation of total because the tip was changed. totalText = '$' + this.total(); } 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(); } 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)); } $('.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 += ' (' + totalItems[x] + ')'; } text += ',  '; } $('.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' ? ' ($' + 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 = $('
').insertAfter(item), cartitem = App.cart.items[cart], obj = App.cached['Dish'][cartitem.id], opt = obj.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 = $(''); if ($.inArray(opt[x].id_option, cartitem.options) !== -1) { check.attr('checked','checked'); } var option = $('
') .append(check) .append('' ); el.append(option); } else if (opt[x].type == 'select') { var select = $('