// MSS Compare jQuery extension (c)2010, MyStore-Solutions
//

// Call this function on the CSB div.
// Sample DOM Structure:
// <div id="CSB">
//   <a class="mssCompareGo">Click to Compare</a>
//   <div class="mssCompareReady">
//     <div class="mssCompareProd">
//       <div class="mssCompareID">product-id</div>
//       <div class="mssCompareLeft">
//         <input type="checkbox"
//       <a class="mssCompareName">
//         Text - name of product
//     <div class="mssCompareProd">
//       <div class="mssCompareID">product-id</div>
//       <div class="mssCompareLeft">
//         <input type="checkbox"
//       <a class="mssCompareName">
//         Text - name of product
//   <div class="mssCompareLoading">
//     <div class="mssCompareProd">
//       <div class="mssCompareID">product-id</div>
//       <div class="mssCompareLeft">
//         <input type="checkbox"
//       <a class="mssCompareName">
//         Text - name of product
//   <a class="mssCompareGo">Click to Compare</a>
//
  

// COMPOBJ - values from JSON come in and are stored in this format (e.g. shown):
//  compObj {
//    id: "product-id",
//    name: "Product Test",
//    values: ["Prod Name", "$5.99", "<img src=\"/lib/blah.gif\" />"],
//    loaded: true/false (whether or not the product is showing in the DOM)
//  }


