/**
 * Rich Cart Widget
 * This widget provides the main rich cart functionality.
 * Created by Jim Barraud, 10/11/06 
 * Reworked by James Wiltshire, 01/17/2007
 */
dojo.provide("atg.b2cblueprint.widget.RichCartSummary");
dojo.require("dojo.lfx.*");
dojo.require("dojo.html.*");
dojo.require("dojo.html.iframe");
dojo.require("dojo.event.*");
dojo.require("dojo.widget.*");
dojo.require("dojo.io.*");

dojo.widget.defineWidget(
  "atg.b2cblueprint.widget.RichCartSummary", 
  dojo.widget.HtmlWidget,
  {
    // Define all global variables for the widget.
    // templatePath: dojo.uri.dojoUri(contextPath+"/javascript/widget/template/richCartSummary.html"),    
    templateString: '<div id="${this.widgetId}" dojoAttachPoint="csContainer" style="overflow:hidden;z-index:1;"><div id="mff_richCartHeader"><h3>${this.i18n.myCartSummary}</h3><a href="javascript:void(0);" class="atg_CoD_csClose" dojoAttachPoint="csClose" dojoAttachEvent="onClick:hide"><span>${this.i18n.close}</span></a></div><div id="atg_CoD_csContent" dojoAttachPoint="csContent"><div class="csEmpty" dojoAttachPoint="csEmptyMessage">${this.i18n.cartIsEmpty}</div></div><div id="atg_CoD_csFooter"><span dojoAttachPoint="richCalculateOrderShippingLink" id="atg_b2cblueprint_richCalculateOrderShippingLink" style="display:block"></span><div class="mff_richCartFooterSubtotal"><span dojoAttachPoint="csSubtotalContainer">${this.i18n.subtotal}&nbsp;&nbsp;<span class="atg_CoD_csSubtotal" dojoAttachPoint="csSubtotal"></span></span></div><a href="${this.url.checkout}" class="atg_CoD_csCheckout mff_inputButton" dojoAttachPoint="csCheckout" onClick="assignCheckout()">${this.i18n.checkout}</a></div></div>',

    // Widget properties
    triggerWidget: null,  // Reference to trigger widget
    data: null,           // Cart data - should be set with initial widget initialisation properties
    quantityNodeId: null, // DOM ID of the node to contain the cart quantity - i.e. "Show Cart (3)"
    isContainer: true,    // core var, shows Dojo that this widget will contain other widgets
    hijackClassName: null,  // CSS class used to signify forms/anchors to hijack 
    highlightColor: null, // Color used to highlight newly added items
    firstPlacementDone: false,
    cartAnimationInProgress: false,
    duration:{
      // Durations in ms of animation elements
      highlight: 3000,
      scroll:500,
      wipe: 280,
      autoHide:5000
    },

    /**
     * Initialize the widget
     */
    initialize: function(){
      dojo.debug("Initializing RichCartSummary widget");          
      // Load any initial data into the widget
      if (this.data!==null){
        this.setAllCartData(this.data);
      }     

      // Hook up event handling
      var _this=this;
      dojo.event.connect(window, "onresize", this, "placeCart");
      dojo.event.connect(window, "onscroll", this, "placeCart");
      dojo.event.connect(document.body, "onclick", function(evt){
        // If body is clicked and cart is showing, hide it. Ignore clicks on links or submit buttons
        // as they'll be perfoming an action themselves.
        var type=evt.target.nodeName;
        if (_this.isShowing && type!="A" && type!="INPUT" && type!="BUTTON"){
          _this.hide();
        }
      });
      dojo.event.connect(this.domNode, "onclick", function(evt){
        // Prevent body from handling click within the cart
        evt.stopPropagation(); 
      });
      
      this.triggerWidget=dojo.widget.byId("richCartTrigger");
      
      if (this.doHijack===true){
        this.hijackAllAddToCartNodes();
      }    
    },
    
    postCreate: function(){     
      var _this=this;
      dojo.addOnLoad(function(){
        // Prepare for first show animation - hide the element's domNode and call the
        // hide animation. Set a callback to change the visibility when hide is complete.
        _this.domNode.style.visibility="hidden";
        _this.attachToContainer();
        _this.hide(function(){
          _this.domNode.style.visibility="visible";
        });
      });
    },
    
    /**
     * Set the rich cart to display all of the passed in data. This function will be
     * called whenever the rich cart widget is initialised (i.e. on page load) and
     * also whenever an item has been added to the cart and a JSON XHR response
     * is received containing the new cart contents.
     */
    setAllCartData: function(pData){
      dojo.debug("Setting all cart data");
      dojo.debug(pData);
      this.data=pData;
      this.clearCartItems();
      this.setCartSummaryData();
      
      if (pData.items){
        // Create CartSummaryItem widget for each line item and add to this parent widget       
        for (var i=0; i<pData.items.length; i++){        
          this.addCartItem(pData.items[i]);
        }
      }
    },
    
    /**
     * Set the summary data for the cart - this includes the subtotal and item quantity
     */
    setCartSummaryData: function(){      
      // Set the cart quantity total - this is the qty in the 'View Cart (3)' link
      var el;
      if (this.quantityNodeId!==null){
        el=dojo.byId(this.quantityNodeId);
        if (el){
          el.innerHTML = "" + this.data.itemCount + "";
        }       
      }
      
      if (this.data.itemCount===0){
        this.showEmptyCart(true);
      }
      else {
        this.showEmptyCart(false);
        // Set the subtotal amount in the cart
        this.csSubtotal.innerHTML = this.data.subtotal;
      }          
    },
    
    /**
     * Turn on/off certain elements of the cart if it is empty. Display a
     * 'cart is empty' message if it is
     */
    showEmptyCart: function(pEmpty){
      if (pEmpty===true){
        // Cart is empty
        dojo.html.hide(this.csSubtotalContainer);
        dojo.html.hide(this.csCheckout);
        
        dojo.html.hide(this.richCalculateOrderShippingLink);
        //dojo.html.hide(dojo.byId("atg_b2cblueprint_richCalculateOrderShippingLink"));
        dojo.html.show(this.csEmptyMessage);
      }
      else {
        // Cart is not empty
        dojo.html.show(this.csSubtotalContainer);
        dojo.html.show(this.csCheckout);
        dojo.html.show(this.richCalculateOrderShippingLink);
        //dojo.html.show(dojo.byId("atg_b2cblueprint_richCalculateOrderShippingLink"));
        dojo.html.hide(this.csEmptyMessage);
      }
    },
    
    /**
     * Add a line item to the rich cart
     */
    addCartItem: function(data){
      dojo.debug("Adding a Line Item");
      dojo.debug(data);
      var lineItem = dojo.widget.createWidget("atg.b2cblueprint:RichCartSummaryItem", {
        data: data,
        highlightColor: this.highlightColor,
        highlightDuration: this.duration.highlight,
        scrollDuration: this.duration.scroll
      });
      
      this.addChild(lineItem);
      this.csContent.appendChild(lineItem.domNode);
    },   
    
    /*
     * Clear all items from the cart
     */
    clearCartItems: function(){
      this.destroyChildren();
    },
        
    /**
     * Toggle display of the cart. This will be called by the CartTrigger widget whenever the
     * show/hide rich cart link is clicked.
     */
    toggleCart: function(){
      
      //Added for google analytics
      
      if(typeof(pageTracker) != 'undefined'){
        pageTracker._trackEvent('shopping cart','top nav');
      }

      if (this.isShowing===true){
        this.hide();
      }
      else {
        this.show();
      }
    },
    
    /**
     * Position the cart at the correct location on screen
     */
    placeCart: function(){
      if (!this.isShowing && this.firstPlacementDone && !this.cartAnimationInProgress){
        return;
      }
      
      this.firstPlacementDone=true;
      var node = this.triggerWidget.triggerLink;
      var pos = dojo.html.getAbsolutePosition(node);
      var cartLeft,cartTop,cartHeight,triggerHeight,scrollOffsetHeight;
      
      // Left position is 228 pixels left of the trigger link
      cartLeft = pos.left - 0;
      cartLeft = (cartLeft > 0) ? cartLeft : 0;
      
      // Top position is directly under the trigger link
      triggerHeight = dojo.html.getMarginBox(node).height;
      scrollOffsetHeight=dojo.html.getScroll().offset.y;
      cartTop = pos.top + triggerHeight + scrollOffsetHeight;
      cartTop = (cartTop-scrollOffsetHeight > 0) ? cartTop : scrollOffsetHeight; 
      
      //dojo.debug("pos.top:"+pos.top+" pos.left:"+pos.left+" triggerHeight:"+triggerHeight+ "scrollOffsetHeight:"+scrollOffsetHeight);
      dojo.debug("Placing cart @ "+cartLeft+", "+cartTop);
                
      this.domNode.style.left=cartLeft+"px";
      this.domNode.style.top=cartTop+"px";            
    },

    /**
     * Show the Rich Cart
     */
    show: function(callback) {
    
      if (this.isShowing){
        // If we've been passed a callback function, then call it even if we're showing. It's
        // most likely that a new item has been added, and the callback is the highlight
        if (callback){
          callback();
        }
        return;
      }
      
      if (this.cartAnimationInProgress===true){
        return;
      }
                
      this.cartAnimationInProgress=true;     
      this.placeCart();
      var _this=this;
      var wipeAnimation=dojo.lfx.html.wipeIn(this.domNode, this.duration.wipe, dojo.lfx.easeOut, function(){
        _this.isShowing = true;
        _this.cartAnimationInProgress=false;
        
        // IE6 - prevent form elements from shining through cart with hidden bg iframe
        if(dojo.render.html.ie){
          if(!_this.bgIframe){
            _this.bgIframe = new dojo.html.BackgroundIframe();
            _this.bgIframe.setZIndex(_this.domNode);
          }
          _this.bgIframe.size(_this.domNode);
          _this.bgIframe.show();
        }

        if (callback && dojo.lang.isFunction(callback)){
          callback();
        }
        
      });
      var fadeAnimation=dojo.lfx.html.fade(this.domNode, { start:0.3, end: 1 }, this.duration.wipe, dojo.lfx.easeOut);    
      dojo.lfx.combine([wipeAnimation,fadeAnimation]).play();
    },

    /**
     * Hide the Rich Cart
     */
    hide: function(callback){
      if (this.cartAnimationInProgress===true){
        return;
      }
      
      this.cartAnimationInProgress=true;
      var _this=this;
      var wipeAnimation=dojo.lfx.html.wipeOut(this.domNode, this.duration.wipe, dojo.lfx.easeIn, function(){
        _this.isShowing = false;
        _this.cartAnimationInProgress=false;
        
        // IE6 - Prevent form element shine through - hide hidden iframe
        if(_this.bgIframe){
          _this.bgIframe.hide();
          _this.bgIframe.size({left:0, top:0, width:0, height:0});
        }
		
        if (callback && dojo.lang.isFunction(callback)){
          callback();
        }
      });
      var fadeAnimation=dojo.lfx.html.fade(this.domNode, { start:1, end: 0.8 }, this.duration.wipe); 
      dojo.lfx.combine([wipeAnimation,fadeAnimation]).play();      
      this.triggerWidget.updateTriggerDisplay();
      this.clearAutoHide();
    },
    
    /*
     * Get an array of all items that have been flagged as 'modified. This will
     * usually be just a single item - the item that has just been added to the cart.
     */
    getChangedItemWidgets: function(){
      var changedItems=[];
      var item;
      for (var i=0; i<this.data.items.length; i++){
        item=this.data.items[i];
        if (item.modified===true){
          // Get referene to child widget
          changedItems[changedItems.length] = this.children[i];     
        }
      }
      return changedItems;
    },

    /**
     * Start the auto-hide timer. This will close the rich cart after a short period of
     * time, unless the use mouses over the cart display, in which case the timer
     * will be cancelled so the user can continue to view the contents
     */
    startAutoHide: function(){      
      // Clear any existing auto-hide timer
      if (this.autoHideTimer!==null){
        this.clearAutoHide();
      }
      
      dojo.debug("Starting auto-hide (in "+this.duration.autoHide+" ms)");
      
      // Auto-hide the rich cart after n ms...
      this.autoHideTimer = dojo.lang.setTimeout(this, this.hide, (this.duration.autoHide));
      
      // ... unless the user mouses over the cart
      dojo.event.connect(this.domNode, "onmouseover", this, "clearAutoHide");
    },
      
    /**
     * Clear the auto-hide timer. This will stop the widget from auto-hiding. The user
     * must now click the 'hide rich cart' link/icon to hide the widget 
     */
    clearAutoHide: function(){
      dojo.debug("Clearing auto-hide");
      clearTimeout(this.autoHideTimer);
      dojo.event.disconnect(this.domNode, "onmouseover", this, "clearAutoHide");
      this.autoHideTimer=null;        
    },
    
    /**
     * Handle a JSON response following an 'add to cart' form submission
     */
    handleResponse: function(data,evt,node){
    
      //Added for google analytics   
      if(typeof(pageTracker) != 'undefined'){
        pageTracker._trackEvent('product','add');
      }
      
      dojo.debug("RichCart:handleResponse");
      dojo.debug(data);
      dojo.debug(node);
      
      // If we got no data whatsoever, then treat this as a serious error
      if (!data){
        this.handleError();
        return;
      }
      
      // If we got an error back in the data then we need to update the UI to display the errors
      if (data.error){
        dojo.debug("Received error from server - resubmitting form");

        var content = "";
        for (var i  = 0; i < data.errors.length; i++) {
          // fix duplicate message issue(footprints #161813)
          // skip generic message if more detailed exists
          if (data.errors.length > 1 && data.errors[i] == 'There was an error while attempting to add the item to the order.') {
            continue;
          } 
          content += "<p>" + data.errors[i] + "</p><br/>";
        }
        var errorContainer = document.getElementById('atg_b2cblueprint_cartFormHandler_errorMsg');
        
        if (errorContainer != null) {
          errorContainer.innerHTML = content;
          errorContainer.style.display = "";
        }
        
        this.enableNode(node);
        return;
      } else {
         var errorContainer = document.getElementById('atg_b2cblueprint_cartFormHandler_errorMsg');
         if (errorContainer != null) {
           errorContainer.innerHTML = "";
           errorContainer.style.display = "none";
         }
      }
      
      //Update 'Calculate shipping' content
      var calculateShippingWidjet = dojo.widget.byId("calculateOrderShipping");
      
      if (calculateShippingWidjet.isShowing) {
        calculateShippingWidjet.hide();
      }

      dojo.io.bind({
      load: function(type, data, evt){
          calculateShippingWidjet.setAllCartData(data);
        },
        formNode: dojo.byId("calculateOrderShippingInitForm"),
        mimetype:'text/json'
      });
      
      dojo.io.bind({
        load: function(type, data, evt){
  
        var hiddenFields = dojo.byId("atg_mff_calculateOrderShippingHiddenFields");
        hiddenFields.innerHTML = data;
        
        },
        formNode: dojo.byId("calculateOrderShippingHiddenFieldsForm")
      });
      
      
      // Clear shipping total
      calculateShippingWidjet.calculateShippingTotal.innerHTML = "";
      
      // All good, update the UI with new cart data
      this.setAllCartData(data);       
      var alreadyShowing=this.isShowing;    
      var changedItems=this.getChangedItemWidgets();

      // Show the cart and scroll the first newly added item into view
      var _this=this;
      this.show(function(){
        // Scroll the first newly added item into view
        if (changedItems.length>0){
          changedItems[0].scrollIntoView();
        }
        for (var i=0; i<changedItems.length; i++){
          changedItems[i].highlight();
        }
        _this.enableNode(node);
      });

      atg.b2cblueprint.shipping.initSaturdayDelivery(calculateShippingWidjet);
           
      // Start the auto-hide timer if the cart wasn't already showing. This will also reset the timer
      // if it's already running
      if (!alreadyShowing || this.autoHideTimer!==null){
        this.startAutoHide();
      }
    },
    
    /*
     * Connect the cart to all forms and links that have the specified className.
     * The class will be set on any <input type="submit"> and <a> tags that are submitted or clicked
     * to add items to the cart. All of these nodes must be 'hijacked' so that the
     * http request uses XHR so that the rich cart can operate.
     */
    hijackAllAddToCartNodes: function(){
      dojo.debug("Connecting RichCart to all elements with class ["+this.hijackClassName+"]");
      var elements=dojo.html.getElementsByClass(this.hijackClassName);
      for (var i=0; i<elements.length; i++){
        this.hijackNode(elements[i]);
      }     
    },
    
    /*
     * Hijack a node. The node should be either a <form> or an <a> node.
     * Hookup the submit to use XHR instead of standard browser request, and 
     * process the returned JSON data with the handleRespones() function
     * 
     */
    hijackNode: function(node){
      dojo.debug("Hijacking node");
      dojo.debug(node);
           
      if (node.isHijacked){
        dojo.debug("Node is already hijacked - ignoring");
        return;
      }
      node.isHijacked=true;
           
      // Create object with common params for io.bind call
      var _this = this;
      var bindParams={
        headers: { "Accept" : "application/json" },
        mimetype: "application/json",
        load: function(type, data, evt) {
          _this.handleResponse(data,evt,node);
        },
        error: function(type, evt) {
          _this.handleError(type, evt);
        },
        timeout: function(type, evt) {
          _this.handleError(type, evt);
        }
      };
      
      if (node.nodeName=="INPUT"){
        var formNode=dojo.html.getParentByType(node,"FORM");
        dojo.event.connect(formNode, "onsubmit", function(evt){
          safePreventEvent(evt);
          
          // Create content object with the name/value pair of the submit button that's been clicked
          // dojo.io.bind doesn't send the value of submit buttons when serializing the form as it 
          // doesn't know which one has been clicked. Server side FormHandlers need this data to
          // invoke the correct formHandler method.
          if (!node.disabled) { //special for IE
            var content={};
            content[node.name]=node.value;
          
            dojo.debug("Add to Cart form clicked - submitting form");
            dojo.debug(formNode);
          
            // Add the form node and the submit button name/value to the io.bind params
            dojo.lang.mixin(bindParams,{
              formNode: formNode,
              content: content
            });
          
            _this.disableNode(node);
            dojo.io.bind(bindParams);
          }
        });
      }
      else if (node.nodeName=="A"){        
        dojo.event.connect(node, "onclick", function(evt){
          dojo.debug("Add to Cart link clicked");
          evt.preventDefault();
          
          // Ensure it's not a double click
          if (node.currentlyAdding && node.currentlyAdding===true){
            dojo.debug("This link has already been clicked - ignoring");
            return;
          }
          
          // Add the URL of the clicked link to the io.bind params
          dojo.lang.mixin(bindParams,{
            url: node.href
          });
         
          _this.disableNode(node);
          dojo.io.bind(bindParams);
        });
      }
      else{
        dojo.debug("Node is not a form submit or an anchor - ignoring");
      }     
    },
    
    /**
     * Attach this widget's domNode to its containing node
     */
    attachToContainer: function(){
      dojo.debug("Appending cart domNode to body");
      document.body.appendChild(this.domNode);
    },
    
    /**
     * Function that will be called whenever an error or timeout occurs with an io.bind call
     * Changes the page location to 'url.error'. This is usually set to the standard cart
     * page, so if anything goes wrong with the Rich Cart, we get to fall back to the standard cart.
     */
    handleError: function(type, evt){
     //alert('error!!!!!!!!!!!');
      //document.location=this.url.error;
    },
    
    /**
     * Disable a node whilst it is being added to the cart. Used to prevent double clicks resulting
     * in duplicate additions to the cart.
     */
    disableNode: function(node){
      // Store original properties we're about to mess with
      node.originalProps={};
      node.originalProps.width=node.style.width;
      node.originalProps.height=node.style.height;
      
      // Set values for width and height so element doesn't change size
      node.style.width=dojo.html.getBorderBox(node).width+"px";
      //node.style.height=dojo.html.getBorderBox(node).height+"px";
        
      if (node.nodeName=="INPUT"){               
        node.originalProps.value=node.value;
        node.disabled=true;
        node.value=this.i18n.addingToCart;
      }
      else if (node.nodeName=="A"){
        node.originalProps.innerHTML=node.innerHTML;
        node.currentlyAdding=true;
        node.innerHTML=this.i18n.addingToCart;
      }
    },
    
    /**
     * Re-enable a node that has been disabled by disableNode();
     */
    enableNode: function(node){      
      if (node.nodeName=="INPUT"){
        node.disabled=false;
        node.value=node.originalProps.value;
      }
      else if (node.nodeName=="A"){
        node.currentlyAdding=false;
        node.innerHTML=node.originalProps.innerHTML;      
      }
      
      // Reset size attributes
      node.style.width=node.originalProps.width;
      node.style.height=node.originalProps.height;
      node.originalProps=null;
    },
    
    /**
     * Resubmit the AddToCart form using a normal HTTP request (non XHR).
     * The Submit button's node should be passed in to signify which button was clicked.
     */
    resubmitForm: function(node){
      
      // Create hidden form element to copy submit button's value into. Need to do this as disabled elements
      // are not submitted from a form by the browser.
      var replacementNode=document.createElement("INPUT");
      replacementNode.type="hidden";
      replacementNode.name=node.name;
      replacementNode.value=node.value;
      
      // Append this to the parent form
      var formNode=dojo.html.getParentByType(node,"FORM");
      formNode.appendChild(replacementNode);
      
      formNode.submit();
    }
  }
);