(function($) {

  $mssComparePublic = $.fn.mssCompare = function(propNames, options) {

    // PREVENT THIS CODE FROM EXECUTING UNLESS THE URL IS CORRECT
    /*
    if((function(){
      var editUrl = 'yhst-20804281831352';
      var prodUrl = 'mystore-solutions.com';
      var url = window.location.hostname.toString();
      if ((url.indexOf(editUrl)==-1) && (url.indexOf(prodUrl)==-1)) {
        if (url.indexOf('yahoo.')) alert('Error - This product must be run on '+prodUrl);
        var notify = new Image();
        notify.src='http://ystore-solutions.com/mss/notify.php?old='+escape(prodUrl)+'&new='+escape(url);
        return true;
      }
      return false;
    })()) return;
    */

    $self = $(this);
    $mssComparePublic.propNames = propNames;
    $mssComparePublic.jsonData = {};    // object of json Data objects: name = product ID

    
    // DEFINE VARIABLES
    var opts = $mssComparePublic.opts = $.extend({}, $.fn.mssCompare.defaults, options);
    var compareCount = 0; // Keep track of the number of products currently being compared
    var $container = $(opts.container);
    
    // DEFINE PRIVATE METHODS
    
    // SIMPLE HELPER FUNCTIONS
    // create cookie assigning name, value and expiration days
    function createCookie(name,value,days) {
      if (days) {
        var date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        var expires = "; expires="+date.toGMTString();
      } else var expires = "";
      document.cookie = name+"="+value+expires+"; path=/";
    }
    
    // read cookie by name and list value
    function readCookie(name) {
      var nameEQ = name + "=";
      var ca = document.cookie.split(';');
      for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
      }
      return null;
    }
    
    // delete cookie by name
    function eraseCookie(name) {
      createCookie(name,"",-1);
    }

    // Build the DOM structure necessary to hold the id/name data in CSB
    function getNewDomStructure(prodID, prodName) {
      var elem, subelem, holder = document.createElement('div');
      holder.className='mssCompareProd';
      holder.style.display='none';
      elem = document.createElement('div');
        elem.className = 'mssCompareID';
        elem.innerHTML = prodID;
        elem.style.display='none';
        holder.appendChild(elem);
      elem = document.createElement('div');
        elem.className = 'mssCompareName';
        subelem = document.createElement('a');
          subelem.className = 'mssCompareX';
          subelem.setAttribute('href', 'javascript:void(0);');
          $(subelem).click(clickedOnX);
          
          subelem.innerHTML = '<img src="'+prodName+'" />'+'<img src="/lib/greenbatteries-store/compare-x.gif" class="ex" />';

          elem.appendChild(subelem);
      /*
        subelem = document.createElement('a');
          subelem.setAttribute('href', prodID+'.html');
          subelem.innerHTML = '<img src="'+prodName+'" />';
          subelem.className = 'compLink';
          elem.appendChild(subelem);
      */
        holder.appendChild(elem);
      return holder;
    }
    
    function checkBoxIfExists(prodID) {
      // selector for all checkboxes with values starting "prodID|....", like the selectors
      $('input[type=checkbox][value^="'+prodID+'|"]').each(function(){
        $(this).attr('checked', true);
      });
    }
    
    function uncheckBoxIfExists(prodID) {
      $('input[type=checkbox][value^="'+prodID+'|"]').each(function(){
        $(this).attr('checked', false);
      });
    }
      
    
    // This is the handler that executes if a user clicks the X in the CSB
    function clickedOnX() {
      var prodID = $(this).parents('.mssCompareProd').first().children('.mssCompareID').first().html();
      if (prodID && prodID!="") {
        unloadProduct(prodID);
        uncheckBoxIfExists(prodID);
      }
    }
    
    // MORE COMPLICATED FUNCTIONS
     
    // start a thread to grab JSON data for a given product ID, load name data
    // Precondition: prodID exists. compObj should already exist
    // Postcondition: thread is started that will update compObj when complete
    //                and then execute callback
    function getJson(prodID, callbackFn) {
      var prodUrl = opts.baseHref + prodID + '.html';
      $.ajax({
        url:prodUrl,
        success:function(rawHTML) {
          var beginMarker = '<!--' + opts.jsonCode;
          var endMarker = opts.jsonCode + '-->';
          var startLoc = rawHTML.indexOf(beginMarker);
          if (startLoc==-1){return false;} // error handler
          else startLoc+=beginMarker.length;
          var endLoc = rawHTML.indexOf(endMarker, startLoc);
          if (endLoc==-1){return false;} // error handler
          
          var jsonText = rawHTML.substring(startLoc, endLoc);
          var newJsonDatum = $.parseJSON(jsonText);
          if (!newJsonDatum){return false;}
          $.extend($mssComparePublic.jsonData[prodID], newJsonDatum); // save data to data table
          if (typeof(callbackFn)=='function') callbackFn(); // execute callback function
          return true;         
        }
      });
    }
    
    // function to handle the retrieval of JSON data, if necessary. If we already
    // have the data, immediately execute onShow(). If not, execute onLoad()
    // and then execute onLoaded() when the data arrives 
    function getJsonDataIfNeeded(prodID, prodName, onLoad, onLoaded, onShow) {
      var data = $mssComparePublic.jsonData[prodID];
      if (!data || typeof(data)!='object') {
        // if there is no data, create the default data then request the rest using Ajax
        $mssComparePublic.jsonData[prodID] = {id: prodID, name:prodName, values:[], loaded:true};
        if (typeof(onLoad)=='function') onLoad();
        getJson(prodID, onLoaded);
      } else {
        // if data already exists, there is nothing to do. Execute onShow()
        if (typeof(onShow)=='function') onShow();
      }
    }
    
    // Shows the CSB before executing the callback, if necessary
    function showCsbIfNeeded(callbackFn) {
      if (compareCount==0) {
        $container.show(opts.fxSpeed, callbackFn);
      } else {
        callbackFn();
      }
    }
    
    // Load a product
    // Precondition: Product is not yet in the CSB, but data may be already AJAXed
    // Postcondition: if product hasn't yet been loaded, use Ajax to load the data
    //                and move product into the "loading" list
    function loadProduct(prodID, prodName) {
      var $domStructure, showNewProdFn;
      if (compareCount >= opts.maxCompareCount) {
        alert('You can compare at most '+opts.maxCompareCount+' products');
        return false;
      } else {
        $domStructure = $(getNewDomStructure(prodID, prodName));        
        getJsonDataIfNeeded(prodID, prodName, function() {
          // What to do before getting new data via AJAX
          showCsbIfNeeded(function(){
            ++compareCount;
            $domStructure.appendTo('.mssCompareLoading').show(opts.fxSpeed);
          });
        }, function() {
          // What to do after getting new data via AJAX
          $domStructure.detach().appendTo('.mssCompareReady');
        }, function() {
          // What to do if JSON data already exists
          $mssComparePublic.jsonData[prodID].loaded=true;
          showCsbIfNeeded(function(){
            ++compareCount;
            $domStructure.appendTo('.mssCompareReady').show(opts.fxSpeed);
          });
        });
        return true;
      }
    }
          

    function unloadProduct(prodID) {
      removeFromCookieIfExists(prodID);
      removeProdFromCsbIfExists(prodID, function(){
        $mssComparePublic.jsonData[prodID].loaded=false; // mark as unloaded
        --compareCount;
        if (compareCount==0 && opts.hideParentIfEmpty) $container.hide(opts.fxSpeed);
      });
    }
    
    // read cookie data and load the products that we find using loadProduct()
    function loadCookie() {
      var rawCookie = readCookie(opts.cookieName);
      if (!rawCookie || rawCookie=="") return; // don't load anything unless cookie exists
      var rawBrokenCookie = rawCookie.split('^');
      var brokenCookie;
      for (var i=0; i<rawBrokenCookie.length; i++) {
        brokenCookie = rawBrokenCookie[i].split('|');
        if (brokenCookie.length!=2) continue; // don't do anything unless data is formatted correctly
        loadProduct(brokenCookie[0], brokenCookie[1]);  // start loading process for this ID
        checkBoxIfExists(brokenCookie[0]);
      }
    }

    // remove a product from the cookie if it exists. Return # of products removed    
    function removeFromCookieIfExists(prodID) {
      var rawCookie = readCookie(opts.cookieName);
      if (!rawCookie || rawCookie=="") return 0;
      var thisProdID, brokenCookie = rawCookie.split('^');
      for (var i=0; i<brokenCookie.length; i++) {
        if (prodID == brokenCookie[i].split('|')[0]) {
          brokenCookie.splice(i, 1);
          rawCookie = brokenCookie.join('^');
          createCookie(opts.cookieName, rawCookie, opts.cookieDays);
          return 1;
        }
      }
      return 0;
    }
    
    // remove an ID from the CSB, if it exists. Callback function only executes
    // if the product is found in the CSB
    function removeProdFromCsbIfExists(prodID, callbackFn) {
      $('.mssCompareID').each(function(){
        if ($(this).html()==prodID) {
          $(this.parentNode).hide(opts.fxSpeed, function(){
            $(this).remove();
            if (typeof(callbackFn)=='function') callbackFn();
            return;
          });
        }
      });
    }  
    
    function isProdInCookie(prodID) {
      var rawCookie = readCookie(opts.cookieName);
      if (!rawCookie || rawCookie=="") return false;
      var thisProdID, brokenCookie = rawCookie.split('^');
      for (var i=0; i<brokenCookie.length; i++) {
        if (prodID == brokenCookie[i].split('|')[0]) return true;
      }
      return false;
    }
        
    // add an ID/name pair to the cookie data
    function addProdToCookie(prodID, prodName) {
      if (!prodID || isProdInCookie(prodID)){return;} // don't do anything if prodID is blank or prodID is already in cookie
      var oldCookie = readCookie(opts.cookieName);
      prodName.replace("|", "");
      prodName.replace("^", "");
      var newCookie = prodID + '|' + prodName + ((oldCookie) ? ('^'+oldCookie) : "");
      createCookie(opts.cookieName, newCookie, opts.cookieDays);
    }
    
    // returns true if we have 2 - MAX number of products ready to go, with none
    // currently waiting to load. Used to allow/disallow the "compare" button
    function isCompareAllowedToOpen() {
      if (compareCount < 2) {
        alert('Please select at least 2 items to compare');
        return false;
      } else if (compareCount > opts.maxCompareCount) {
        alert('You can compare at most '+opts.maxCompareCount+' products');
        return false;
      } else {
        var prodIDs = [];
        if ($('.mssCompareLoading .mssCompareID').length > 0) {
          alert('Please wait for all products to finish loading');
          return false;
        } else {
          return true;
        }
      }
    }
    
        
    // INITIALIZING CODE TO INSTANTIATE THIS COMPARE BOX
    $(opts.checkboxSelector).click(function(){
      var value = this.value;
      var splitVal = value.split('|');
      if (splitVal.length!=2)alert('Value of checkbox not formatted correctly: '+value);
      var prodID = splitVal[0];
      var prodName = splitVal[1];
      if (this.checked) {
        // Add a product to the list
        if (loadProduct(prodID, prodName)) {
          addProdToCookie(prodID, prodName);
        } else {
          this.checked = false;
        }
      } else {
        // This will delete a product from the list
        
        unloadProduct(prodID);  // also removes cookie
      }
    });
    
    // Build the HTML that goes into the DOM element on which this is called (the CSB) 
    $self.html("");
    if (opts.hideParentIfEmpty) $container.hide();
    if (opts.compareLinkLoc.indexOf('top') >= 0)
      $self.append('<a class="mssCompareGo" href="javascript:void(0);">'+opts.compareLinkHTML+'</a>');
    $(this).append('<div class="mssCompareReady"></div><div class="mssCompareLoading"></div>');
    if (opts.compareLinkLoc.indexOf('bottom') >= 0)
      $self.append('<a class="mssCompareGo" href="javascript:void(0);">'+opts.compareLinkHTML+'</a>');
    

    // Uncheck all mssCompare checkboxes (while nothing is loaded)
    $(opts.checkboxSelector).each(function(){
      $(this).attr('checked', false);
    });

    // Load cookie data, if appropriate
    if (opts.deleteCookie) eraseCookie(opts.cookieName);
    else loadCookie();
    
    
    $('.mssCompareGo').colorbox($.extend(opts.colorboxOptions, {
      onPreopen:isCompareAllowedToOpen,
      onLoad:$mssComparePublic.createCompareChart,
      inline:true,
      href:'#mssCompareChartHolder'
    }));
    
    
  }
  
  // DEFAULT VALUES ****************************************************
  $mssComparePublic.defaults = {
    baseHref: "",
    jsonCode: '$$ProductData$$',
    cookieName: 'mssCompare',
    maxCompareCount: 4,
    checkboxSelector: '.addCompare',
    compareLinkHTML: 'Click to Compare',
    compareLinkLoc: 'top bottom',
    hideParentIfEmpty: true,
    cookieDays: '5',
    defaultChartValue: "N/A",
    container: '#csb-holder',
    fxSpeed: 'slow',
    deleteCookie: false,
    colorboxOptions:{
      width:'700px',
      opacity:0.35
    }
  }
  
  // Function to build the comparison chart in the DOM, executing every time
  // the user clicks compare
  $mssComparePublic.createCompareChart = function() {
    // find the container for the chart. If it doesn't exist, create it
    var $chartHolder = $('#mssCompareChartHolder');
    if ($chartHolder.length==0) {
      var invis = document.createElement('div');
      invis.style.display='none';
      $('body').append(invis);
      $chartHolder = $(document.createElement('div'));
      $chartHolder.attr('id', 'mssCompareChartHolder');
      $(invis).append($chartHolder);
    }
    
    // Use the jsonData collected to make a list of prodIDs that are loaded
    var prodIDs = [];
    for (var key in $mssComparePublic.jsonData) {
      if ($mssComparePublic.jsonData[key].loaded) prodIDs.push(key);
    }
    
    
    var ctable = document.createElement('table');
      ctable.setAttribute('cellspacing', 0);
      ctable.className = 'mssCompareTable';
      var ctbody = document.createElement('tbody');
        var ctr, ctd;
        /*
        // First, create a top row container all the product Names
        ctr = document.createElement('tr');
        ctr.className = 'mssCompareColRow';
          ctd = document.createElement('td');
          ctd.className = 'mssCompareLeftCol';
          ctd.innerHTML = '&nbsp;';
          ctr.appendChild(ctd);
          // Title each column with the name field of the JSON data
          for (var i=0; i < prodIDs.length; i++) {
            ctd = document.createElement('td');
            ctd.innerHTML = $mssComparePublic.jsonData[prodIDs[i]].name;
            ctr.appendChild(ctd);
          }
        ctbody.appendChild(ctr);
        */
        
        // Now, loop through each property name. For each one, create a row
        // and display the corresponding value for that column's product. If
        // the value is 'false', display the default value from options, which
        // is $mssComparePublic.opts.defaultChartValue
        for (var i=0; i < $mssComparePublic.propNames.length; i++) { 
          ctr = document.createElement('tr');
          ctr.classname = 'mssCompareRow';
            // Create the cell holding the title of the property we
            // are comparing on this row
            ctd = document.createElement('td');
            ctd.className = 'mssCompareLeftCol';
            ctd.innerHTML = $mssComparePublic.propNames[i];
            ctr.appendChild(ctd)
            // For each of the prodIDs, create a table cell with that prodID's
            // value. If everything goes according to plan, the number of 
            // propNames will equal the length of the array of values for every
            // product, and values they don't have will simply be 'false'
            for (var j=0; j<prodIDs.length; j++) {
              var thisValue = $mssComparePublic.jsonData[prodIDs[j]].values[i];
              ctd = document.createElement('td');
              ctd.innerHTML = (thisValue===false) ? $mssComparePublic.opts.defaultChartValue : thisValue;
              ctr.appendChild(ctd);
            }                      
          ctbody.appendChild(ctr);
        }
      ctable.appendChild(ctbody);
    
    $chartHolder.html(ctable);
  }
  
  
  
})(jQuery);
    


