var cDisc = 0.0;
var cCustomerId='';

var useCroppedImageHack = false;
if(BrowserDetect.browser == "Firefox") {
  useCroppedImageHack = true;
}


Number.prototype.pts_to_mm = function() {
  var pt_to_mm = 0.352777778 ;
  var px_to_mm = 0.132291666666667 ;
  
  return this * pt_to_mm ;
} ;

Number.prototype.mm_to_pts = function() {
  var pt_to_mm = 0.352777778 ;
  var px_to_mm = 0.132291666666667 ;
  
  return this / pt_to_mm ;
} ;

Number.prototype.round = function(places) {
  if (!places) places = 2 ;
  return Math.round(this*Math.pow(10,places))/Math.pow(10,places) ;
} ;


Date.short_months = $w("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" );
Date.day_of_week = $w("Sunday Monday Tuesday Wednesday Thursday Friday Saturday" );

Date.prototype.toFormat = function(format, include_time){
  var str = "";
  if(format == 0) { // mm/dd/yyyy
    str = Date.padded2(this.getMonth() + 1) + "/" + Date.padded2(this.getDate()) + "/" + Date.padded2(this.getFullYear());
  } else if(format == 1) { // dd/mm/yyyy
    str = Date.padded2(this.getDate()) + "/" + Date.padded2(this.getMonth() + 1) + "/" + Date.padded2(this.getFullYear());
  } else if(format == 2) { // short date
    str = Date.short_months[this.getMonth()] + " " + this.getDate() + ", " + this.getFullYear();
  } else { //long date
    str = Date.day_of_week[this.getDay()] + " " + Date.months[this.getMonth()] + " " + this.getDate() + ", " + this.getFullYear();
  }
 
  
  if (include_time) { hour=this.getHours(); str += " " + this.getAMPMHour() + ":" + this.getPaddedMinutes() + " " + this.getAMPM() }
  return str;
}

var PPSizerSize = 10;
var PPSizerHalf = PPSizerSize/2;
var offsetWidgetSize = 5;
var PPRotatorSize = 10;
var PPRotatorSizeHalf = PPRotatorSize/2;

var alertLevelHeaderClasses = ["alert icon_error", "alert icon_warning", "alert icon_notice"];

function bindFunc(locals, func) {
  this.locals = locals;
  this.func = func;
  var self = this;
  return function() { self.func(self.locals); };
}

function isNumeric(sText) {
  var validChars = "0123456789.";
  var isNumber=true;
  var c;
  
  
  for (var i = 0; i < sText.length && isNumber == true; i++) { 
    c = sText.charAt(i); 
    if (validChars.indexOf(c) == -1) {
      isNumber = false;
    }
  }
  return isNumber;
}

function getPriceValue(el, max, defaultValue, min) {
  if (!defaultValue) defaultValue = 0;
  var price = parseFloat(el.value);
  if (isNaN(price)) {
    el.value = defaultValue;
    price = defaultValue;
  }
  if (!isNumeric(el.value) || price != price.round()) {
    price = price.round();
    el.value = price;
  }
  if (max != null && price > max) {
    el.value = max;
    price = max;
  }
  if (min != null && price < min) {
    el.value = min;
    price = min;
  }
  while (el.value.length > 1 && el.value.charAt(0) == '0' && el.value.charAt(1) != '.') {
    el.value = el.value.substr(1, el.value.length-1);
  }
  return price;
}

function simpleFormat(str) {
  if (str == null) return '';
  var text = '<p>' + str + '</p>';
  text = text.replace(/\n\n/g, '</p><p>');
  text = text.replace(/\n/g, '<br />');
  return text;
}

var debug = null;

function log(msg, trace) {
  try {
    console.debug(msg);
    if(trace) {
      console.trace();
    }
  } catch(e) {}
  if((debug!=null)&&(debug.style.display != 'none')) {
    debug.value = msg + "\n" + debug.value;
  }
}

function dopt(opts, name, defaultVal) {
  if(opts==null) {
    return defaultVal;
  }
  if(opts[name]==null) {
    return defaultVal;
  }
  return opts[name];
}

function dopti(opts, name, defaultVal) {
  var val = dopt(opts, name, defaultVal) ;
  return parseInt(val, 10) ;
}


function backgroundClicked(event) {
  var stopClickEvent = false;
  var doDeselect = true;
//  log("backgroundClicked");
  var el = Event.element(event);
  while(el != null) {
    if(el.nodeType==1) {
      if(el.getAttribute("stopdeselect")=="true") {
        doDeselect = false;
//        log("found stopdeselect");
      } else if(el.id=="dhtmlgoodies_colorPicker") {
        doDeselect = false;
        log("found dhtmlgoodies_colorPicker stopdeselect");
      } else if(el.getAttribute("stopdeselect")=="false") {
        doDeselect = true;
        log("found stopdeselect=false");
        break;
      }
    }
    el = el.parentNode;
  }
  if(doDeselect) {
    d.deSelectCurrentItem();
  }
  return true;
}


function startAsyncAction() {

}

function finishAsyncAction() {

}

var asyncActions = new Hash();

function asyncStart(container) {
  var containerEl = $(container);
  var containerId = getElId(containerEl);
  
  log("asyncStart:" + containerId);
  
  //there can only be one async action per container...
  if(asyncActions[containerId] != null) {
    log("Stopping existing async action " + containerId);
    asyncFinish({id:containerId, v: asyncActions[containerId].v});
  }
  
  var divB = document.createElement("DIV");
  divB.style.display = "none";
  divB.style.zIndex = 3000;
  divB.className = "async_action_background";
  
  var div = document.createElement("DIV");
  div.style.display = "none";
  div.style.zIndex = 3000;
  div.className = "async_action";
  
  div.innerHTML="<img src='/images/spinner_no_bg.gif' alt='[]' />";
  var oldPositioning = null;
  

  var dims = Element.getDimensions(container);
  var offset = Position.cumulativeOffset(containerEl);
  log(offset);
  log(dims);
  
  divB.style.position = "absolute";
  divB.style.top = offset[1] + "px";
  divB.style.left = offset[0] + "px";
  divB.style.height = dims.height + "px";
  divB.style.width = dims.width + "px";
  divB.style.display="none";
  document.body.appendChild(divB);
  divB.id = "aa_" + containerId + "_bg";
  
  div.style.position = "absolute";
  div.style.top = offset[1] + "px";
  div.style.left = offset[0] + "px";
  div.style.height = dims.height + "px";
  div.style.width = dims.width + "px";
  div.backgroundColor="#000000";
  div.style.display="none";
  document.body.appendChild(div);
  //Effect.Appear(div, { duration: 0.2 });
  
  div.id = "aa_" + containerId;
  
  $(divB).show();
  $(div).show();
  var v = getNextId();
  asyncActions[containerId] = {d:div, db:divB, c:container, p:oldPositioning, v:v};
  return {id: containerId, v:v};
}

function asyncFinish(key) {
  if(key == null) {
    return;
  }
  var containerId = key.id;
  
  var aa = asyncActions[containerId];
  if(aa == null) {
    log("asyncFinish ignored (" + containerId + " not in asyncActions)");
  } else {
    if(aa.v != key.v) {
      log("asyncFinish ignored (" + aa.v + " != " + key.v + ")");
    } else {
      delete asyncActions[containerId];
      try {
        // Check to make sure the object exists before removing it (because this is run twice and may not still exist)
        if($(aa.d.id)){
         // Effect.Fade(aa.d, { 
          //  duration: 0.2, 
          //  afterFinish: function asyncFinishAfterFinish(){document.body.removeChild(aa.d);}
         // });
         document.body.removeChild(aa.d);
        }
        if($(aa.db.id)){
          document.body.removeChild(aa.db);
        }
      } catch(e) {}
    }
  }
}




var UP_URL = "/upload-progress/upload.status";

var up_started = false;
var up_stop = false;
var up_errorCount=0;
var current_upload_id = 0;

function startTrackingUpload(id) {
  
  d.track("fileuploading");
  current_upload_id = id;
  up_started = false;
  up_stop = false;
  up_errorCount = 0;
  window.setTimeout( function() {
    if(!up_stop) {
      var el = $('upload_box_' + id);
      if(el != null) $('upload_box_' + id).style.display='none';
      $('upload_status_' + id).style.display='';
      $('upload_progress_indicator_' + id).style.width="0px";
      $('upload_status_message_' + id).innerHTML = "";
      var t1 = new Ajax.Request(d.pathPrefix + UP_URL + "?nc=" + new Date().getTime(), {asynchronous:true, evalScripts:true, method:'get'});
    }
  }, 500);
}

function stopTrackingUpload(id, keep_visible) {
  up_stop = true;
  var el = $('upload_box_' + id);
  if(el != null) $('upload_box_' + id).style.display='';
  if(keep_visible != true) {
    $('upload_status_' + id).style.display='none';
    el = $('upload_box_' + id);
  }
  if(el != null) $('upload_image_' + id).value="";
  //finishAsyncAction();
}

function up_set_status(status,status_msg,size,uploaded,speed,eta_mins_frac,eta_secs) {
  //log("setting upload status:" + status + "," + status_msg + "," + size + "," + uploaded + "," + speed + "," + eta_mins_frac + "," + eta_secs);

  //alert("setting upload status");
  //window.status='got upload status:' + status + " size=" + size;
  if(status==0) {
    up_errorCount = 0;
    up_started = true;
    var percent = (uploaded * 100) / size;
    var ind = $('upload_progress_indicator_' + current_upload_id);
    var bar = $('upload_status_bar_' + current_upload_id);
    var sm = $('upload_status_message_' + current_upload_id);
    
    var bar_width = bar.getWidth() - 2;
    if(bar_width < 0) {
      bar_width = 0;
    }
    //log("w=" + bar_width);
    //log(parseInt((bar_width * percent) / 100) + "px");
    ind.style.width = parseInt((bar_width * percent) / 100, 10) + "px";
    
    var eta_mins = parseInt(eta_secs / 60, 10);
    var eta_secs_left = eta_secs - (eta_mins * 60);
    var eta_str = eta_mins + ":" + eta_secs_left + " minutes";
    if(eta_mins==0) {
      eta_str = eta_secs_left + " seconds";
    }
    
    var size_str = "";
    var size_kb = parseInt(size/1000, 10);
    if(size_kb > 1000) {
      size_mb = parseFloat(parseInt(size_kb/10, 10)) / 100;
      size_str = size_mb + "MB";
    } else {
      size_str = size_kb + "KB";
    }
    
    var perc_str = parseFloat(parseInt(percent*100, 10)) / 100;
    if(uploaded==0) {
      sm.innerHTML = ml("UPLOAD_STARTING") + "...";
    } else {
      sm.innerHTML = ml("Uploaded %1s% of %2s at %3s KBs", [perc_str,size_str, speed ]);
    }
    if(size == uploaded) {//hmm... need this because mod_upload no longer removes the entry on end...
      stopTrackingUpload(current_upload_id, true);
    }
  } else {
    if(status==4) {
      up_errorCount++;
      if(up_errorCount > 5) {
        stopTrackingUpload(current_upload_id);
      }
    } else {
      if(up_started) {//must have finished (or error...)
        stopTrackingUpload(current_upload_id);
      }
    }
  }
  if(!up_stop) {
    
    window.setTimeout( function() {
        //window.status='sending another request for upload status';
        var t1 = new Ajax.Request(UP_URL + "?nc=" + new Date().getTime(), {asynchronous:true, evalScripts:true, method:'get'});
    }, 500);
  }
}


function fileOk(id) {
  var fname = $('upload_image_' + id ).value;
  
  var lastDot = fname.lastIndexOf(".");
  var etx = "";
  if(lastDot != null) {
    ext = fname.substring(lastDot + 1,fname.length);
    ext = ext.toLowerCase();
  }
  
  var allowed = d.allowedFileUploadImageExtensions();
  for(var i=0; i < allowed.length; i++) {
    if(allowed[i] == ext) {
      return true;
    }
  }
  alert(ml("Files of type '%1s' are not allowed.\nYou may upload files with the following extensions: %2s", [ext, allowed.join(" ")]));
  return false;
}


function f_clientWidth() {
  return f_filterResults (
    window.innerWidth ? window.innerWidth : 0,
    document.documentElement ? document.documentElement.clientWidth : 0,
    document.body ? document.body.clientWidth : 0
  );
}
function f_clientHeight() {
  return f_filterResults (
    window.innerHeight ? window.innerHeight : 0,
    document.documentElement ? document.documentElement.clientHeight : 0,
    document.body ? document.body.clientHeight : 0
  );
}
function f_scrollLeft() {
  return f_filterResults (
    window.pageXOffset ? window.pageXOffset : 0,
    document.documentElement ? document.documentElement.scrollLeft : 0,
    document.body ? document.body.scrollLeft : 0
  );
}
function f_scrollTop() {
  return f_filterResults (
    window.pageYOffset ? window.pageYOffset : 0,
    document.documentElement ? document.documentElement.scrollTop : 0,
    document.body ? document.body.scrollTop : 0
  );
}
function f_filterResults(n_win, n_docel, n_body) {
  var n_result = n_win ? n_win : 0;
  if (n_docel && (!n_result || (n_result > n_docel)))
    n_result = n_docel;
  return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}


var hiddenSelects = [];

var currentPopupId = null;
var popupStack = [];
var currentPopupTransitionEffect = null;

function popup(divId, options) {
  if(options == null) options = {};
  var div = $(divId);
  if(div==null) {
    alert("unable to get element:" + divId);
    return;
  }
  
  var bgdiv = $('popupbackground');
  if(bgdiv==null) {
    alert("unable to get element:popupbackground");
    return;
  }
  if(options.bgClass == null) {
    bgdiv.className = "popupbackground";
  } else {
    bgdiv.className = "popupbackground " + options.bgClass;
  }
  var height = f_clientHeight();
  var width = f_clientWidth();
  
  if(width < document.body.clientWidth) { width = document.body.clientWidth; }
  
  var bimg = $('body-bottom');
  var pos = Position.cumulativeOffset(bimg);
  
  body_height = pos[1] + 2;
  
  //bgdiv.className="popupbackground";
  if(currentPopupTransitionEffect != null) {
    currentPopupTransitionEffect.cancel();
  }
  
  var bgOpacity = options.bgOpacity == null ? 0.8 : options.bgOpacity;
  
  currentPopupTransitionEffect = new Effect.Appear(bgdiv, { to: bgOpacity, duration: 0.2 });
  bgdiv.style.zIndex = 3000;
  
  if (document.body.scrollHeight>document.body.offsetHeight){ 
    bgdiv.style.height=document.body.scrollHeight+"px"; 
  }else{
    bgdiv.style.height=document.body.offsetHeight+"px";
  }
  
  div.style.zIndex = 3001;
  div.style.visibility="hidden";
  div.style.display="";
  
  var popupParentOffset = 0;
  var popupParent = Position.offsetParent(div);
  if(popupParent != null) {
    var popupParentPos = Position.cumulativeOffset(popupParent);
    popupParentOffset = popupParentPos[1];
  }
  
  if(options.positionCallback == null) {
    if( ((((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop()) <10 ) {
      div.style.top = (10 - popupParentOffset) + "px";
    } else {
      div.style.top = (((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop() - popupParentOffset + "px";
    }
    
    //div.style.left = (((f_clientWidth()  / 2) - (div.clientWidth/2))) + f_scrollLeft() + "px";
    div.style.left="50%";
    div.style.marginLeft=-(div.offsetWidth/2)+"px";
  } else {
    options.positionCallback(div, popupParentOffset);
  }
  
  log("popup(" + divId + ")");
  
  if(currentPopupId != null) {
    restoreHiddenSelects();
    log("added " + currentPopupId + " to popupStack");
    popupStack.push(currentPopupId);
    $(currentPopupId).style.display="none";
  }
  currentPopupId = divId;
  storeBackgroundSelects(div);
  
  div.style.visibility="visible";
}

function storeBackgroundSelects(div) {
  var sels = document.getElementsByTagName("SELECT");
  for(var i=0;i < sels.length;i++) {
    if(!$(sels[i]).descendantOf(div)) {
      hiddenSelects.push(sels[i]);
      sels[i].style.visibility="hidden";
    }
  }
}

function restoreHiddenSelects() {
  while(hiddenSelects.size() > 0) {
    var el = hiddenSelects.pop();
    el.style.visibility = "";
  }
}


function closePopup(divId) {
  if(currentPopupId!=divId) { //may be in the stack...
    log("Closing popup that is not current: " + divId);
    var filteredPopups = [];
    for(var i=0; i < popupStack.length; i++) {
      if(popupStack[i] != divId) {
        filteredPopups.push(popupStack[i]);
      } else {
        log("Removed popup from stack:" + divId);
      }
      popupStack = filteredPopups;
    }
    return;
  }
  var div = $(divId);
  if(div==null) {
    alert("unable to get popup element");
    return;
  }
  var bgdiv = $('popupbackground');
  if(bgdiv==null) {
    alert("unable to get background element");
    return;
  }
  log("closePopup(" + divId + ")");
  
  
  
  //div.style.visibility="hidden";
  //div.style.display="none";
  //new Effect.Morph(div, {style: "margin-top: 0", duration: 0.2});
  //new Effect.toggle(div, 'appear', { duration: 0.2 });
  div.hide();
  
  restoreHiddenSelects();
  if(popupStack.length > 0) {
    currentPopupId = popupStack.pop();
    log("pulled " + currentPopupId + " from popupStack");
    $(currentPopupId).style.display="";
    storeBackgroundSelects($(currentPopupId)) ;
  } else {
    //bgdiv.className="popupclose";
    //bgdiv.hide();
    if(currentPopupTransitionEffect != null) {
      currentPopupTransitionEffect.cancel();
    }
    currentPopupTransitionEffect = new Effect.Fade(bgdiv, { duration: 0.2 });
    currentPopupId = null;
  }
}

function repositionPopup(divId) {
  var div = $(divId);
  if(div==null) {
    alert("unable to get popup element");
    return;
  }
  
  var popupParentOffset = 0;
  var popupParent = Position.offsetParent(div);
  if(popupParent != null) {
    var popupParentPos = Position.cumulativeOffset(popupParent);
    popupParentOffset = popupParentPos[1];
  }
  
  if( ((((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop()) <10 ) {
    div.style.top = (10 - popupParentOffset) + "px";
  } else {
    div.style.top = (((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop() - popupParentOffset + "px";
    //div.style.marginTop= - 150 + "px";
  }
  
  //div.style.left = (((f_clientWidth()  / 2) - (div.clientWidth/2))) + f_scrollLeft() + "px";
  
  /*------------------------------------------*/
  
  //div.style.left = (((f_clientWidth()  / 2) - (div.clientWidth/2))) + f_scrollLeft() + "px";
  div.style.left="50%";
  div.style.marginLeft=-(div.offsetWidth/2)+"px";
}

function hashSize(hash) {
  var cnt = 0;
  for(k in hash) {
    cnt++;
  }
  return cnt;
}

function hashFirstElement(hash) {
  for(k in hash) {
    return hash[k];
  }
  return null;
}

function hashFirstKey(hash) {
  for(k in hash) {
    return k;
  }
  return null;
}

function hashMerge(original, over) {
  if(over == null) return original;
  if(original == null) return over;
  for(k in over) {
    if(original[k] != null) {
      if(original[k] instanceof Function) { //do nothing..
      } else if(original[k] instanceof Object) {
        original[k] = hashMerge(original[k], over[k]);
      } else {
        original[k] = over[k];
      }
    } else {
      original[k] = over[k];
    }
  }
  return original;
}

/*
function showDesignerHelp() {
  myLightWindow.activateWindow({
      href: "/ppr/product_info/designer_help?popup", 
      title: 'Designer Help',
      width: 650,
      height: 550
    });
  d.track("show-designer-help");
}
*/
function showDesignerFaq() {
  myLightWindow.activateWindow({
      href: "/ppr/product_info/pop/tshirt_faq", 
      title: 'FAQs',
      width: 650,
      height: 550
    });
  d.track("show-designer-faq");
}

  

  
function showDesignerShipping() {
  myLightWindow.activateWindow({
      href: "/ppr/product_info/pop/shipping", 
      title: 'Shippping Information',
      width: 650,
      height: 550
    });
  d.track("show-designer-shipping");
}

function updateToplinks() {
  var t2 = new Ajax.Updater(d.ajaxUrl("toplinks_container", d.pathPrefix + "/home/toplinks"), {asynchronous:true, evalScripts:true});
}
  
function updatePageTop() {
  var t2 = new Ajax.Updater("page_top_container", d.ajaxUrl(d.pathPrefix + "/home/page_top"), {asynchronous:true, evalScripts:true, onComplete:function(response){new AdminBar();}});
}


function makePlaceholderAsset() {
  return new Asset( { id:0, url:"/ppr/images/load.gif", wUrl:"/ppr/images/load.gif", width: 50, height:50, type: 0, name: "Loading..."}, d);
}




Effect.Band = function(elementClose, elementOpen) {
  element = $(elementClose);
  element.makeClipping();
  
  elementOpen = $(elementOpen);
  var oHeight = elementOpen.getDimensions().height;
  
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
    elementOpen.undoClipping();
      },
    afterSetup: function(effect) {
      elementOpen.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      //scale the elementOpen...
        var currentScale = 1 - ((effect.options.scaleFrom/100.0) + (effect.factor * effect.position));
      var height = oHeight * currentScale;
      elementOpen.style.height = Math.round(height) + 'px';
    }
    
    }, arguments[2] || {})
  );
};


var imageEffects = [
  ["M_S",ml("Sepia Tone"), "sepia.gif",{single:true},
    [ml("Threshold"),"t","S", {r: $R(0,1), d:0.8}]
  ],
  ["M_G",ml("Greyscale"), "greyscale.gif",{single:true},
    ["PH","t","P", {d:1}]
  ],
  ["M_B",ml("Black And White"), "bandw.gif",{single:true},
    [ml("Threshold"),"t","S", {r: $R(0,1), d:0.65}]
  ],
  ["C_B",ml("Exposure"),"brightness.gif",{},
    [ml("Contrast"),"c","S", {r: $R(0,10), d:5, v:[1,2,3,4,5,6,7,8,9]}],
    [ml("Brightness"),"b","S", {r: $R(1,300), d:100}]
  ],
  ["C_H",ml("Hue"),"hue.gif",{},
    [ml("Hue"),"h", "S", {r: $R(0,100), d:100}]
  ],
  ["C_T",ml("Tint"),"tint.gif",{},
    [ml("Color"),"c","CS", {r: $R(0,255), d:125}],
    [ml("Amount"),"a","S", {r: $R(0,100), d:50}]
  ],
  ["C_L",ml("Color Balance"),"color_balance.gif",{},
    [ml("Red"),"r","S", {r: $R(0,3), d:1}],
    [ml("Green"),"g","S", {r: $R(0,3), d:1}],
    [ml("Blue"),"b","S", {r: $R(0,3), d:1}]
  ],
  ["C_R",ml("Reduce Colors"),"reduce_colors.gif",{},
    [ml("Colors"),"a","S", {r: $R(0.1,1), d:0.5}]
  ],
  ["C_S",ml("Saturation"),"saturation.gif",{},
    [ml("Amount"),"a","S", {r: $R(0,300), d:100}]
  ],
  ["A_S",ml("Sketch"),"sketch.gif",{},
    [ml("Direction"),"d","R", {d:45}],
    [ml("Strength"),"s","S", {r: $R(1,4), d:1}]
  ],
  ["A_O",ml("Oil Painting"),"oil_painting.gif",{},
    [ml("Brush Size"),"b", "S", {r: $R(1,10), d:5}]
  ],
  ["A_C",ml("Comic"),"comic.gif",{},
    [ml("Strength"),"b","S", {r: $R(1,10), d:5}]
  ],
  ["A_CH",ml("Charcoal"),"charcol.gif",{},
    [ml("Amount"),"a","S", {r: $R(0,9), d:3}]
  ],
  ["A_E",ml("Edge"),"edges.gif",{},
    [ml("Size"),"a","S", {r: $R(3,9), d:6}]
  ],
  ["A_EM",ml("Emboss"),"emboss.gif",{},
    [ml("Size"),"a","S", {r: $R(0,3), d:1}]
  ],
  ["A_SH",ml("Shade"),"shade.gif",{},
    [ml("Direction"),"d","R", {d:50}],
    [ml("Elevation"),"e","S", {r: $R(1,100), d:50}]
  ],
  ["D_H",ml("Halftone"),"halftone.gif",{},
    [ml("Strength"),"s","S", {r: $R(0,9), d:5}]
  ],
  ["D_N",ml("Newspaper"),"halftone.gif",{},
    [ml("Size"),"t","S", {r: $R(0,6),v:$R(0,6), d:2}],
    [ml("Strength"),"s","S", {r: $R(0,40),v:$R(0,40), d:5}]
  ],
  ["B_R",ml("Radial Blur"),"radial_blur.gif",{},
    [ml("Amount"),"a","S", {r: $R(1,15), d:3}]
  ],
  ["B_M",ml("Motion Blur"),"motion_blur.gif",{},
    [ml("Direction"),"d","R", {d:90}],
    [ml("Speed"),"s","S", {r: $R(1,20), d:2}]
  ],
  ["B_N",ml("Normal Blur"),"normal_blur.gif",{},
    [ml("Amount"),"a","S", {r: $R(1,20), d:3}]
  ],
  ["F_M",ml("Money"),"money.gif",{},
    [ml("Strength"),"b","S", {r: $R(1,10), d:5}]
  ],
  ["F_T",ml("Tiles"),"tiles.gif",{},
    [ml("Strength"),"b","S", {r: $R(1,10), d:5}]
  ],
  ["F_F",ml("Fade To Black"),"fade_to_black.gif",{},
    [ml("Direction"),"d", "CB", {v:[["Horizontal",0],["Vertical",1],["Top Left to Bottom Right",3],["Bottom Left to Top Right",4],["Center",2]], d:0}],
    [ml("Reverse"),"s","CH",{d:true}]
  ]
];

function ieOMO(el) {
  if(el.className == "et_effect") {
    el.className = "et_effect over";
  }
}

function ieOMOu(el) {
  if(el.className == "et_effect over") {
    el.className = "et_effect";
  }
}

function ieOMc(aId, eIdx) {
  var image = d.currentCViewArea.allItems[aId];
  image.effectClick(eIdx);
}

function ibOMc(aId, eIdx) {
  var image = d.currentCViewArea.allItems[aId];
  image.borderClick(eIdx);
}


function selectOptionHtml(options, selectedOption) {
  var opts = [];
  for(var i=0; i < options.length; i++) {
    var opt = options[i];
    var v = null;
    var c = null;
    if(opt instanceof Array) {
      c = opt[0];
      v = opt.length > 1 ? opt[1] : opt[0];
    } else if(opt instanceof Object) {
      c = opt.c;
      v = opt.v;
      var subOpts = selectOptionHtml(opt.v, selectedOption);
      opts.push('<optgroup label="' + c + '">' + subOpts + '</optgroup>');
      continue;
    } else {
      c = v = opt;
    }
    var sel = v == selectedOption ? ' selected="true"' : '';
    opts.push('<option value="' + v + '"' + sel + '>' + c + '</option>');
  }
  return opts.join("\n");
}

function $FV(elId) {
  var el = $(elId);
  if(el == null) return null;
  return el.value;
}

Number.prototype.toPrice = function() {
  s = new String(parseFloat(Math.round(this * 100)) / 100.0);
  if(s.indexOf('.') < 0) { s += '.00'; }
  if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
  
  return s;
};


var msgBoxCallback = null;
var msgboxId = null;

function msgBox(title, message, icon, yes, no, cancel, callback) {
	msgBoxCallback = callback;
	msgboxId = getNextId();
	var html="";
	html+="<div class='popup_box'><div class='popup_int'>";
	if(icon != null) { html += '<span class="popup_icon ' + icon + '" >&nbsp;</span>'; }
	html+='<h3>' + title + '</h3>';
	
	html += '<div class="popup_content"><p>' + message + '</p></div><div class="foot">';
	
	if(yes != null) {
		html += '<input type="button" value="' + yes + '" onclick="msgBoxFinish(0);" class="button go" id="d_msgbox_def_' + msgboxId + '"/> ';
	}
	if(no != null) {
		html += '<input type="button" value="' + no + '" onclick="msgBoxFinish(1);" class="button go"/> ';
	}
	if(cancel != null) {
		html += '<input type="button" value="' + cancel + '" onclick="msgBoxFinish(2);" class="button cancel"/>';
	}
	html += "</div>";
	html += "</div></div>";
	var div = document.createElement("DIV");
	div.className = "popup";
	div.style.display="none";
	div.id = "msgbox_" + msgboxId;
	div.innerHTML = html;
	
	document.body.appendChild(div);
	popup("msgbox_" + msgboxId);
	try {
	  $('d_msgbox_def_' + msgboxId).focus();
	} catch(e) {}
}

function msgBoxFinish(result) {
	closePopup("msgbox_" + msgboxId);
	var div = document.getElementById("msgbox_" + msgboxId);
	document.body.removeChild(div);
  if(msgBoxCallback != null) {
    msgBoxCallback(result);
  }
}


function _pcRebindSessionLinks(target) {
  //log("rebinding secure links for " + target);
  for(var i=0; i < document.links.length; i++) {
    var link = document.links[i];
    if(link.href.indexOf(target) == 0) {
      log("rebinding secure link " + link.href);
      Event.observe(link, "click", _pcSecureLink);
    }
  }
}

function _pcSecureLink(e) {
  log("session change link clicked");
  var el = Event.element(e);
  while(el != null && el.tagName != "A") {
    el = el.parentElement;  
  }
  if(el == null) {
    log("Unable to get link");
    return;  
  }  
  var url = _pcGetSecureLink(el.href);
  window.location = url;
  log("changed location to " + url);
  el.href = url;
  Event.stop(e);
  return false;
}

function _pcGetSecureLink(url) {
  if(url.indexOf("?") == -1) {
    return url + "?_pc_session_id=" + pcSID + "&_pc_skey=" + pcSKey;
  } else {
    return url + "&_pc_session_id=" + pcSID + "&_pc_skey=" + pcSKey;
  }
}


var FIELD_TYPE_PRODUCT_SIZE = 0;
var FIELD_TYPE_PRODUCT_COLOR = 1;
var FIELD_TYPE_LIST_DROPDOWN = 2;
var FIELD_TYPE_LIST_CHECKBOX = 3;
var FIELD_TYPE_LIST_RADIO = 4;
var FIELD_TYPE_LIST_MULTISELECT = 5;
var FIELD_TYPE_LIST_MULTISELECT_QTY = 6;
var FIELD_TYPE_TEXT_BOX=7;
var FIELD_TYPE_TEXT_AREA=8;
var FIELD_TYPE_FILE = 9;
var FIELD_TYPE_IMAGE = 10;
var FIELD_TYPE_DATE = 11;
var FIELD_TYPE_DATE_TIME = 12;
var FIELD_TYPE_TIME = 13;

var FIELD_TYPE_SYSTEM_USER_FIRST_NAME = 100;
var FIELD_TYPE_SYSTEM_USER_LAST_NAME = 101;
var FIELD_TYPE_SYSTEM_USER_ADDRESS = 102;
var FIELD_TYPE_SYSTEM_USER_CITY = 103;
var FIELD_TYPE_SYSTEM_USER_STATE = 104;
var FIELD_TYPE_SYSTEM_USER_COUNTRY = 105;
var FIELD_TYPE_SYSTEM_USER_SALUTATION = 106;
var FIELD_TYPE_SYSTEM_USER_PHONE_NUMBER = 107;
var FIELD_TYPE_SYSTEM_USER_COMPANY = 108;
var FIELD_TYPE_SYSTEM_USER_POST_CODE = 109;
var FIELD_TYPE_SYSTEM_USER_EMAIL = 110;
var FIELD_TYPE_SYSTEM_USER_AGREE_TERMS = 112;
var FIELD_TYPE_SYSTEM_USER_AGREE_REFUND = 113;

var FIELD_TYPES_OPTIONS = {}
FIELD_TYPES_OPTIONS[FIELD_TYPE_PRODUCT_SIZE]={list:true,options:true,price:true,caption:"Size", code:"ft_size", singleSelect:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_PRODUCT_COLOR] = {}
FIELD_TYPES_OPTIONS[FIELD_TYPE_LIST_DROPDOWN]={list:true,options:true,price:true,caption:ml("Drop-down"), code:"ft_dropdown", singleSelect:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_LIST_CHECKBOX]={list:true,options:true,price:true,caption:ml("Checkbox"), code:"ft_checkbox", singleSelect:false};
FIELD_TYPES_OPTIONS[FIELD_TYPE_LIST_RADIO]={list:true,options:true,price:true,caption:ml("Radio Buttons"), code:"ft_radiobutton", singleSelect:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_LIST_MULTISELECT]={list:true,options:true,price:true,caption:ml("Multiple Select"), code:"ft_multiselect", singleSelect:false};
FIELD_TYPES_OPTIONS[FIELD_TYPE_LIST_MULTISELECT_QTY]={list:true,options:true,price:true,caption:ml("Multiple Select with Qty"), code:"ft_multiselectqty", singleSelect:false};
FIELD_TYPES_OPTIONS[FIELD_TYPE_FILE]={price:true,options:true,caption:ml("File"), code:"ft_file", file:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_IMAGE]={price:true,options:true,caption:ml("Image"), code:"ft_image", file:true};

FIELD_TYPES_OPTIONS[FIELD_TYPE_TEXT_BOX]={options:true,caption:ml("Text Field"), code:"ft_textfield", text:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_TEXT_AREA]={options:true,caption:ml("Text Area"), code:"ft_textarea", text:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_DATE]={options:true,caption:ml("Date"), code:"ft_date", date:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_DATE_TIME]={options:true,caption:ml("Date Time"), code:"ft_datetime", date:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_TIME]={options:true,caption:ml("Time"), code:"ft_time", date:true};


FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_FIRST_NAME]={caption:ml("First Name"), code:"ft_fname", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_LAST_NAME]={caption:ml("Last Name"), code:"ft_lname", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_ADDRESS]={caption:ml("Address"), code:"ft_address", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_CITY]={caption:ml("City"), code:"ft_city", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_STATE]={caption:ml("State"), code:"ft_state", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_COUNTRY]={caption:ml("Country"), code:"ft_country", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_SALUTATION]={caption:ml("Salutation"), code:"ft_sal", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_PHONE_NUMBER]={caption:ml("Phone Number"), code:"ft_phnum", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_COMPANY]={caption:ml("Company"), code:"ft_company", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_POST_CODE]={caption:ml("Post Code"), code:"ft_pcode", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_EMAIL]={caption:ml("Email Address"), code:"ft_email", system: true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_AGREE_TERMS]={caption:ml("Agree to terms and conditions"), code:"ft_tandc", system: true, visibleIsRequired: true, lockedCaption:true};
FIELD_TYPES_OPTIONS[FIELD_TYPE_SYSTEM_USER_AGREE_REFUND]={caption:ml("Agree to refund policy"), code:"ft_refund", system: true, visibleIsRequired: true, lockedCaption:true};


var FIELD_TYPES_LIST = [
{ name: ml("Text"), options: [ FIELD_TYPE_TEXT_BOX,FIELD_TYPE_TEXT_AREA], allow_custom: true },
  { name: ml("Select"), options: [ FIELD_TYPE_LIST_DROPDOWN,FIELD_TYPE_LIST_CHECKBOX, FIELD_TYPE_LIST_RADIO, FIELD_TYPE_LIST_MULTISELECT], allow_custom: true},
  { name: ml("File"), options: [ FIELD_TYPE_FILE,FIELD_TYPE_IMAGE] , allow_custom: false},
  { name: ml("Date"), options: [ FIELD_TYPE_DATE,FIELD_TYPE_DATE_TIME,FIELD_TYPE_TIME] , allow_custom: false}
];

var PRICE_MODIFY_NONE = 0;
var PRICE_MODIFY_FIXED = 1;
var PRICE_MODIFY_PERCENT = 2;

var PRICE_TYPE_NONE = 0;
var PRICE_TYPE_FLAT = 1;
var PRICE_TYPE_COLOR = 2;

var DEC_PRICE_COLOR = 0;
var DEC_PRICE_FLAT = 1;
var DEC_PRICE_TABLE = 2;

var FIELD_OPTION_REQUIRED = 1;

var FIELD_FLAG_AVAILABILITY_LOCKED=1;

var SIZE_OPTIONS_LOCKED = 1;
var IS_CUSTOM_FIELD_LIST = 2;
var USE_FIELD_SCOPE = 4;
var USE_FIELD_AVAILABILITY = 8;
var NO_PRICING = 16;

var DESIGNER_MODE_SHOP = 0;
var DESIGNER_MODE_CONFIGURE = 1;
var DESIGNER_MODE_VIEW_CUSTOM_PRODUCT = 2;
var DESIGNER_MODE_AMEND = 3;



var DIG_EVENT_COLOR = 1;
var DIG_EVENT_PRIMARY = 2;
var DIG_EVENT_STARTUP = 4;

var DIG_EVENT_ALL = DIG_EVENT_COLOR + DIG_EVENT_PRIMARY + DIG_EVENT_STARTUP;

var CP_NEW = -1;
var CP_NEW_EXTRA = -2;
var CP_DECORATED_PRODUCT = 0;
var CP_CUSTOM_PRODUCT_DEF = 1;
var CP_WORKING_COPY = 2;
var CP_EMAIL_COPY = 3;
var CP_GIFT_CERTIFICATE = 10;
var CP_DIGITIZATION = 11;
var CP_EXTRA_CHARGE = 12;
var CP_NON_DEC = 20;
var CP_CREDIT = 30;

var ORDER_CACHE_COUNT = 10;

var ORDER_INTERNET = 0;
var ORDER_QUOTE = 2;
var ORDER_OMS = 3;

var QUOTE_STATUS_INCOMPLETE = 0;
var QUOTE_STATUS_DRAFT = 1;
var QUOTE_STATUS_SAVED = 2;
var QUOTE_STATUS_APPROVAL_REQUESTED = 3;
var QUOTE_STATUS_APPROVED = 4;
var QUOTE_STATUS_DENIED = 5;
var QUOTE_STATUS_CANCELLED = 6;

var QUOTE_STATUS_STRING = {
  0 : ml("In Draft"),
  1 : ml("In Draft"),
  2 : ml("Saved"),
  3 : ml("Awaiting Approval"),
  4 : ml("Approved"),
  5 : ml("Rejected"),
  6 : ml("Cancelled"),
  11 : ml("Expired")
};

var QUOTE_STATUS_CLASS = {
  0 : "draft",
  1 : "draft",
  2 : "saved",
  3 : "action",
  4 : "approved",
  5 : "denied",
  6 : "cancelled",
  11 : "expired"
};

var QUOTE_STATUS_COLOR = {
  0 : "f8f8f8",
  1 : "e2e2e2",
  2 : "69cfc3",
  3 : "dfdf9c",
  4 : "77d26d",
  5 : "db7a55",
  6 : "ff403d",
  11 : 'ff403d'
};

var ORDER_PAYMENT_STATUS_INCOMPLETE = 0;
var ORDER_PAYMENT_STATUS_DRAFT = 1;
var ORDER_PAYMENT_STATUS_SAVED = 2;
var ORDER_PAYMENT_STATUS_PAYMENT_REQUESTED = 3;
var ORDER_PAYMENT_STATUS_DEPOSIT_PAID = 4;
var ORDER_PAYMENT_STATUS_DEPOSIT_UNCONFIRMED = 10;
var ORDER_PAYMENT_STATUS_BALANCE_OUTSTANDING = 9;
var ORDER_PAYMENT_STATUS_PAYMENT_UNCONFIRMED = 8;
var ORDER_PAYMENT_STATUS_PAID_IN_FULL = 5;
var ORDER_PAYMENT_STATUS_REFUNDED = 6;
var ORDER_PAYMENT_STATUS_CANCELLED = 7;

var ORDER_PAYMENT_STATUS_STRING = {
  0 : ml("In Draft"),
  1 : ml("In Draft"),
  2 : ml("Saved"),
  3 : ml("Awaiting Payment"),
  4 : ml("In Production"),
  10: ml("Deposit unconfirmed"),
  9 : ml("Balance Outstanding"),
  8 : ml("Payment Unconfirmed"),
  5 : ml("Paid In Full"),
  6 : ml("Refunded"),
  7 : ml("Cancelled")
};

var ORDER_PAYMENT_STATUS_CLASS = {
  0 : "draft",
  1 : "draft",
  2 : "saved",
  3 : "payment",
  4 : "deposit",
  10: "deposit_unconfirmed",
  9 : "outstanding",
  8 : "unconfirmed",
  5 : "paid",
  6 : "refunded",
  7 : "cancelled"
};

var ORDER_PAYMENT_STATUS_COLOR = {
  0 : "f8f8f8",
  1 : "e2e2e2",
  2 : "69cfc3",
  3 : "dfdf9c",
  4 : "c2e572",
  10: "222222",
  9 : "d2d277",
  8 : "b668b5",
  5 : "77d26d",
  6 : "7b686a",
  7 : "ff403d"
};

var ORDER_PRODUCTION_STATUS_INCOMPLETE = 0;
var ORDER_PRODUCTION_STATUS_ON_HOLD = 7;
var ORDER_PRODUCTION_STATUS_PENDING = -1;
var ORDER_PRODUCTION_STATUS_AWAITING_PROCESSING = 1;
var ORDER_PRODUCTION_STATUS_AWAITING_SHIPPING = 2;
var ORDER_PRODUCTION_STATUS_SHIPPED = 3;
var ORDER_PRODUCTION_STATUS_CANCELLED = 4;
var ORDER_PRODUCTION_STATUS_REFUNDED = 5;
var ORDER_PRODUCTION_STATUS_DELETED = 6;

var ORDER_PRODUCTION_STATUS_STRING = {
  0 : ml("Incomplete"),
  7 : ml("On Hold"),
  1 : ml("Awaiting Processing"),
  2 : ml("Awaiting Shipping"),
  3 : ml("Shipped"),
  4 : ml("Cancelled"),
  5 : ml("Refunded"),
  6 : ml("Deleted"),
  '-1' : ml("Pending")
};

var ORDER_PRODUCTION_STATUS_CLASS = {
  0 : "incomplete",
  7 : "on_hold",
  1 : "awaiting_processing",
  2 : "awaiting_shipping",
  3 : "shipped",
  4 : "cancelled",
  5 : "refunded",
  6 : "deleted",
  '-1' : "pending"
}

var PAYMENT_METHODS = {
  '0' : ml("Credit Card"),
  '-1' : ml("PayPal Express Checkout"),
  '-2' : ml("Cash On Delivery"),
  '-3' : ml("Gift Certificate"),
  '1' : ml("External Payment Method"),
  '-4' : ml("Other")
};

var PAYMENT_STATUS_REQUESTED = 4;
var PAYMENT_STATUS_UNCONFIRMED = 0;
var PAYMENT_STATUS_PAID = 1;
var PAYMENT_STATUS_REJECTED = 5;
var PAYMENT_STATUS_REFUNDED = 2;
var PAYMENT_STATUS_CANCELLED = 3;
var PAYMENT_STATUS_INCOMPLETE = 6;
var PAYMENT_STATUS = {
  '0' : ml("Unconfirmed"),
  '1' : ml("Paid"),
  '2' : ml("Refunded"),
  '3' : ml("Cancelled"),
  '4' : ml("Requested"),
  '5' : ml("Rejected"),
  '6' : ml("Incomplete")
};

var APPROVE_ALWAYS = 0;
var APPROVE_ON_DEPOSIT = 1;
var APPROVE_ON_FULL_PAYMENT = 2;

var AMOUNT_TYPE_PERCENT = 1;
var AMOUNT_TYPE_FIXED = 2;

var INTERNAL_NOTE = 0;
var TO_CUSTOMER_NOTE = 1;
var FROM_CUSTOMER_NOTE = 2;

var NOTE_CATEGORIES = {
  '0' : ml("General")
};

var MODE_EDIT = 0;
var MODE_VIEW = 1;

var MIN_MESSAGE_TIMEOUT = 8000;
var DEFAULT_MESSAGE_TIMEOUT = 10000;
var SYNC_PERIOD = 60000;
var ROLLOVER_DELAY = 300;

var LIC_SOLO = 3;
var LIC_SOLO_PLUS = 2;
var LIC_AFF = 1;
var LIC_NONE = 0;

var OMS_STATUS_LOADING = 0;
var OMS_STATUS_LIST = 1;
var OMS_STATUS_ORDER = 2;
var OMS_STATUS_DASHBOARD = 3;
var OMS_STATUS_NEWSFEED = 4;

var OMS_STATUS_BY_CODE = {
  dashboard : OMS_STATUS_DASHBOARD,
  newsfeed : OMS_STATUS_NEWSFEED
};

var BH_LIST = {
  'order' : {
    'columns' : {
      'id' : "#",
      'customer' : ml("Customer"),
      'company' : ml("Company"),
      'email' : ml("Email"),
      'phone' : ml("Phone"),
      'po_number' : ml("Customer PO Number"),
      'status' : ml("Status"),
      'production' : ml("Production"),
      'value' : ml("Value"),
      'outstanding' : ml("Outstanding"),
      'order_date' : ml("Order Date"),
      'due_date' : ml("Due Date"),
      'po_number' : ml("Customer PO Number")
    },
    'required' : ['id', 'customer', 'status']
  },
  'quote' : {
    'columns' : {
      'id' : "#",
      'customer' : ml("Customer"),
      'company' : ml("Company"),
      'email' : ml("Email"),
      'phone' : ml("Phone"),
      'po_number' : ml("Customer PO Number"),
      'status' : ml("Status"),
      'value' : ml("Value"),
      'outstanding' : ml("Outstanding"),
      'quote_date' : ml("Quote Date"),
      'expiry_date' : ml("Expiry Date"),
      'due_date' : ml("Order Due Date")
    },
    'required' : ['id', 'customer', 'status']
  }
};


var utf8CodePages = [
[0x0000,0x007F,"Basic Latin", "Std"]
,[0x0080,0x00FF,"Latin-1 Supplement", "Std"]
,[0x0100,0x017F,"Latin Extended-A", "Std"]
,[0x0180,0x024F,"Latin Extended-B", "Std"]
,[0x0250,0x02AF,"IPA Extensions"]
,[0x02B0,0x02FF,"Spacing Modifier Letters"]
,[0x0300,0x036F,"Combining Diacritical Marks"]
,[0x0370,0x03FF,"Greek and Coptic"]
,[0x0400,0x04FF,"Cyrillic"]
,[0x0500,0x052F,"Cyrillic Supplement"]
,[0x0530,0x058F,"Armenian"]
,[0x0590,0x05FF,"Hebrew"]
,[0x0600,0x06FF,"Arabic"]
,[0x0700,0x074F,"Syriac"]
,[0x0750,0x077F,"Arabic Supplement", "Arabic"]
,[0x0780,0x07BF,"Thaana"]
,[0x07C0,0x07FF,"NKo"]
,[0x0800,0x083F,"Samaritan"]
,[0x0900,0x097F,"Devanagari"]
,[0x0980,0x09FF,"Bengali"]
,[0x0A00,0x0A7F,"Gurmukhi"]
,[0x0A80,0x0AFF,"Gujarati"]
,[0x0B00,0x0B7F,"Oriya"]
,[0x0B80,0x0BFF,"Tamil"]
,[0x0C00,0x0C7F,"Telugu"]
,[0x0C80,0x0CFF,"Kannada"]
,[0x0D00,0x0D7F,"Malayalam"]
,[0x0D80,0x0DFF,"Sinhala"]
,[0x0E00,0x0E7F,"Thai"]
,[0x0E80,0x0EFF,"Lao"]
,[0x0F00,0x0FFF,"Tibetan"]
,[0x1000,0x109F,"Myanmar"]
,[0x10A0,0x10FF,"Georgian"]
,[0x1100,0x11FF,"Hangul Jamo"]
,[0x1200,0x137F,"Ethiopic"]
,[0x1380,0x139F,"Ethiopic Supplement"]
,[0x13A0,0x13FF,"Cherokee"]
,[0x1400,0x167F,"Unified Canadian Aboriginal Syllabics"]
,[0x1680,0x169F,"Ogham"]
,[0x16A0,0x16FF,"Runic"]
,[0x1700,0x171F,"Tagalog"]
,[0x1720,0x173F,"Hanunoo"]
,[0x1740,0x175F,"Buhid"]
,[0x1760,0x177F,"Tagbanwa"]
,[0x1780,0x17FF,"Khmer"]
,[0x1800,0x18AF,"Mongolian"]
,[0x18B0,0x18FF,"Unified Canadian Aboriginal Syllabics Extended"]
,[0x1900,0x194F,"Limbu"]
,[0x1950,0x197F,"Tai Le"]
,[0x1980,0x19DF,"New Tai Lue"]
,[0x19E0,0x19FF,"Khmer Symbols"]
,[0x1A00,0x1A1F,"Buginese"]
,[0x1A20,0x1AAF,"Tai Tham"]
,[0x1B00,0x1B7F,"Balinese"]
,[0x1B80,0x1BBF,"Sundanese"]
,[0x1C00,0x1C4F,"Lepcha"]
,[0x1C50,0x1C7F,"Ol Chiki"]
,[0x1CD0,0x1CFF,"Vedic Extensions"]
,[0x1D00,0x1D7F,"Phonetic Extensions"]
,[0x1D80,0x1DBF,"Phonetic Extensions Supplement"]
,[0x1DC0,0x1DFF,"Combining Diacritical Marks Supplement"]
,[0x1E00,0x1EFF,"Latin Extended Additional", "Std"]
,[0x1F00,0x1FFF,"Greek Extended"]
,[0x2000,0x206F,"General Punctuation", "Std"]
,[0x2070,0x209F,"Superscripts and Subscripts"]
,[0x20A0,0x20CF,"Currency Symbols", "Std"]
,[0x20D0,0x20FF,"Combining Diacritical Marks for Symbols"]
,[0x2100,0x214F,"Letterlike Symbols"]
,[0x2150,0x218F,"Number Forms"]
,[0x2190,0x21FF,"Arrows"]
,[0x2200,0x22FF,"Mathematical Operators"]
,[0x2300,0x23FF,"Miscellaneous Technical"]
,[0x2400,0x243F,"Control Pictures"]
,[0x2440,0x245F,"Optical Character Recognition"]
,[0x2460,0x24FF,"Enclosed Alphanumerics"]
,[0x2500,0x257F,"Box Drawing"]
,[0x2580,0x259F,"Block Elements"]
,[0x25A0,0x25FF,"Geometric Shapes"]
,[0x2600,0x26FF,"Miscellaneous Symbols"]
,[0x2700,0x27BF,"Dingbats"]
,[0x27C0,0x27EF,"Miscellaneous Mathematical Symbols-A"]
,[0x27F0,0x27FF,"Supplemental Arrows-A"]
,[0x2800,0x28FF,"Braille Patterns"]
,[0x2900,0x297F,"Supplemental Arrows-B"]
,[0x2980,0x29FF,"Miscellaneous Mathematical Symbols-B"]
,[0x2A00,0x2AFF,"Supplemental Mathematical Operators"]
,[0x2B00,0x2BFF,"Miscellaneous Symbols and Arrows"]
,[0x2C00,0x2C5F,"Glagolitic"]
,[0x2C60,0x2C7F,"Latin Extended-C", "Std"]
,[0x2C80,0x2CFF,"Coptic"]
,[0x2D00,0x2D2F,"Georgian Supplement"]
,[0x2D30,0x2D7F,"Tifinagh"]
,[0x2D80,0x2DDF,"Ethiopic Extended"]
,[0x2DE0,0x2DFF,"Cyrillic Extended-A"]
,[0x2E00,0x2E7F,"Supplemental Punctuation"]
,[0x2E80,0x2EFF,"CJK Radicals Supplement", "CJK"]
,[0x2F00,0x2FDF,"Kangxi Radicals", "CJK"]
,[0x2FF0,0x2FFF,"Ideographic Description Characters"]
,[0x3000,0x303F,"CJK Symbols and Punctuation", "CJK"]
,[0x3040,0x309F,"Hiragana", "CJK"]
,[0x30A0,0x30FF,"Katakana", "CJK"]
,[0x3100,0x312F,"Bopomofo"]
,[0x3130,0x318F,"Hangul Compatibility Jamo"]
,[0x3190,0x319F,"Kanbun"]
,[0x31A0,0x31BF,"Bopomofo Extended"]
,[0x31C0,0x31EF,"CJK Strokes", "CJK"]
,[0x31F0,0x31FF,"Katakana Phonetic Extensions", "CJK"]
,[0x3200,0x32FF,"Enclosed CJK Letters and Months", "CJK"]
,[0x3300,0x33FF,"CJK Compatibility", "CJK"]
,[0x3400,0x4DBF,"CJK Unified Ideographs Extension A", "CJK"]
,[0x4DC0,0x4DFF,"Yijing Hexagram Symbols", "CJK"]
,[0x4E00,0x9FFF,"CJK Unified Ideographs", "CJK"]
,[0xA000,0xA48F,"Yi Syllables"]
,[0xA490,0xA4CF,"Yi Radicals"]
,[0xA4D0,0xA4FF,"Lisu"]
,[0xA500,0xA63F,"Vai"]
,[0xA640,0xA69F,"Cyrillic Extended-B"]
,[0xA6A0,0xA6FF,"Bamum"]
,[0xA700,0xA71F,"Modifier Tone Letters"]
,[0xA720,0xA7FF,"Latin Extended-D"]
,[0xA800,0xA82F,"Syloti Nagri"]
,[0xA830,0xA83F,"Common Indic Number Forms"]
,[0xA840,0xA87F,"Phags-pa"]
,[0xA880,0xA8DF,"Saurashtra"]
,[0xA8E0,0xA8FF,"Devanagari Extended"]
,[0xA900,0xA92F,"Kayah Li"]
,[0xA930,0xA95F,"Rejang"]
,[0xA960,0xA97F,"Hangul Jamo Extended-A"]
,[0xA980,0xA9DF,"Javanese"]
,[0xAA00,0xAA5F,"Cham"]
,[0xAA60,0xAA7F,"Myanmar Extended-A"]
,[0xAA80,0xAADF,"Tai Viet"]
,[0xABC0,0xABFF,"Meetei Mayek"]
,[0xAC00,0xD7AF,"Hangul Syllables"]
,[0xD7B0,0xD7FF,"Hangul Jamo Extended-B"]
,[0xD800,0xDB7F,"High Surrogates"]
,[0xDB80,0xDBFF,"High Private Use Surrogates"]
,[0xDC00,0xDFFF,"Low Surrogates"]
,[0xE000,0xF8FF,"Private Use Area"]
,[0xF900,0xFAFF,"CJK Compatibility Ideographs", "CJK"]
,[0xFB00,0xFB4F,"Alphabetic Presentation Forms"]
,[0xFB50,0xFDFF,"Arabic Presentation Forms-A", "Arabic"]
,[0xFE00,0xFE0F,"Variation Selectors"]
,[0xFE10,0xFE1F,"Vertical Forms"]
,[0xFE20,0xFE2F,"Combining Half Marks"]
,[0xFE30,0xFE4F,"CJK Compatibility Forms", "CJK"]
,[0xFE50,0xFE6F,"Small Form Variants"]
,[0xFE70,0xFEFF,"Arabic Presentation Forms-B", "Arabic"]
,[0xFF00,0xFFEF,"Halfwidth and Fullwidth Forms"]
,[0xFFF0,0xFFFF,"Specials"]
,[0x10000,0x1007F,"Linear B Syllabary"]
,[0x10080,0x100FF,"Linear B Ideograms"]
,[0x10100,0x1013F,"Aegean Numbers"]
,[0x10140,0x1018F,"Ancient Greek Numbers"]
,[0x10190,0x101CF,"Ancient Symbols"]
,[0x101D0,0x101FF,"Phaistos Disc"]
,[0x10280,0x1029F,"Lycian"]
,[0x102A0,0x102DF,"Carian"]
,[0x10300,0x1032F,"Old Italic"]
,[0x10330,0x1034F,"Gothic"]
,[0x10380,0x1039F,"Ugaritic"]
,[0x103A0,0x103DF,"Old Persian"]
,[0x10400,0x1044F,"Deseret"]
,[0x10450,0x1047F,"Shavian"]
,[0x10480,0x104AF,"Osmanya"]
,[0x10800,0x1083F,"Cypriot Syllabary"]
,[0x10840,0x1085F,"Imperial Aramaic"]
,[0x10900,0x1091F,"Phoenician"]
,[0x10920,0x1093F,"Lydian"]
,[0x10A00,0x10A5F,"Kharoshthi"]
,[0x10A60,0x10A7F,"Old South Arabian"]
,[0x10B00,0x10B3F,"Avestan"]
,[0x10B40,0x10B5F,"Inscriptional Parthian"]
,[0x10B60,0x10B7F,"Inscriptional Pahlavi"]
,[0x10C00,0x10C4F,"Old Turkic"]
,[0x10E60,0x10E7F,"Rumi Numeral Symbols"]
,[0x11080,0x110CF,"Kaithi"]
,[0x12000,0x123FF,"Cuneiform"]
,[0x12400,0x1247F,"Cuneiform Numbers and Punctuation"]
,[0x13000,0x1342F,"Egyptian Hieroglyphs"]
,[0x1D000,0x1D0FF,"Byzantine Musical Symbols"]
,[0x1D100,0x1D1FF,"Musical Symbols"]
,[0x1D200,0x1D24F,"Ancient Greek Musical Notation"]
,[0x1D300,0x1D35F,"Tai Xuan Jing Symbols"]
,[0x1D360,0x1D37F,"Counting Rod Numerals"]
,[0x1D400,0x1D7FF,"Mathematical Alphanumeric Symbols"]
,[0x1F000,0x1F02F,"Mahjong Tiles"]
,[0x1F030,0x1F09F,"Domino Tiles"]
,[0x1F100,0x1F1FF,"Enclosed Alphanumeric Supplement"]
,[0x1F200,0x1F2FF,"Enclosed Ideographic Supplement"]
,[0x20000,0x2A6DF,"CJK Unified Ideographs Extension B", "CJK"]
,[0x2A700,0x2B73F,"CJK Unified Ideographs Extension C", "CJK"]
,[0x2F800,0x2FA1F,"CJK Compatibility Ideographs Supplement", "CJK"]
,[0xE0000,0xE007F,"Tags"]
,[0xE0100,0xE01EF,"Variation Selectors Supplement"]
,[0xF0000,0xFFFFF,"Supplementary Private Use Area-A"]
,[0x100000,0x10FFFF,"Supplementary Private Use Area-B"]
]

var Designer = Class.create({
  CLASSDEF: {
      name: 'Designer'
  },
  
  initialize: function D_initialize(pathPrefix, designMode, inPopup, curGlyph, curCode, priceRounding, price99, useAnalytics, cMod, tMod, declibMarkup, lengthUnit, priceFormat, commissionRate, appLicMode) {
    this.selected_tabs = {"d":"layout","m":"customize","p":"mens", "i":"user"};
    this.cart = new Cart();
    this.decorationLibraryManager = new DecorationLibraryManager(this);
    this.teamNameTemplates = new TeamNameTemplates();
    this.chargeDecorationLibraries = {};
    this.assets = {};
    this.designerOptions = new DesignerOptions(DFLAG_DEFAULTS);
    this.appLicMode = appLicMode;
    this.debugAjax = 0;
    
    this.currentCProduct = null;
    this.productTypes = new Hash();
    this.currentProductType = null;
    this.nextId = 1;
    this.nextItemId = 1;
    this.pathPrefix = pathPrefix;
    this.sizer = null;
    this.rotator = null;
    this.productsById = new Hash();
    this.inPopup = inPopup;
    this.extraExternalParams = "";
    this.hideQualityWarning = false;
    this.useAnalytics = useAnalytics;
    this.showGrid = true;
    this.autoCheckout = true;
    this.extraCallbackParams = "";
    
    this.defaultMarkupType = 0;
    this.defaultMarkupAmount = 20;
    
    this.discounts = {};
    this.discountNames = {};
    this.defaultDiscountId = null;
    this.affiliateDiscounting = true;
    this.declibMarkup = declibMarkup;
    this.commissionRate = commissionRate;
    this.aMarkupRate = commissionRate / (100.0 - commissionRate);
    
    this.lengthUnit = lengthUnit;
    
    
    this.priceRounding = priceRounding;
    this.price99 = price99;
    this.priceFormat = priceFormat;
    
    this.imagePopupLoaded = false;
    
    this.defaultQtyDisabled = false;
    if(designMode=="shop") {
      this.mode = DESIGNER_MODE_SHOP;
    } else if(designMode=="configure") {
      this.mode = DESIGNER_MODE_CONFIGURE;
     } else if(designMode=="configure_new") {
      this.mode = DESIGNER_MODE_CONFIGURE;
      this.configuringNewProduct = true;
    } else if(designMode=="amend") {
      this.mode = DESIGNER_MODE_AMEND; 
      this.defaultQtyDisabled = true;
      
    } else {
      this.mode = DESIGNER_MODE_VIEW_CUSTOM_PRODUCT;
    }
    
    this.currentCanvasType = 0; //LAYOUT
    this.zoom = 1; //400x400 (LAYOUT)
    
    var re = new RegExp("\\[PATH_PREFIX\\]", "g");
  
    types[0] = types[0].replace(re,pathPrefix);
    types[1] = types[1].replace(re,pathPrefix);
    
    type_min = type_min.replace(re,pathPrefix);
    
    this.layoutViewUrlCID = new RegExp("\\[CID\\]", "g");
    this.layoutViewUrlS = new RegExp("\\[S\\]", "g");
    this.layoutViewUrlSc = new RegExp("\\[SC\\]", "g");
    
    this.allowedItemTypes = null;
    
    if (typeof(orderManager) == "undefined") {
      Ajax.Responders.register({
        onException: function D_onException(aj, ex) {
          try {
            log("Exception Occured\nError name: " + ex.name + "\nError message: " + ex.message, true);
            alert("Exception Occured\nError name: " + ex.name + "\nError message: " + ex.message);
            log(ex); //this was commented out.. why?
          } catch(e) {}
          throw ex;
        }
      });
    }
    
    
    
    this.onKeyUpEvent = this.onKeyUp.bindAsEventListener(this);
    Event.observe(document, "keyup", this.onKeyUpEvent);
    
    Event.observe(document.body, "mousedown", backgroundClicked);
    
    this.onQtyDDBgEvent = this.onQtyDDBg.bindAsEventListener(this);
    
    this.onCopyBgEvent = this.onCopyBg.bindAsEventListener(this);
    
    this.showEffectPreview = true;
    
    this.effectIconsLoaded = false;
    this.borderIconsLoaded = false;
    this.preloadEffectIndex = -1;
    this.preloadBorderIndex = -1;
    
    //currency information
    this.curGlyph = (curGlyph==null) ? "$" : curGlyph; 
    this.curCode = (curCode==null) ? "USD" : curCode;
    //currency modifier
    if(cMod==null) {
      this.cMod = 1;
    } else {
      this.cMod = cMod;
    }
    //tax modifier
    if(tMod==null) {
      this.tMod = 1;
    } else {
      this.tMod = tMod;
    }
    
    
    if(pwCurCur!=null) {
      this.curGlyph = pwCurCur[2];
      this.curCode = pwCurCur[1];
      this.cMod = pwCurModifier;
      var self = this;
      pwCurRegisterArea(0, null, null, null, null, null, function(pwCurCur, pwCurBCur, pwCurModifier) {
          self.curGlyph = pwCurCur[2];
          self.curCode = pwCurCur[1];
          self.cMod = pwCurModifier;
          //redisplay prices here..
          self.cart.updateCartPrice(true);
          self.currentCProduct.updateCartPrice(false);
          self.currentProductType.updatePrice();
          d.cart.products.each(function(product){
            for (var viewId in product.views) {
              for (var areaId in product.views[viewId].areas) {
                for (var itemId in product.views[viewId].areas[areaId].allItems) {
                  var item = product.views[viewId].areas[areaId].allItems[itemId];
                  if(item.updateDigitizationNotice) item.updateDigitizationNotice(true, true);
                }
              }
            }
          });
      });
    } else {
      log("pwCurCur is null");
    }
    
    
    this.defaultProcess = null;
    this.defaultProcessId = null;
    
    this.priceTables = {};
    if(priceTableData != null) {
      for(var i=0; i < priceTableData.length; i++) {
        var pt = new PriceTable(priceTableData[i]);
        this.priceTables[pt.id] = pt;
      }
    }
    
    this.customProducts = {};
    
    this.contentTopEl = $("top_of_content");
    this.translateParams = null;
    this.useLegacyPrices = false;
    this.legacyPriceChanges = {};
    this.inOrderManager = false;
    this.doPriceRounding = true;
  },
  
  initProductTypeConfiguration: function D_initProductTypeConfiguration() {
    var html;
    
    // generate product type selector
    var productTypeSelector = $("product_type_selector");
    if (!$("sel_prod_type") && productTypeSelector) {
      html = '<select onchange="d.changeProductType(this);" id="sel_prod_type">';
      
      var self = this;
      $H(this.productTypes).each(function(pair){
          if(!pair.value.hidden) {
            var ptid = pair.key;
            
            var selHtml = "";
            if (!self.currentProductType) {
              self.currentProductType = self.productTypes[pair.key];
              selHtml = 'selected="selected"';
            }
            html += '<option '+ selHtml +' value="' + pair.key + '">' + pair.value.name + '</option>';
          }
      });
      
      html += '</select>';
      
      new Insertion.Bottom(productTypeSelector, html);
    }
    
    // generate product category containers
    var productCategoryContainer = $("product_category_container");
    if (productCategoryContainer) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        if (productType.cats && productType.cats.length > 0) {
          var styleHtml = 'style="display: none;"';
          if (self.currentProductType == productType) styleHtml = '';
          html = '<span id="category_selector_' + pair.key + '" ' + styleHtml + '>' +
                    '<label>' + ml("Select Category") + '</label>' +
                    '<select id="ddpc_' + pair.key + '">' +
                      '<option selected="selected" value="0">' + ml("All") + '</option>';
          productType.cats.each(function(cat) {
            html += '<option value="' + cat.id + '">' + cat.name + '</option>';
          });
          html += '</select></span>';
          new Insertion.Bottom(productCategoryContainer, html);
        }
      });
    }
    
    var productConfig = $("product_config");
    if (productConfig) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        var styleHtml = 'style="display: none;"';
        if (self.currentProductType == productType) styleHtml = '';
        html = '<div id="pt_' + pair.key + '" ' + styleHtml + '>' +
        '<div id="product_list_' + pair.key + '" class="product_list">' +
        '</div></div>';
        new Insertion.Bottom(productConfig, html);
      });
    }
    
    var typeColorsContainer = $("type_colors_container");
    if (typeColorsContainer) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        var style = ' style="display: none;"';
        if (productType == d.currentProductType) style = '';
        html = '<ul id="pt_col_' + pair.key + '"' + style + '></ul>';
        new Insertion.Bottom(typeColorsContainer, html);
      });
    }
    
    var qtyLi = $("qty_li");
    if (qtyLi) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        html = '<li id="product_type_data_' + pair.key + '">';
        if (productType.sizeField) {
          html += 
          '<div class="sizing" id="pt_fc_' + productType.sizeField.id + '" style="display:none;">' +
            '<a href="#" onclick="d.showProductInfo(); return false;" class="size_chart">' + ml('size chart') + '</a>' +
            '<label>' +
              ml('Product Sizing') + '<br />' +
            '</label>' +
            '<div id="pt_foc_' + productType.sizeField.id + '" class="size_input"></div>' +
          '</div>';
        }
        html += '</li>';
        new Insertion.Before(qtyLi, html);
      });
    }
    
    var cfContainer = $("product_type_cf_container");
    if (cfContainer) {
      cfContainer.hide();
      
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        html = '<li id="product_type_cf_' + pair.key + '" style="display: none;">';
        
        if (productType.customFields.list.length > 0) {
          html += '<div class="custom_fields">';
          for(var i=0;i< productType.customFields.list.length;i++) {
            var field = productType.customFields.list[i];
            html +=
            '<div class="custom_field" id="pt_fc_' + field.id + '" style="display:none;">' +
              '<label>' +
                '<span class="help" id="cf_lbl_' + field.id + '">' + field.name + '</span>' +
                '<b class="tip" id="cf_tip_' + field.id + '">' +
                  '<b>' + field.description + '</b>' +
                '</b>' +
              '</label>' +
              '<div id="pt_foc_' + field.id + '" class="custom_option"></div>' +
            '</div>';
          }
          html += '</div>';
        }
        html += '</li>';
        new Insertion.Bottom(cfContainer, html);
      });
    }
    
    $H(this.productTypes).each(function(pair){
        if (pair.value != d.currentProductType) {
          pair.value.bindElements();
          pair.value.selectCategory(0);
          pair.value.deselect();
        }
    });
    this.currentProductType.selectCategory(0);
  },
  
  setDesignerOptions: function D_setDesignerOptions(flags) {
    this.designerOptions = new DesignerOptions(flags);
  },
  
  setDesignElements: function D_setDesignElements(sizer, designPane) {
    this.sizer = sizer;
    this.designPane = designPane;
    this.rotator = $("ts_rot");
    if(this.rotator == null) this.rotator = this.sizer;
    
    this.roSizers = new Hash();
    
    this.roSizers["TR"] = $("ts_tr");
    this.roSizers["TL"] = $("ts_tl");
    this.roSizers["BR"] = $("ts_br");
    this.roSizers["BL"] = $("ts_bl");
    
   
  },
  
  bindElements: function D_bindElements() {
    this.saveContainer = $("save_div");
    if($('copy_options') != null) {
      if(!this.designerOptions.showCopyPaste() && !this.designerOptions.showCopyItem()) { //no c&p available
        $('copy_options').hide();
        $('paste_options').hide();
      } else if(!this.designerOptions.showCopyPaste()) {
        $('copy_area_button').hide();
      } else if(!this.designerOptions.showCopyItem()) {
        $('copy_item_button').hide();
      }
    }
    if($('zoom_options') != null) {
       if(!this.designerOptions.showZoom()) {
         $('zoom_options').hide();
       }
    }
  },
  
  setManagePaneContainer: function D_setManagePaneContainer(el) {
    this.managePaneContainer = el;
  },
  
  setDefaultProcess: function D_setDefaultProcess(processId) {
    this.userSelectedProcessId = processId; 
    this.defaultProcess = processes.byId[processId];
    this.defaultProcessId = processId;
  },
  
  setDebugAjax: function D_setDebugAjax(doDebug) {
    this.debugAjax = doDebug;
  },
  
  ajaxUrl: function D_ajaxUrl(url) {
    if(this.translateParams != null) {
      url = addUrlParam(url, "translate=" + this.translateParams);
    }
    if(this.debugAjax != 0) {
      return addUrlParam(url, "email_request_log=" + this.debugAjax);
    } else {
      return url;  
    }
  },
  
  addChargeDecorationLibrary: function D_addChargeDecorationLibrary(id, range1price, range2price, range3price, range4price, range5price, range6price, range7price) {
    var declib = new ChargeDecorationLibrary(range1price, range2price, range3price, range4price, range5price, range6price, range7price);
    this.chargeDecorationLibraries[id] = declib;
    return declib;
  },
  
  addProductType: function D_addProductType(id, options) {
    var pt = new ProductType(id, options);
    this.productTypes[pt.id] = pt;
    return pt;
  },
  
  addProduct: function D_addProduct(product) {
    log("Adding product " + product.id + " to cache");
    this.productsById[product.id] = product;
  },
  
  addCustomProduct: function D_addCustomProduct(id, options) {
    var cp = new CustomProduct(id, options);
    this.customProducts[cp.id] = cp;
  },
  
  getProduct: function D_getProduct(productId, callback, asyncContainer) {
    if(this.productsById[productId]!=null) {
      if(callback!=null) {
        callback(this.productsById[productId]);
      }
      return this.productsById[productId];
    }
    if(callback!=null) {
      var self = this;
      this.getData({pid:productId}, function(transport) {
        log("getProduct callback");
        if(self.productsById[productId]!=null) {
          if(callback!=null) {
            callback(self.productsById[productId]);
          }
        } else {
          log(self);
          alert("Unable to load product " + productId);
        }
      }, asyncContainer);
    }
    return null;
  },
  
  getData: function D_getData(params, callback, asyncContainer) {
    var p = "m=" + this.mode + "&";
    for(k in params) {
      p += k + "=" + params[k] + "&";
    }
    var asyncKey = null;
    if(asyncContainer) {
      asyncKey = asyncStart(asyncContainer);
    }
    var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/js/getdata.js?" + p), {asynchronous:true, evalScripts:true, onComplete: function D_onComplete(response) {
        if(asyncKey!=null) {
          asyncFinish(asyncKey);
        }
        callback(response);
      }
     });
  },
  
  addAsset: function D_addAsset(options) {
    var asset = new Asset(options, this);
    this.assets[asset.id] = asset;
    return asset;
  },
  
  addPriceToUpdate: function D_addPriceToUpdate(el_id, pp_id) {
    if(!d.pricesToUpdate) d.pricesToUpdate = [] ;
    d.pricesToUpdate.push({id: pp_id, ele: el_id+pp_id}) ;
  },
  
  selectCurrentProductType: function D_selectCurrentProductType(id, doUpdate) {
    this.currentProductType = this.productTypes[id];
  },
  
  getProductType: function D_getProductType(id, callback) {
    if(this.productTypes[id] != null) {
      callback(this.productTypes[id]);
      return;
    }
    var self = this;
    this.getData({ptid:id, pid:-1}, function() {
      callback(self.productTypes[id]);
    }, "m_apparel_pane");
  },
  
  //called from the cart
  selectConfiguredProduct: function D_selectConfiguredProduct(id, doUpdate, doSelect) {
    log("d.selectConfiguredProduct()");
    //check if the current item is dirty....
    if((typeof(dontWarnBeforeLeaving) != 'undefined')&&(dontWarnBeforeLeaving == false)) { //current item is dirty...
      msgBox(ml("Save Changes?"), ml("The currently loaded item has unsaved changes. Do you want to save the item before switching product?"), null, ml("Yes"), ml("No"), ml("Cancel"), function(result) {
          if(result == 0) { //yes
            d.currentCProduct.save(false, false, function() {
                d.selectConfiguredProduct(id, doUpdate, doSelect);
            });
          } else if(result == 1) { //no
            dontWarnBeforeLeaving = true;
            if(d.currentCProduct.addedToCart) {
              d.currentCProduct.rollBack();
            }
            d.selectConfiguredProduct(id, doUpdate, doSelect);
          } else {
            //cancel...
          }
      });
      return;
    }

    this.currentCProduct = this.cart.getProduct(id);
    log(id);
    if(this.currentCProduct.product.type.id != this.currentProductType.id) {
      log("changing product type");
      this.currentProductType.deselect();
      this.currentProductType = this.currentCProduct.product.type;
      this.currentProductType.select( function() {
        d.currentProductType.selectConfiguredProduct(d.currentCProduct, doUpdate, doSelect);
      }, d.currentCProduct.product.categoryId, this.currentCProduct.product.id);
    } else {
      log("select configured product for type.");
      this.currentProductType.selectConfiguredProduct(this.currentCProduct, doUpdate, doSelect);
    }
    this.cart.selectCartItem(this.currentCProduct);
    this.currentCProduct.setAlertIcons();
  },
  
  updateConfiguredProductId: function D_updateCurrentConfiguredProductId(oldId, newId) {
    log("updateConfiguredProductId from " + oldId + " to " + newId);
    var cp = this.cart.productsById[oldId];
    cp.id = newId;
    this.cart.productsById.remove(oldId);
    this.cart.productsById[newId] = cp;
    if(this.currentCProduct == cp) {
      log("updateConfiguredProductId: this.currentCProduct == cp");
      //need to regen the fields...
      this.currentProductType.setFields(this.currentCProduct);
    }
    this.cart.products.each(function(p) {
        if (p.cp_type == CP_EXTRA_CHARGE) {
          var index = p.associatedItems.indexOf(oldId);
          if (index != -1) {
            p.associatedItems[index] = newId;
          }
        }
    });
  },
  
  updateCancelState: function D_updateCancelState(id, options) {
    var prod = this.cart.getProduct(id);
    if(prod == null) { //dynamically added derived object... (digitization)
      prod = new ConfiguredProduct(id, options);
			this.cart.add(prod);
    } else {
      prod.options = options;
    }
  },
  
  // Find the first view/area that has the selected
  initViews: function D_initViews(defaultView){
    //log("initViews:");
    //init the base price to include the first area price that can use the selected process
    for(var i =0; i < this.currentCProduct.product.views.list.size(); i++) {
      var pView = this.currentCProduct.product.views.list[i];
      log("-View:"); log(pView) ;
      for(var j=0; j < pView.areas.list.size(); j++) {
        var pArea = pView.areas.list[j] ;
        log("--Area: "); log(pArea);
        for(var k=0; k < pArea.processes.list.size(); k++) {
          var allowed_p = pArea.processes.list[k];
          if (allowed_p.id == d.userSelectedProcessId) {
            d.selectCurrentView(pView.id, pArea.id) ;
            return;
          }
        }
      }
    }
    
  },
  
  //load the product view 
  selectCurrentView: function D_selectCurrentView(id, areaId) {
    log("selectCurrentView()");
    this.viewToggle(id); //make it appear selected
    if(this.currentCView != null) {
      this.currentCView.hide();
    }
    //this.currentView = this.currentProduct.views.byId[id];
    this.currentCView = this.currentCProduct.getView(id, true);
    //this.currentCView.setDesignerBackground(); show() will do this
    
    if(this.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) { //lets also set the area and load the items used in that area
      
      if(areaId == null) { //no area passed... get the first used area.. if none used get the first area...
        areaId = this.currentCView.getFirstAreaId();
        if(areaId==null) {
          log("selectCurrentView: ERROR: no first area in view");
        }
      } else {
        log("selectCurrentView: using passed areaId:" + areaId);
      }
      this.currentCView.show();
      
      
      this.selectCurrentArea(areaId, false);
      this.currentCView.setBgPosition();
    } else {
      this.currentCView.show();
      this.currentCView.checkBgImage();
    }
  },
  
  selectCurrentArea: function D_selectCurrentArea(areaId, allowTransition, fromMouseClick) {
    this.currentCViewArea = this.currentCView.getArea(areaId, true);
    log("d.selectCurrentArea(" + this.currentCViewArea.productArea.name + ")");
    var rb = $("a_sel_" + areaId);
    if(rb != null) {
      rb.checked = true;
    }
    this.currentCViewArea.show(allowTransition);
    if(this.currentCViewArea.canZoom()) {
      if(fromMouseClick && this.currentCanvasType == 0 && this.designerOptions.clickAreaZooms()) {
        this.setZoomEnabled(this.currentCViewArea, true, false);
        d.selectTab('d','design', function(tab) { return d.selectDesignTab(tab, true); });
      } else {
        this.setZoomEnabled(this.currentCViewArea, true, (this.currentCanvasType == 1));
      }
    } else { //if(this.currentCanvasType == 1) {
      this.setZoomEnabled(this.currentCViewArea, false, true);
    }
    this.checkCopyPasteState();
  },
  
  checkAreaHighlightPosition: function D_checkAreaHighlightPosition(viewId) {
    if(this.currentCView != null && this.currentCView.id == viewId) {
      log("checkAreaHighlightPosition(" + viewId + ");");
      this.currentCView.positionAreaHighlight();
    } else if(this.currentCView == null) {
      log("checkAreaHighlightPosition: currentCView is null");
    } else {
      log("checkAreaHighlightPosition: currentCView.id (" + this.currentCView.id + ") <> " + viewId);
    }
  },
  
  selectCurrentProcess: function D_selectCurrentProcess(processId) {
   log("depricated call to selectCurrentProcess", true);
  },
  
  /*
  when viewing a product outside the designer
  */
  initViewMode: function D_initViewMode() {
    //this.currentProductType.selectConfiguredProduct(this.currentProduct, true, false);
  },
  
  //check if in ammend mode and trying to do something that could modify the price...
  canAddAsset: function D_canModifyDesign(processId) {
    if(this.mode == DESIGNER_MODE_AMEND) {
      var cpvap = this.currentCViewArea.processes[processId];
      if(cpvap.items.list.length == 0) { //about to add
        return false;
      }
      return cpvap.productProcess.canModifyDesign();
    }
    return true;
  },
  
  addImage: function D_addImage(processId, options) {
    if(options == null) options = {};
    if(processId == null) {
      //select process
      processId = this.showSelectProcess("image", options.useDefaultProcess);
      if(processId == null) {
        return;
      }
    }
    
    if(!this.canAddAsset(processId)) {
      alert(ml("You cannot add an image to this area because it would change the price of your order"));
      return;
    }
    this.newProcessId = processId;
    
    var om = this.inOrderManager ? '&om=1' : '';
    
    this.decorationLibraryManager.selectImage(this.currentCViewArea.productArea.processes.byId[processId], function(image) {
        var newAsset = d.addAsset(image.assetOptions());
        if((this.mode == DESIGNER_MODE_AMEND) && (newAsset.hasCost())) {
          if(newAsset.imageType == 2) {
            alert(ml("You cannot add this embroidery design because it would change the price of your order."));
          } else {
            alert(ml("You cannot add this image design because it would change the price of your order."));
          }
          return;
        }
        
        
        var opts = {ar:"1"};
        if(newAsset.imageType == 2) {
          opts.emb="1";
          opts.stc = newAsset.stitchCount;
          opts.cw = newAsset.colorWays.length > 0 ? 0 : null;
        } 
        if(options.toReplace != null) {
          opts.t = options.toReplace.top;
          opts.l = options.toReplace.left;
          opts.z = options.toReplace.zIndex;
          if(newAsset.imageType != 2) { //cant set emb size...
            opts.w = options.toReplace.width;
            opts.h = options.toReplace.height;
          }
        }
        var process = processes.byId[this.newProcessId];
        if(newAsset.imageType != 2 && process.isWilcomEMB() && newAsset.digitizedProcessId != process.id) { //we are adding a non emb to a wilcom area... make sure its ready for it...
          log("Adding undigited image to embroidery area");
          if(process.autoSplit) { //fulfillment house setup to auto reduce/split colors 
            opts.cc = process.maxColors;
            opts.spc = "1" ;
            popup("split_colors_status");
            var self = this;
            var ajax = new Ajax.Request(d.ajaxUrl("/shared/library/digitize_asset/" + newAsset.id + "?color_count=" + process.maxColors + "&process_id=" + process.id + om + "&uid=" + cCustomerId), {asynchronous:true, evalScripts:true, 
                onSuccess: function D_onSuccess(transport) {
                  var result = transport.responseText.evalJSON();
                  log(result);
                  newAsset = d.addAsset(result);
                  var existingColors = newAsset.getMetaData(["split_colors", process.maxColors], null);
                  //var merged = hashMerge(existing, result);
                  //newAsset.setMetaData(["split_colors", process.maxColors], merged);
                  log(newAsset);
                  //init the items colors
                  opts.c = existingColors.process_colors[self.newProcessId].join(":");
                  
                  if(options.toReplace != null) {
                    options.toReplace.del(); 
                  }
                  self.addNewItemByAsset(self.newProcessId, newAsset, opts);
                  
                  self.decorationLibraryManager.getLibrary(self.newProcessId, function(decLib) {
                      decLib.myDigitizedDesigns.toggleVisible(true);
                      if(decLib.myDigitizedDesigns.selected) {
                        decLib.myDigitizedDesigns.refreshImages(true);
                      } else {
                        decLib.myDigitizedDesigns.select();
                      }
                  });
                },
                onFailure: function D_onFailure(transport) {
                  alert("An Error Occured:" + transport.responseText);
                },
              //  onException: function D_onException() {
            //      alert("Exception occured");
              //  },
                onComplete: function D_onComplete() {
                  closePopup("split_colors_status");
                }
            });
            
            /*
            this.image.getPercentOpaque(function(percent) {
                this.selectImageCallback(this.image);
                this.close();
            }.bind(this));
            */
            return;
          } else {
            
            popup("copy_digitize_image");
            var self = this;
            
            
            var ajax = new Ajax.Request(d.ajaxUrl("/shared/library/make_digitize_copy/" + newAsset.id + "?process_id=" + process.id + om + "&uid=" + cCustomerId), {asynchronous:true, evalScripts:true, 
                onSuccess: function(transport) {
                  var result = transport.responseText.evalJSON();
                  log(result);
                  newAsset = d.addAsset(result);
                  newAsset.getOpacity(null, function(percent) {
                      closePopup("copy_digitize_image");
                      newAsset.setMetaData("percent_opaque", percent);
                      opts.po = percent;
                      if(options.toReplace != null) {
                        options.toReplace.del(); 
                      }
                      self.addNewItemByAsset(self.newProcessId, newAsset, opts);
                      self.decorationLibraryManager.getLibrary(self.newProcessId, function(decLib) {
                          decLib.myDigitizedDesigns.toggleVisible(true);
                          if(decLib.myDigitizedDesigns.selected) {
                            decLib.myDigitizedDesigns.refreshImages(true);
                          } else {
                            decLib.myDigitizedDesigns.select();
                          }
                      });
                  });
                },
                onFailure: function(transport) {
                  closePopup("copy_digitize_image");
                  alert("An Error Occured:" + transport.responseText);
                }
            });
            return;
          }
        } else if(newAsset.imageType != 2 && process.isWilcomEMB()) { //its a digitized asset..          
          if(process.autoSplit) { //fulfillment house setup to auto reduce/split colors 
            var existingColors = newAsset.getMetaData(["split_colors", process.maxColors], null); //get the existing colors...
            if(existingColors != null) { 
              opts.c = existingColors.process_colors[process.id].join(":");
              opts.cc = process.maxColors;
              opts.spc = "1" ;
            }
          }
        }
        if(options.toReplace != null) {
          options.toReplace.del(); 
        }
        this.addNewItemByAsset(this.newProcessId, newAsset, opts);
    }.bind(this));
    d.track("add-image");
    return;
  },
  
  addText: function D_addText(processId, options) {
    if(options == null) options = {};
    if(processId == null) {
      processId = this.showSelectProcess("text", options.useDefaultProcess);
      if(processId == null) {
        return;
      }
    }
    
    if(!this.canAddAsset(processId)) {
      alert(ml("You cannot add an image to this area because it would change the price of your order"));
      return;
    }
    this.newTextOptions = options;
    this.newProcessId = processId;
    $("new_text").value = "";
    
    popup("new_text_popup");
    $("new_text").focus();
  },
  
  addTeamName: function D_addText(processId, options) {
    if(options == null) options = {};
    if(processId == null) {
      processId = this.showSelectProcess("text", options.useDefaultProcess);
      if(processId == null) {
        return;
      }
    }
    //check there is not already a teamname (if EMB)
    if(processes.byId[processId].isWilcomEMB()) {
      if(this.currentCViewArea.processes[processId].isUsingItemOfType(2)) {
        alert(ml("You can only have 1 embroidery teamname per decoration area"));
        return;
      }
    }
    
    this.newProcessId = processId;
    $("teamname_templates_container").update(this.teamNameTemplates.buildHtml(processId));
    
    var sizeHtml = "";
    if(this.currentProductType.sizeField != null) {
      sizeHtml = '<h4>' + ml('Setup Sizing (can be changed later)') + '</h4>' + this.currentProductType.sizeField.renderMultiQty(this.currentCProduct, this.currentCProduct.product.getSizeField(), this.currentCProduct.getSelectedSize(), this.currentCProduct.product.sizeColorCombinations, "tn_", false, true);
    } else {
      sizeHtml = '<h4>' + ml('Select Qty (can be changed later)') + '</h4>' + ml('Number of teamnames') + ': <input type="text" size="3" id="tn_qty" value="' + this.currentCProduct.qty + '"/>';
    }
    $("teamname_sizing_container").update(sizeHtml);
    popup("new_teamname_popup");
  },
  
  
  continueAddTeamname: function() {
    var opts = {team_t: this.teamNameTemplates.selectedTemplateId};
    var proc = processes.byId[this.newProcessId];
    if(proc.isWilcomEMB()) {
      opts.emb = "1";
    }
    opts.tc = pwColorPicker.getDefaultColor(this.newProcessId, "#FF3333", proc.defaultTextColorId);
    var teamItem = this.addNewItemByAsset(this.newProcessId, new TeamNameAsset(), opts);
    
    if(this.currentCProduct.getTeamNames().initNew(teamItem)) {
      popup("edit_teamname_popup");
    } else {
      alert(ml("You specify the number of teamnames you want"));
    }
  },
  
  addPlaceholder: function(processId) {
    if(processId == null) {
      processId = this.showSelectProcess("placeholder");
      if(processId == null) {
        return;
      }
    }
    
    var opts = {text: "Placeholder Artwork"};
    var proc = processes.byId[processId];
    if(proc.isWilcomEMB()) {
      opts.emb = "1";
    }
    this.addNewItemByAsset(processId, new PlaceholderAsset(), opts);
  },
  
  //get a list of allowed item types (image/text/teamname) by process
  getAllowedItemTypes: function D_getAllowedItemTypes() {
    if(this.allowedItemTypes != null) {
      return this.allowedItemTypes;
    }
    var allowed = {
      image: { count:0, single:null},
      text: { count:0, single:null},
      teamname: { count:0, single:null},
      placeholder: { count:0, single:null}
    };
    for(var i=0; i < processes.list.length; i++) {
      var proc = processes.list[i];
      if(proc.allowImages) {
        allowed.image.count ++;
        allowed.image.single = proc.id;
      }
      if(proc.allowText) {
        allowed.text.count ++;
        allowed.text.single = proc.id;
      }
      if(proc.allowTeamNames) {
        allowed.teamname.count ++;
        allowed.teamname.single = proc.id;
      }
      if(proc.allowPlaceHolder) {
        allowed.placeholder.count ++;
        allowed.placeholder.single = proc.id;
      }
    }
    this.allowedItemTypes = allowed;
    return this.allowedItemTypes;
  },
  
  //when an area/process is selected check what add item options should be available...
  checkAddAvailableOptions: function D_checkAddAvailableOptions() {
    var allAllowed = this.getAllowedItemTypes();
    
    if(allAllowed.image.count == 0) {
      //$("no_items_0_image").hide();
      $("add_item_image").hide();
    } else {
      //$("no_items_0_image").show();
      $("add_item_image").show();
    }
    if(allAllowed.text.count == 0) {
      //$("no_items_0_text").hide();
      $("add_item_text").hide();
    } else {
      //$("no_items_0_text").show();
      $("add_item_text").show();
    }
    if(allAllowed.teamname.count == 0) {
      //$("no_items_0_teamname").hide();
      $("add_item_team_name").hide();
    } else {
      //$("no_items_0_teamname").show();
      $("add_item_team_name").show();
    }
    if(this.inOrderManager) {
      $("add_item_placeholder").show();
    } else {
      $("add_item_placeholder").hide();
    }
  },
  
  showSelectProcess: function D_showSelectProcess(type, useDefaultProcess) {
    var types = ["text","teamname","image", "placeholder"];
    for(var i=0; i < types.length; i++) {
      if(types[i] == type) {
        $("select_type_" + types[i]).show();
      } else {
        $("select_type_" + types[i]).hide();
      }
    }
    //$("select_type_" + type).style.display = "";
    //$("select_type_" + otherType).style.display = "none";
    
    var allowedProcesses = this.currentCViewArea.getAllowedProcesses();
    var allowedTypes = this.getAllowedItemTypes();
    
    if(allowedTypes[type].count == 1) {
      if(this.defaultProcess != null && this.defaultProcess.id == allowedTypes[type].single) {
        log("Only one allowedTypes[process].count and its the default process... skip select process step");
        return allowedTypes[type].single;
      }
    }
    
    var procSize = hashSize(allowedProcesses);
    if(procSize == 0) {
      alert(ml("This product has been configured incorrectly and has no usable decoration processes assigned to it."));
      return null;
    } else {
      
      var allowCount = 0;
      var allowProcessId = null;
      var foundDefaultProcess = false;
      for(var i=0; i < processes.list.size(); i++) {
        var process = processes.list[i];
        /*if(process.allow[type] == true) { //can this process allow this type (i.e can emb allow text?)
          $("add_" + type + "_type_" + process.id).show();
        } else {
          $("add_" + type + "_type_" + process.id).hide();
        }*/
        
        if((allowedProcesses[process.id])&&(process.allow[type] == true)) { //we can use the process....
          if (processes.list.size() == 1) {
            return process.id;
          }
          $("add_" + type + "_type_" + process.id).className="";
          allowCount ++ ;
          allowProcessId = process.id;
          if(d.defaultProcessId == process.id) {
            foundDefaultProcess = true;
          }
        } else {
          $("add_" + type + "_type_" + process.id).className="disabled";
          //why cant we add this?
          if(process.allow[type] != true) {
            if(type=="text") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have text added", process.name));
            } else if(type=="teamname") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have team names added", process.name));
            } else if(type=="image") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have image designs added", process.name));
            } else if(type=="placeholder") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have placeholder added", process.name));
            }
          } else if(this.currentCViewArea.productArea.canDecorate(process)) { //because of mixing
            $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be mixed with existing decorations", process.name));
          } else {
            $("add_item_" + type + "_disabled_" + process.id).update(this.currentCViewArea.productArea.getDecProcDisabledMessage(process));
          }
          
        }
      }
      if(useDefaultProcess && foundDefaultProcess) {
        return d.defaultProcessId;
      }
      /*
      if(this.defaultProcess != null) {
        if(allowCount == 1 && allowProcessId == this.defaultProcess.id) {
          return this.defaultProcess.id;
        }
      }*/
      popup("select_type_popup");
      return null;
    }
  },
  
  continueAddText: function D_continueAddText() {
    var text = $("new_text").value;
    if((text == null)||(text == "")) {
      alert(ml("You must enter a value"));
      return;
    }
    var opts = {text: text};
    var proc = processes.byId[this.newProcessId];
    if(proc.isWilcomEMB()) {
      opts.emb = "1";
    }
    if(this.newTextOptions != null) {
      if(this.newTextOptions.toReplace != null) {
        opts.t = this.newTextOptions.toReplace.top;
        opts.l = this.newTextOptions.toReplace.left;
        opts.z = this.newTextOptions.toReplace.zIndex;
        this.newTextOptions.toReplace.del();
      } 
    }
    opts.tc = pwColorPicker.getDefaultColor(this.newProcessId, "#FF3333", proc.defaultTextColorId);
    this.addNewItemByAsset(this.newProcessId, new TextAsset(), opts);
    this.newTextOptions = null;
  },
  
  addNewItemByAsset: function D_addNewItemByAsset(processId, asset, initData) {
    //add to current canvas
    var newItem = this.currentCViewArea.addNewItem(processId, asset, initData);
    if(newItem != null) { 
      newItem.select();
      d.itemChanged(true);
      return newItem;
    }
  },
  
  startNewCartItem: function D_startNewCartItem(doSelect, showTab) {
    //check if the current item is dirty....
    if((typeof(dontWarnBeforeLeaving) != 'undefined')&&(dontWarnBeforeLeaving == false)) { //current item is dirty...
      msgBox(ml("Save Changes?"), ml("The currently loaded item has unsaved changes. Do you want to save the item before designing another product?"), null, ml("Yes"), ml("No"), ml("Cancel"), function(result) {
          if(result == 0) { //yes
            d.currentCProduct.save(false, false, function() {
                d.startNewCartItem(doSelect, showTab);
            });
          } else if(result == 1) { //no
            dontWarnBeforeLeaving = true;
            if(d.currentCProduct.addedToCart) {
              d.currentCProduct.rollBack();
            }
            d.currentProductType.itemSaved();
            d.startNewCartItem(doSelect, showTab);
          } else {
            //cancel...
          }
      });
      return;
    }
    
    var cp = this.cart.getUnsavedProduct();
    if(cp != null) {
      this.selectConfiguredProduct(cp.id, true, doSelect);
      if(showTab) {
        d.selectTab('m','apparel');
      }
      cp.setAlertIcons();
    } else {
      
      var asyncKey = asyncStart("designer_container");
      var self = this;
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/new_product?id=" + this.currentCProduct.product.id), {asynchronous:true, evalScripts:true, onComplete: function D_onComplete(response) {
          asyncFinish(asyncKey);
          cp = self.cart.getUnsavedProduct();
          if(cp != null) {
            self.selectConfiguredProduct(cp.id, true, doSelect);
            if(showTab) {
              d.selectTab('m','apparel');
            }
            cp.setAlertIcons();
          } else {
            alert("ERROR: No new item was added");
          }
        }
       });
    }
    
  },
  
  tbTransformed: function D_tbTransformed(configuredProductId, viewId, areaId, options) {
    var cp = this.cart.getProduct(configuredProductId);
    if(cp == null) {
      log("tbTransformed: Unable to get configured product " + configuredProductId);
      return;
    }
    var view = cp.views[viewId];
    if(view == null) {
      log("tbTransformed: Unable to get view " + viewId);
      return;
    }
    var area = view.areas[areaId];
    if(area == null) {
      log("tbTransformed: Unable to get area " + areaId);
      return;
    }
    area.tbTransformed(options);
  },
  
  selectTab: function D_selectTab(type, tab, callback) {
    var curTab = this.selected_tabs[type];
    if(curTab == tab) {
      return;
    }
    d.deSelectCurrentItem();
    d.track("select-tab/" + type + "/" + tab);
    
    if(callback!=null) {
      if(!callback(tab)) {
        return;
      }
    }
    log("cur tab = " + curTab + " type=" + type);
    var oldTab = $(type + "_" + curTab);
    if(oldTab.className != "unselected_tab_hidden") {
      $(type + "_" + curTab).className = "unselected_tab";
    }
    $(type + "_" + tab).className = "selected_tab";
    
    if(callback==null) {
      $(type + "_" + curTab + "_pane").style.display = "none";
      $(type + "_" + tab + "_pane").style.display = "";
    }
    this.selected_tabs[type] = tab;
    
    if(type=="m") {
      //if(tab=="customize") {
      //  this.refreshGallery();
      //}
      /*if(tab=="cart") {
        $("not_cart").style.display="none";
        $("is_cart").style.display="";
      } else {
        $("not_cart").style.display="";
        $("is_cart").style.display="none";
      }*/
    }
  },
  
  selectDesignTab: function D_selectDesignTab(tab, autoZoomed) {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      setBackgroundImage(d.designPane, d.designPane, this.currentCProduct.getViewURL(this.currentLayout.id, 1));
      return;
    }
    if(!this.currentCViewArea.canZoom()) {
      this.setZoomEnabled(this.currentCViewArea, false, false);
      return;
    }
    log("selectDesignTab");
    if(tab == null) {
      if(this.currentCanvasType == 0) {
        tab = "layout";
      } else {
        tab = "design";
      }
    }
    if(tab=="layout") {
      this.currentCanvasType = 0;
      if($("zoom_out_button").style.display != "none") {
        this.displayZoomButton(false);
      }
    } else {
      this.currentCanvasType = 1;
      if(d.designerOptions.clickAreaSelects() && this.designerOptions.clickAreaZooms() && this.currentCViewArea.canZoom()) {
        this.displayZoomButton(true);
      }
    }
    
    this.currentCView.toggleDesignMode();
    //this.calibrateCanvas(true);
    return true;
  },
  
  displayZoomButton: function D_displayZoomButton(show) {
    if(this.zoomButtonEffect != null) {
      this.zoomButtonEffect.cancel();
      this.zoomButtonEffect = null;
    }
    if(!show) {
      if($("zoom_out_button").style.display != "none") {
        this.zoomButtonEffect = Effect.Fade("zoom_out_button", { duration: 1.0 });
      }
    } else {
      this.zoomButtonEffect = Effect.Appear("zoom_out_button", { duration: 1.0 });
    }
  },
  
  setZoomEnabled: function D_setZoomEnabled(area, enabled, toggleZoomButton) {
    if(enabled) {
      if(this.currentCanvasType == 0) { //zoomed out
        $("d_layout").className = "selected_tab";
        $("d_design").className = "unselected_tab";
      } else {
        $("d_layout").className = "unselected_tab";
        $("d_design").className = "selected_tab";
      }
      updateToolTip("d_layout", ml("Zoom out to display the complete product"));
      updateToolTip("d_design", ml("Zoom into the decoration area to get better control"));
      if(toggleZoomButton) this.displayZoomButton(true);
    } else {
      $("d_layout").className = "selected_tab";
      $("d_design").className = "selected_tab";
      updateToolTip("d_layout", ml("Unable to zoom because this decoration area is too large"));
      updateToolTip("d_design", ml("Unable to zoom because this decoration area is too large"));
      if(toggleZoomButton) this.displayZoomButton(false);
    }
  },
  
  calibrateCanvas: function D_calibrateCanvas(fromSwitch) {
    //var pos = Position.cumulativeOffset(this.currentCanvas);
    //this.canvasX = pos[0];
    //this.canvasY = pos[1];
  },
  
  //store what zoom level we are currently using...
  setCurrentZoom: function(zoom) {
    this.zoom = zoom;
  },
  
  toCanvasDims: function D_toCanvasDims(dims, productProcess, canvasType) {
    if(productProcess==null) {
      alert("Error: toCanvasDims: productProcess==null");
    }
    //log(productProcess);
    //log(dims);
    var scale = 1.0;
    if(canvasType!=null) {
      scale = (canvasType==0) ? productProcess.layoutScale : productProcess.designScale;
    } else {
      scale = productProcess.layoutScale * this.zoom;
    }
    if(dims.l != null) {
      dims.l = (dims.l * scale);// + this.canvasX;
    }
    if(dims.t != null) {
      dims.t = (dims.t * scale);// + this.canvasY;
    }
    if(dims.h != null) {
      dims.h = (dims.h * scale);
    }
    if(dims.w != null) {
      dims.w = (dims.w * scale);
    }
    return dims;
  },
  
  fromCanvasDims: function D_fromCanvasDims(dims, productProcess, canvasType) {
    if(productProcess==null) {
      alert("Error: fromCanvasDims: productProcess==null");
    }
    var scale = 1.0;
    if(canvasType!=null) {
      scale = (canvasType==0) ? productProcess.layoutScale : productProcess.designScale;
    } else {
      scale = productProcess.layoutScale * this.zoom;
    }
    if(dims.l != null) {
      dims.l = (dims.l) / scale;
    }
    if(dims.t != null) {
      dims.t = (dims.t) / scale;
    }
    if(dims.h != null) {
      dims.h = (dims.h / scale);
    }
    if(dims.w != null) {
      dims.w = (dims.w / scale);
    }
    return dims;
  },
  
  viewMouseMove: function D_viewMouseMove(td) {
    if(td.className != "d_layout_selected") {
      td.className = "d_layout_mouseover";
    }
  },
  
  viewMouseOut: function D_viewMouseOut(td) {
    if(td.className != "d_layout_selected") {
      td.className = "d_layout_unselected";
    }
  },
  
  viewClick: function D_viewClick(viewId) {
    if(viewId == this.selectedViewId) {
      return;
    }
    this.selectCurrentView(viewId);
  },
  
  //toggle the 'selected' class on the thumbnail of the view
  viewToggle: function D_viewToggle(viewId) {
    if(viewId == this.selectedViewId) {
      return;
    }
    var td = $("d_l_" + this.selectedViewId);
    if(td!=null) {
      td.className = "d_layout_unselected";
    }
    td = $("d_l_" + viewId);
    if(td!=null) {
      td.className = "d_layout_selected";
    }
    this.selectedViewId = viewId;
  },
  /*
  layoutSelect: function D_layoutSelect(el, layoutId) {
    this.currentCProduct.layoutSelect(layoutId, el.checked);
  },
  */
  layoutChecked: function D_layoutChecked(el, id) {
    if(!el.checked) {
      if(this.currentCProduct.layoutExistsWithItems(id)) {
        if(confirm(ml("Are you sure you want to remove all the designs?"))) {
          var lm = this.currentCProduct.views[id];
          while( lm.items.length> 0) {
            lm.items[0].del();
          }
          lm.selected = false;
          this.itemChanged(true);
        }
      }
    }
  },
  
  colorMouseMove: function D_colorMouseMove(t, id) {
    //if(t.className != "color_panel_cell_selected") {
      //t.className = "color_panel_cell_over";
    //}
    t.className=t.className.replace(" over", "");
    t.className=t.className+" over";
  },
  
  colorMouseOut: function D_colorMouseOut(t, id) {
    //if(t.className != "color_panel_cell_selected") {
      //t.className = "color_panel_cell";
    //}
    t.className=t.className.replace(" over", "");
  },
  
  piMouseMove: function D_piMouseMove(t, id) {
    if(t.className != "product_cell_selected") {
      t.className = "product_cell_over";
    }
  },
  
  piMouseOut: function D_piMouseOut(t, id) {
    if(t.className != "product_cell_selected") {
      t.className = "product_cell";
    }
  },
  
  getNextId: function D_getNextId() {
    return this.nextId --;
  },
  
  getNextItemId: function CP_getNextItemId() {
    return this.nextItemId ++; 
  },
  
  registerServerItemId: function CP_registerServerItemId(id) {
    if(id >= this.nextItemId) {
      this.nextItemId = id + 1;
    }
    return id;
  },
  
  qtyChanged: function D_qtyChanged(fromKeyUp) {
    if(fromKeyUp && !this.currentCProduct.product.usesMinQty()) {
      this.currentCProduct.updateQty($("qty").value, false);
      d.itemChanged();
    } else if(!fromKeyUp && this.currentCProduct.product.usesMinQty()) {
      this.currentCProduct.updateQty($("qty").value, false);
      d.itemChanged();
    }  
  },
  
  multiQtyChange: function D_multiQtyChange(el, fieldId, optionId, subOptionId, unitPrice, isAutoPrice) {
    var value = (typeof el == 'number') ? el : el.value;
    this.currentCProduct.updateMultiQty(value, fieldId, optionId, subOptionId, unitPrice, isAutoPrice);
    d.itemChanged();
  },
  
  //user had clicked the dropdown for qty
  qtyDropDown: function D_qtyDropDown() {
    if(this.qtyDropDownActive) {
      //ignore.. mouseup will hide the dropdown...
    } else {
      $("qty_dropdown_float").update(this.currentCProduct.product.buildQtyDropdownHtml(this.currentCProduct.qty));
      $("qty_dropdown_float").show();
      this.qtyDropDownActive=true;
      //$("qty_selector_sprites").style.top = "-66px";
	  $("qty_selector").className="alt";
	  
      this.qtyDropDownTime = new Date().getTime();
      Event.observe(document.body, "mouseup", this.onQtyDDBgEvent);
    }
  },
  
  qtyDropDownSelected: function D_qtyDropDownSelected(qty) {
    $("qty").value = qty;
    this.currentCProduct.updateQty(qty, false);
    d.itemChanged();
  },
  
  onQtyDDBg: function D_onQtyDDBg(event) {
    var thisTime = new Date().getTime();
    if(this.qtyDropDownTime != null && thisTime - this.qtyDropDownTime < 2000) {
       this.qtyDropDownTime = null;
       return; //this was the mouse up event from when we bound it... we ignore it....
    }
    //$("qty_selector_sprites").style.top = "-22px";
	$("qty_selector").className=null;
	
    $("qty_dropdown_float").hide();
    this.qtyDropDownActive = false;
    Event.stopObserving(document.body, "mouseup", this.onQtyDDBgEvent);
  },
  
  fieldChange: function D_fieldChange(el, fieldId) {
    d.currentCProduct.setFieldValue(fieldId, parseInt($(el).value, 10)) ;
  },
  
  itemChanged: function D_itemChanged(is_layout_change) {
    if (typeof(dontWarnBeforeLeaving) != 'undefined') dontWarnBeforeLeaving = false;
    if(is_layout_change) {
      this.currentCProduct.onLayoutChanged();
      this.currentCView.queueViewUpdate();
      if(this.saveContainer != null) this.saveContainer.show();
    }
    this.currentCProduct.product.type.itemChanged();
  },
  
  //from server callback to let us know a thumbnail for a view is ready...
  updateView: function D_updateView(cpid, viewId, src, renderVersion, results) {
    var p = this.cart.getProduct(cpid);
    if(p!=null) {
      p.updateView(viewId, src, renderVersion, results);
    }
  },
  
  checkout: function D_checkout() {
    if(d.mode == DESIGNER_MODE_AMEND) {
      window.location = d.pathPrefix + "/user/view_order/" + this.cart.id + this.extraExternalParams;
    } else {
      /*
      if($("update_cart_container") != null) {
        if($("update_cart_container").style.display == "") {
          if(!confirm(ml("You have unsaved changes to the current item in your cart. Are you sure you want to checkout with saving?"))) {
            return;
          }
        }
      }*/
      d.track("checkout");
      proceedWithOrder();
      //window.location = d.pathPrefix + "/shop/verify" + this.extraExternalParams;
      
    }
  },
  
  continueShopping: function D_continueShopping() {
    if(d.mode == DESIGNER_MODE_AMEND) {
      window.location = d.pathPrefix + "/home/create";
    } else {
      if($("update_cart_container") != null) {
        if($("update_cart_container").style.display == "") {
          if(!confirm(ml("You have unsaved changes to the current item in your cart. Are you sure you want to navigate away without saving?"))) {
            return;
          }
        }
      }
      d.track("continue shopping");
      window.location = d.pathPrefix + "/home/create";
    }
  },
  
  notifyCartChanged: function D_notifyCartChanged() {
    if(window.opener != null) {
      try {
        window.opener.notifyCartChanged();
      } catch(e) {
        log("Unable to notify opener window of item saved");
        log(e);
      }
    }
  },
  
  offsetTop: function D_offsetTop(element, cutOff) {
    var valueT = 0;
    do {
      valueT += element.offsetTop  || 0;
      element = element.offsetParent;
    } while (element && element != cutOff);
    return valueT;
  },
  
  scrollCat: function D_scrollCat(amount) {
    this.currentProductType.scrollCat(amount);
  },
  
  showQualityWarning: function D_showQualityWarning() {
    if(this.hideQualityWarning) {
      return false;
    }
    //popup('quality_warning');
    //return true;
    return false;
  },
  
  acceptQualityWarning: function D_acceptQualityWarning() {
    var doHide = $("dont_show_qual_warning").checked;
    if(doHide) {
      this.hideQualityWarning = true;
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/user_quality_warning"), {asynchronous:true, evalScripts:true });
    }
    closePopup('quality_warning');
  },
  
  //user has selected a different product type....
  changeProductType: function D_changeProductType(el) {
    var pt = el.value;
    
    if(pt != this.currentProductType.id) {
      this.deSelectCurrentItem();
      
      d.track("change-product-type");
      
      this.currentProductType.deselect();
      var self = this;
      
      this.getProductType(pt, function(prodType) {
        self.currentProductType = prodType;
        if (self.currentProductType.id == $("sel_prod_type").value) {
          self.currentProductType.select( function() {
            d.currentProductType.selectConfiguredProduct(d.currentCProduct, true, false);
            d.currentCProduct.afterProductChanged();
          }, -1, -1);
        }
        //$("product_type_tab_name").innerHTML = prodType.name;
      });
      
    }
    
  },
  
  recalibrateCanvas: function D_recalibrateCanvas() {
    if(this.currentCViewArea!=null) {
      this.currentCViewArea.canvasX = null;
      if(this.currentCViewArea.selectedItem != null) {
        this.currentCViewArea.selectedItem.autoRepositionHandles();
      }
    }
  },
  
  roundPrice: function D_roundPrice(price) {
    if(this.doPriceRounding) {
      if(this.priceRounding > 1) {
        log("rounding " + price + " to " + this.priceRounding);
        var cents = Math.round(price * 100);
        log("cents = " + cents);
        var divs = parseInt(cents / this.priceRounding, 10);
        log("divs = " + divs);
        var adjCents = divs * this.priceRounding;
        log("adjCents = " + adjCents);
        if(adjCents < cents) {
          log("adjCents < cents");
          adjCents += this.priceRounding;
        }
        //round to 99cents if on 1 dollar
        if(this.price99) {
          if((adjCents % 100 == 0)&&(cents>0)) {
            log("adjCents % 100");
            adjCents -= 1;
          }
        }
        price = parseFloat(adjCents) / 100;
    } else {
      var cents = Math.round(price * 100);
      if(this.price99) {
        if((cents % 100 == 0)&&(cents>0)) {
          log("cents % 100");
          cents -= 1;
        }
      }
      price = parseFloat(cents) / 100;
      }
    }
    return price;
  },
  
  roundPriceObject: function D_roundPriceObject(price) {     
    price = price.copy();
    price.rrp = this.roundPrice(price.rrp);
    price.crp = this.roundPrice(price.crp);
    if(!price.isOverriden) price.override = this.roundPrice(price.override);
    if(price.legacy) price.legacy = this.roundPrice(price.legacy); //we will assume the rounding rules hanvnt changed.....
    return price;
  },
  
  round: function D_round(price) {
    return parseFloat(Math.round(price * 100.0)) / 100.0
  },
  
  addPrice: function D_addPrice(base, inc) {
    var res = {cost:0.0, rrp: 0.0, crp: 0.0, overide:0.0};
    var p = [base, inc];
    for(var i=0; i < 2; i++) {
      if(p[i].cost != null) res.cost +=  p[i].cost;
      if(p[i].rrp != null) res.rrp +=  p[i].rrp;
      if(p[i].crp != null) res.crp +=  p[i].crp;
      if(p[i].overide != null) res.overide +=  p[i].overide;
    }
    return res;
  },
  
  legacyPrice: function D_legacyPrice(legacy, fallback) {
    if(this.useLegacyPrices && legacy != null) {
      return legacy;
    } else {
      return fallback;
    }
  },
  
  registerLegacyPrice: function D_registerLegacyPrice(object) {
    this.legacyPriceChanges[object.id] = object;
  },
  
  resetLegacyPriceTracking: function() {
    this.legacyPriceChanges = {};
  },
  
  getLegacyPrices: function() {
    var objectsWithLegacy = [];
    for(var k in this.legacyPriceChanges) {
      var obj = this.legacyPriceChanges[k];
      if(obj.legacyPriceChanges.length > 0) {
        objectsWithLegacy.push(obj);
      }
    }
    return objectsWithLegacy;
  },
  
  formatPrice: function D_formatPrice(price, clazz, applyTax, rrp) {
    if(applyTax==null) {
      applyTax = true;
    }
    var taxWarn = "";
      if((this.tMod != 1)&&(applyTax) && (!d.currentCProduct || !d.currentCProduct.product.taxExempt)) {
      log("Applying tax mod of " + this.tMod);
      taxWarn = '<span class="tax_warning">*</span>';
      price *= this.tMod;
    }
    price *= this.cMod;
    if(rrp != null) {
      if((this.tMod != 1)&&(applyTax) && (!d.currentCProduct || !d.currentCProduct.product.taxExempt)) {
        rrp *= this.tMod;
      }
      rrp *= this.cMod;
    }
      
    var dc = price.toPrice(); //fix: .toFixed(2) was truncating
    var codeHtml = this.curCode;
    if(clazz!=null) {
      codeHtml = '<span class="' + clazz + '">' + this.curCode + "</span>";
    }
    if((rrp != null)&&(rrp != price)&&(typeof(orderManager) == "undefined")) {
      rrp = '<span class="rrp">' + this.curGlyph + (parseFloat(Math.round(rrp * 100)) / 100.0) + '</span> ';   
    } else {
      rrp = '';  
    }
    
    switch(this.priceFormat) {
      case 1: return rrp + this.curGlyph + dc + " " + taxWarn;
      case 2: return rrp + dc + " " + codeHtml + taxWarn;
      case 3: return rrp + dc + " " + taxWarn;
      case 4: return rrp + dc + this.curGlyph + " " + taxWarn;
      default: return rrp + this.curGlyph + dc + " " + codeHtml + taxWarn;
    }
  },
  
  deSelectCurrentItem: function D_deSelectCurrentItem() {
    if(this.currentCViewArea) {
      log("deSelectCurrentItem");
      this.currentCViewArea.selectItem(null);
    }
  },
  
  onKeyUp: function D_onKeyUp(event) {
    if(this.currentCViewArea && this.currentCViewArea.selectedItem!=null) {
      if(event==null) {
        event = window.event;
      }
      if(event.keyCode==Event.KEY_DELETE) {
        var el = Event.element(event);
        
        var allowDelete = true;
        
        while(el != null) {
          if(el.tagName == "INPUT" || el.tagName == "SELECT" || el.tagName == "TEXTAREA") {
            return;
          } else if(el.getAttribute("allow-delete") == "true") {
            return;
          }
          el = el.parentElement;
        }
        if(this.currentCViewArea.selectedItem.canModifyDesign) {
          this.currentCViewArea.selectedItem.deleteClick(event);
        }
      }
    }
  },
  
  showProductInfo: function D_showProductInfo() {
      d.deSelectCurrentItem();
      var purl = this.pathPrefix + "/product_info/ppop";
      var url = "";
      if(purl.indexOf("?") == -1) {
        url = purl + "?pid=" + this.currentCProduct.product.id;
      } else {
        url = purl + "&pid=" + this.currentCProduct.product.id;
      }
    //}
    $("dynamic_popup").innerHTML = "<br/><br/>Generating..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({success:"dynamic_popup"}, d.ajaxUrl(url), {asynchronous:true, evalScripts:true,
      onComplete: function D_onComplete() { 
        asyncFinish(aKey);
        $("dynamic_popup").style.width="650px";
        repositionPopup("dynamic_popup");
      },
      onFailure: function D_onFailure() {
        closePopup("dynamic_popup");
        alert("An error occured processing preview");
      }
    });
    
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  showDesignerHelp: function D_showDesignerHelp() {
    d.deSelectCurrentItem();
    var url = this.pathPrefix + "/product_info/designer_help/ppop?popup";
    $("dynamic_popup").innerHTML = "<br/><br/>Generating..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({success:"dynamic_popup"}, d.ajaxUrl(url), {asynchronous:true, evalScripts:true,
      onComplete: function D_onComplete() { 
        asyncFinish(aKey);
        //$$("#dynamic_popup .popup")[0].style.width="550px";
        $("dynamic_popup").style.width="550px";
        $("dynamic_popup").style.marginLeft="-275px";
        repositionPopup("dynamic_popup");
      },
      onFailure: function D_onFailure() {
        closePopup("dynamic_popup");
        alert("An error occured processing preview");
      }
    });
    
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  
  displayAssetInformation: function D_displayAssetInformation(asset) {
    $("asset_view").style.display='';
    $("i_no_tab").width="600";
    var aImg = $("asset_view_img");
    var dim = asset.scale(300,300);
    aImg.style.width = parseInt(dim.w, 10) + "px";
    aImg.style.height = parseInt(dim.h, 10) + "px";
    setTransparentImage($("asset_view"), aImg, asset.getWUrl(), function() { repositionPopup('add_image'); });
    if(asset.colors != null) {
      var tr = $("asset_colors_tr");
      while(tr.firstChild != null) {
        tr.removeChild(tr.firstChild);
      }
      for(var i=asset.colors.length-1; i >=0; i--) {
        var color = asset.colors[i];
        
        var td = document.createElement("TD");
        td.innerHTML = '<div class="mp_color_button" style="width:10px;display:inline;margin-right:2px;font-size:8px;height:10px;background-color:' + color + ';cursor:default;"><img src="' + this.pathPrefix + '/images/trans.gif" width="10" height="10" border="0"/></div>';
        tr.appendChild(td);
      }
      
      $("asset_colors").style.display="";
    } else {
      $("asset_colors").style.display="none";
    }
  },
  
  preloadImages: function D_preloadImages() {
    return;
    
    var imgUrl = null;
    if(!this.effectIconsLoaded) {
      this.preloadEffectIndex ++;
      if(imageEffects.size() <= this.preloadEffectIndex) {
        this.effectIconsLoaded = true;
      } else {
        imgUrl = d.pathPrefix + "/images/effects/" + imageEffects[this.preloadEffectIndex][2];
      }
    }
    if((!this.borderIconsLoaded)&&(imgUrl==null)) {
      this.preloadBorderIndex ++;
      if(borderEffects.size() <= this.preloadBorderIndex) {
        this.borderIconsLoaded = true;
      } else {
        var border = borderEffects[this.preloadBorderIndex];
        if(border[0] == 0) {
          imgUrl = d.pathPrefix + "/images/noborder.gif";
        } else {
          imgUrl = d.pathPrefix + "/border/image/" + border[0] + "/thumb-ppr.gif";
        }
      }
    }
    if(imgUrl!=null) {
      var cbimg = document.createElement("IMG");
      $("preload-panel").appendChild(cbimg);
      var callbackDone = false;
  
      var callback = function() {
        if(callbackDone) {
          return;
        }
        callbackDone = true;
        window.setTimeout( function() {
          d.preloadImages();
        }, 100);
      };
      
      cbimg.onload = callback;
      cbimg.onerror = callback;
      cbimg.onabort = callback;
      cbimg.src = imgUrl;
      if(BrowserDetect.browser == "Explorer") {
        if(cbimg.complete) {
          callback();
        }
      }
    }
  },
  
  saveWorkingVersion: function D_saveWorkingVersion() {
    this.currentCProduct.saveWorkingVersion();
  },
  
  
  track: function D_track(key) {
    if(this.useAnalytics) {
      try {
        urchinTracker("/designer-action/" + key);
      } catch(ex) {
        log("Exception tracking " + key);
        log(ex);
      }
    }
  },
  
  monitorSizing: function D_monitorSizing() {
    if(this.testWidthImg == null) {
      this.testWidthImg = $('body-bottom');
      if(this.testWidthImg!=null) {
        this.lastPageWidthTest = Position.cumulativeOffset(this.testWidthImg)[0];
      }
    } else {
      var pos = Position.cumulativeOffset(this.testWidthImg);
      //log("monitorSizing:" + pos[0] + "," + this.lastPageWidthTest);
      if(pos[0]!=this.lastPageWidthTest) {
        this.recalibrateCanvas();
      }
      this.lastPageWidthTest = pos[0];
    }
    
    if(this.testWidthImg != null) {
      var _this = this;
      window.setTimeout( function() {
          _this.monitorSizing();
          
      }, 500);
    } else {
      log("Not monitoring sizing: no body-bottom");
    }
  },
  
  
  emailDesign: function D_emailDesign() {
    this.currentCProduct.email();
  },
  
  sendEmail: function D_sendEmail() {
    var aKey = null;
    var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/send_email?mode=" + this.mode), {asynchronous:true, evalScripts:true, parameters:Form.serialize("email_data", true),
      onComplete: function D_onComplete() {
        asyncFinish(aKey);
      }
    });
    aKey = asyncStart($("dynamic_popup"));
  },
  
  displayGrid: function D_displayGrid(isOn) {
    this.showGrid = isOn;
    if(this.showGrid) {
      $("hide_grid").style.display="";
      $("show_grid").style.display="none";
    } else {
      $("hide_grid").style.display="none";
      $("show_grid").style.display="";
    }
    this.currentCView.prepareAreas(false);
  },
  
  showPreview: function D_showPreview() {
    this.currentCProduct.showPreview();
  },
  
  setDiscountStructure: function D_setDiscountStructure(id, discounts, discontinued, name) {
    this.discountNames[id] = name;
    if (discontinued) {
      if (this.discounts[id]) {
        delete this.discounts[id];
      }
    } else {
      this.discounts[id] = discounts;
      if(this.defaultDiscountId == null) {
        this.defaultDiscountId = id;
      }
    }
  },
  
  setAffiliateDiscounting: function D_setAffiliateDiscounting(affiliateDiscounting) {
    this.affiliateDiscounting = affiliateDiscounting;
  },
  
  getDiscount: function D_getDiscount(discountId, qty, price, cost) {
    var dis = 0;
    var dt = this.discounts[discountId];
    if(dt == null) {
      log("ERROR: missing discount configuration " + discountId);
      return 0;
    }
    for(var i=0; i < dt.size(); i++) {
      if(qty > dt[i].m) {
        log("found discount level at " + dt[i].m + " discount=" + dt[i].d);
        dis = dt[i].d;
      }
    }
    if(dis == 0) {
      return 0;
    }
    
    //apply qty before rounding because discount is not at the unit level...
    if(this.affiliateDiscounting) {
      log("applying discount of " + dis + "% to " + price + ", qty =" + qty);
      return Math.round(price * dis * qty) / 100.0 ;
    } else {
      log("applying discount of " + dis + "% to " + cost + ", qty =" + qty);
      return Math.round(cost * dis * qty) / 100.0 ;
    }
  },
  
  selectBackgroundColor: function D_selectBackgroundColor(processId) {
    this.currentCViewArea.selectBackgroundColor(processId);
  },
  
  setError: function D_setError(cProductId,views, errors) {
    //sort into views/areas
    var errorsByViews = {};
    if(errors==null) errors = [];
    for(i=0; i < views.length;i++) {
      errorsByViews[views[i]] = {areas:{}, errors:{}};
    }
    for(var i=0; i < errors.length; i++ ){
      var error = errors[i];
      var view = errorsByViews[error.v];
      if(error.a == null) {
        //error is against the view...
        view.errors[error.type + error.cause] = error;
      } else {
        if(view.areas[error.a] == null) {
          view.areas[error.a] = {};
        }
        view.areas[error.a][error.type + error.cause] = error;
      }
    }
    var cProduct = this.cart.getProduct(cProductId);
    //call each view...
    for(i=0; i < views.length;i++) {
      var cView = cProduct.getView(views[i]);
      if(cView != null) {
        cView.setErrors(errorsByViews[views[i]]);
      }
    }
  },
  
  getLengthUnit: function D_getLengthUnit() {
    switch (this.lengthUnit)
    {
    case 1:
      return "in";
    case 2:
      return "cm";
    }
  },
  
  convertLengthFromInches: function D_convertLengthFromInches(l) {
    switch (this.lengthUnit)
    {
    case 1:
      return l;
    case 2:
      return l*2.54;
    }
  },
  
  convertLengthToInches: function D_convertLengthToInches(l) {
    switch (this.lengthUnit)
    {
    case 1:
      return l;
    case 2:
      return l/2.54;
    }
  },
  
  allowedFileUploadImageExtensions: function D_allowedFileUploadImageExtensions() {
    var allowed = [];
    var proc = processes.byId[this.decorationLibraryManager.currentProcessId];
    if((this.mode == DESIGNER_MODE_CONFIGURE) && (proc.isWilcomEMB())) {
      allowed = ["emb"];
    } else {
      if(proc.isWilcomEMB()) {
        if(proc.allowImageUpload) {
          allowed = ["jpg", "jpeg", "png" , "gif", "emb", "svg", "eps"];
        } else {
          allowed = ["emb"];
        }
      } else {
        allowed = ["jpg", "jpeg", "png" , "gif", "svg", "eps"];
      }
    }
    return allowed;
  },
  
  //load the popup asking what to add....
  showAddItemIntro: function() {

    var allowedTypes = this.currentCViewArea.getAllAllowedItemTypes();
    
    var passCount =0;
    var itemTypes = ["image","text","teamname"];
    var passedArrows = [];
    for(var i=0; i < itemTypes.length; i++) {
      var type = itemTypes[i];
      if(allowedTypes[type].count > 0) {
        $("opener_" + type).show();
        passedArrows.push($("arrow_" + type));
        passCount += 1;
      } else {
        $("opener_" + type).hide();
      }
    }
    if(passCount==0) {
      alert("FATAL ERROR: this product cannot be decorated");  
    } else if(passCount==1) {
      passedArrows[0].className = "opener_overlay middle";
    } else if(passCount==2) {
      passedArrows[0].className = "opener_overlay top";
      passedArrows[1].className = "opener_overlay bottom";
    } else {
      //starts correctly...
    }
    
    var loc = "";
    if(this.currentCView.productView.areas.list.length == 1) {
      loc = ml("You will be adding a decoration to the %1s of the %2s", [this.currentCView.productView.name , this.currentCProduct.product.name]);
    } else {
      loc = ml("You will be adding a decoration to the %1s %2s of the %3s", [this.currentCView.productView.name, this.currentCViewArea.productArea.name, this.currentCProduct.product.name]);
    }
    $("opener_location").update(loc);
    
    var self = this;
    popup('opener', {
        bgClass:"lightbg",
        bgOpacity: 0.65,
        positionCallback: function(div, popupParentOffset) {
           var pos = Position.cumulativeOffset(self.currentCViewArea.canvas);
           var topSectionHeight = $("opener_top_section").getHeight();
           
           div.style.left = (pos[0] + 194 + (self.currentCViewArea.canvas.getWidth() / 2)) + "px";
           
           var top = pos[1] - topSectionHeight - 90 + (self.currentCViewArea.canvas.getHeight() / 2);
           if(top < 10) {
             top = 10;
           }
           div.style.top = (top - popupParentOffset) + "px";
           if(self.contentTopEl != null) {
             pos = Position.cumulativeOffset(self.contentTopEl);
             if(pos[1] > top) { //the popup goes above the content top...
               self.contentTopEl = div;
             }
           } else {
             self.contentTopEl = div;
           }
        }
    });
  },
  
  initScrollPosition: function() {
    if(this.contentTopEl != null) {
      this.contentTopEl.scrollIntoView(true);
    }
  },
  
   //user had clicked the dropdown for copy
  chooseCopy: function D_chooseCopy() {
    if(this.copyDropDownActive) {
      //ignore.. mouseup will hide the dropdown...
    } else {
     
      
      $("copy_dropdown_float").show();
      this.copyDropDownActive=true;
      //$("qty_selector_sprites").style.top = "-66px";
      this.copyDropDownTime = new Date().getTime();
      Event.observe(document.body, "mouseup", this.onCopyBgEvent);
    }
  },
  
  copyArea: function D_copyArea() {
    if(hashFirstElement(this.currentCViewArea.allItems) != null) {
      this.hideCopySelector();
      this.currentCopyItem = this.currentCViewArea;
      this.currentCopyItemState = this.currentCViewArea.getCopyState();
      this.checkCopyPasteState();
    }
  },
  
  copySelected: function D_copySelected() {
    if(this.currentCViewArea.selectedItem != null) {
      this.hideCopySelector();
      this.currentCopyItem = this.currentCViewArea.selectedItem;
      this.currentCopyItemState = NULL;
      this.checkCopyPasteState();
    }
  },
  
  onCopyBg: function D_onCopyBg(event) {
    var thisTime = new Date().getTime();
    if(this.copyDropDownTime != null && thisTime - this.copyDropDownTime < 500) {
      log("Aborting onCopyBg: thisTime=" + thisTime + " this.copyDropDownTime=" + this.copyDropDownTime);
       this.copyDropDownTime = null;
       return; //this was the mouse up event from when we bound it... we ignore it....
    }
    var element = Event.element(event);
    if(element != null && element.className == "disabled") return;
    
    //$("qty_selector_sprites").style.top = "-22px";
    this.hideCopySelector();
    
  },
  
  hideCopySelector: function D_hideCopySelector() {
    $("copy_dropdown_float").hide();
    this.copyDropDownActive = false;
    Event.stopObserving(document.body, "mouseup", this.onCopyBgEvent);
  },
  
  checkCopyPasteState: function D_checkCopyPasteState() {
    if(this.currentCViewArea.selectedItem == null) {
      $("copy_item_button").className = "disabled";  
    } else {
      $("copy_item_button").className = "";
    }   
    if(hashFirstElement(this.currentCViewArea.allItems) == null) {
      $("copy_area_button").className = "disabled";
      $("copy_button").className = "disabled";
    } else {
      $("copy_area_button").className = "";
      $("copy_button").className = "";
    }
    if(this.currentCopyItem != null) {
      $("paste_button").className = "";
    } else {
      $("paste_button").className = "disabled";
    }
  },
  
  paste: function D_paste() {
    log("D_paste");
    if(this.currentCopyItem == null) return;
    var results = this.currentCopyItem.copyTo(this.currentCViewArea, this.currentCopyItemState);
    this.currentProductType.updatePrice();
    this.itemChanged();
    if(results[0] != results[1]) {
      if(results[1] == 1) {
        alert(ml("Unable to paste item because target decoration area does not support the items decoration process")); 
      } else if(results[0] == 0) {
        alert(ml("Unable to paste any items because target decoration area does not support the items decoration process")); 
      } else {
        alert(ml("Not all items could be pasted because target decoration area does not support the items decoration process (%1s/%2s)", results));
      }
    }
  },

  updateBreadCrumb: function D_updateBreadCrumb(pid, cpid) {
    var params = {};
    if (pid) {
      params['id'] = pid;
    } else {
      params['pid'] = cpid;
    }
    var asyncKey = asyncStart("breadcrumb");
    var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/update_breadcrumb?" + $H(params).toQueryString()), {asynchronous:true, evalScripts:true,
        onComplete: function D_onComplete(response) {
          asyncFinish(asyncKey);
        }
    });
  }
});


var ChargeDecorationLibrary = Class.create({
  CLASSDEF: {
      name: 'ChargeDecorationLibrary'
  },
  
  initialize: function(range1price, range2price, range3price, range4price, range5price, range6price, range7price) {
    this.range1price = range1price;
    this.range2price = range2price;
    this.range3price = range3price;
    this.range4price = range4price;
    this.range5price = range5price;
    this.range6price = range6price;
    this.range7price = range7price;
  }
});

var decorationLibrary = null; //this is the currently selected library 
var DecorationLibraryManager = Class.create({
  CLASSDEF: {
      name: 'DecorationLibraryManager'
  },
  
  initialize: function DLM_initialize(designer) {
    this.designer = designer;
    this.libraries = {};
    this.nextId = -1;
    this.imageSelectorFunction = this.selectImageInLibrary.bind(this);
    this.categorySelectorFunction = this.selectCategoryInLibrary.bind(this);
    this.imageUploadedFunction = this.imageUploaded.bind(this);
    this.doubleClickImageFunction =  this.doubleClickImage.bind(this);
  },
  
  getNextId: function DLM_getNextId() {
    return this.nextId--;
  },
  
  selectImage: function DLM_selectImage(productAreaProcess, callback) {
    this.selectImageCallback = callback;
    popup("decoration_libraries");
    this.productAreaProcess = productAreaProcess;
    this.currentProcessId = productAreaProcess.id;
    this.getLibrary(this.currentProcessId, function(library) {
        $("library_browser_" + this.currentProcessId).show();
    }.bind(this));
  },
  
  getLibrary: function DLM_getLibrary(processId, callback) {
    if((this.libraries[processId] == null)||((this.libraries[processId][0] != cCustomerId))) {
      if(this.libraries[processId] != null) { //must have changed user...
        $("library_browser_" + this.currentProcessId).remove();
      }
      $("declib_loading").show();
      var aKey = asyncStart("decoration_libraries_container");
      this.registerLibraryCallback = function() {
        asyncFinish(aKey);
        $("declib_loading").hide();
        decorationLibrary = this.libraries[processId][1];
        callback(decorationLibrary);
      }.bind(this);
      var modeStr = "";
      if(d.mode == DESIGNER_MODE_CONFIGURE) {
        modeStr="&for_product=1";
      }
      var om = '';
      if(d.inOrderManager) {
        om = '&om=1';
      }
      var ajax = new Ajax.Updater("decoration_libraries_container", d.ajaxUrl("/designer/get_library?uid=" + cCustomerId + "&process=" + processId + modeStr + om), {asynchronous:true, evalScripts:true, insertion: Insertion.Bottom}); 
    } else {
      decorationLibrary = this.libraries[processId][1];
      callback(decorationLibrary);
    }
  },
  
  registerLibrary: function DLM_registerLibrary(library, processId) {
    log("registerLibrary: " + processId);
    this.libraries[processId] = [cCustomerId, library];
    library.imageSelectionCallback = this.imageSelectorFunction; //track when a user selects an image...
    library.imageUploadedCallback = this.imageUploadedFunction; //track when a user uploads an image...
    library.categorySelectionCallback = this.categorySelectorFunction; //track when a user selects a category
    library.doubleClickImageCallback = this.doubleClickImageFunction; //track when user doublic clicks image...
    if(this.registerLibraryCallback != null) {
      this.registerLibraryCallback();
      this.registerLibraryCallback = null;
    }
  },
  
  close: function DLM_close() {
    $('image_details_' + decorationLibrary.process).style.display='none';
    closePopup("decoration_libraries");
    $("library_browser_" + decorationLibrary.process).style.display = "none";
    this.image = null;
  },
  
  selectImageInLibrary: function DLM_selectImageInLibrary(loading, image) {
    if(loading) {
		
      this.image = image;
      //$("image_details_" + this.currentProcessId).style.display="block";
							
      var dims = image.scale(250);
      var image_thumb = $("image_thumb_"+this.currentProcessId);
      if (image_thumb) image_thumb.style.display = "none";
      var aKey = asyncStart("image_container_"+this.currentProcessId);
      var self = this;
      queueImageLoading(image_thumb,image.url,dims[0],dims[1],
        function() {
          asyncFinish(aKey);
          image_thumb.style.display = "";
        }
      );
      
      //the following adds the image protection
      myqueue.add(image_thumb);
      
      $("existing_name_" + this.currentProcessId).innerHTML = image.name;
      var w = d.convertLengthFromInches(image.width / this.productAreaProcess.perfectDPI);
      var h = d.convertLengthFromInches(image.height / this.productAreaProcess.perfectDPI);
      $("decoration_size_field_" + this.currentProcessId).innerHTML = w.toFixed(2) + " x " + h.toFixed(2) + " " + d.getLengthUnit();
      //show declib price here
      if(image.decorationLibraryId == 0) {
        Element.hide("decoration_price_" + this.currentProcessId);
      } else {
        dec_price = d.chargeDecorationLibraries[image.decorationLibraryId];
        if(dec_price == null) {
          Element.hide("decoration_price_" + this.currentProcessId);
        } else {
          // only add markup if percent markup in use and not a custom product
          var percentMarkup = 1.0; //this leaves markup alone DNC-6587 (was 0 which would set price to $0 when fixed markup on site)
          var fixedMarkup = 0;
          if(d.currentCProduct.usingCustomProductWithFixedMarkup()) {
            // leave markup as %0
          } else if(d.defaultMarkupType == 0) {
            percentMarkup = d.currentCProduct.getPercentMarkup();
          }
          
          var wsPrice = (dec_price.range1price + (d.declibMarkup / 100) * dec_price.range1price);
          var rrpPrice = wsPrice + d.aMarkupRate * wsPrice;
          
          
          var price = priceFromCost(rrpPrice, d.commissionRate, percentMarkup, 0);
          price.round();
          if(d.mode == DESIGNER_MODE_CONFIGURE) {
            $("decoration_price_field_" + this.currentProcessId).innerHTML = "$" + price.wsCost;
          } else {
            $("decoration_price_field_" + this.currentProcessId).innerHTML = "$" + price.crp;
          }
          Element.show("decoration_price_" + this.currentProcessId);
        }
      }
	  						
      var decLibLi = $("dec_lib_li_"+image.uId);
      if(decLibLi != null) {
          var int_y_pos=get_page_ypos(decLibLi);
          var int_x_pos=get_page_pos(decLibLi);
          
          var offset_x_pos=get_page_pos($("decoration_libraries"));
          var offset_y_pos=get_page_ypos($("decoration_libraries"));
          //var offset_x_pos=get_page_pos($$("#decoration_libraries .interior")[0]);
          //var offset_y_pos=get_page_ypos($$("#decoration_libraries .interior")[0]);
          
          var tb_pos=get_page_pos($("decoration_list_container_div_"+this.currentProcessId))+$("decoration_list_container_div_"+this.currentProcessId).offsetWidth;
          var ep_pos=get_page_pos(decLibLi)+270;
          
          var diff=0;
          if(ep_pos>tb_pos){
            diff=tb_pos-ep_pos;
          }
          
          $("image_details_"+this.currentProcessId).style.width=eval(dims[0]+255)+"px";
          
          $("image_details_"+this.currentProcessId).style.display="block";
          $("image_details_"+this.currentProcessId).style.position="absolute";
          $("image_details_"+this.currentProcessId).style.left=parseInt(int_x_pos+diff-offset_x_pos)+"px";
          $("image_details_"+this.currentProcessId).style.top=parseInt(int_y_pos-offset_y_pos)+"px";
      }
      
    } else {
      this.image = null;
      $("image_details_" + this.currentProcessId).style.display="none";
      return true;
    }
  },
  
  doubleClickImage: function DLM_doubleClickImage(image) {
    this.image = image;
    $("image_details_" + this.currentProcessId).style.display="none";
    this.selectCurrentImage();
  },
  
  selectCategoryInLibrary: function DLM_selectCategoryInLibrary(loading, category) {
    $("image_details_" + this.currentProcessId).style.display="none";
    return true;
  },
  
  imageUploaded: function DLM_imageUploaded(image) {
    var dims = image.scale(350);
    var img = $("copyright_preview");
    img.width = dims[0];
    img.height = dims[1];
    img.src = image.url;
    var process = processes.byId[d.newProcessId];
    
    this.confirmImage = image;
    popup("confirm_image_upload");
  },
  
  setConfirmResult: function DLM_setConfirmResult(result) {
    if(result == 0) { //cancel
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/shared/library/set_copyright_result?id=" + this.confirmImage.id + "&result=" + result), {asynchronous:true, evalScripts:true});
      this.confirmImage.remove();
      $('copyright_preview').src = "/images/trans.gif";
      $('copyright_selected').checked = false;
      closePopup("confirm_image_upload");
    } else {
      if(!$("copyright_selected").checked) {
        alert(ml("Please tick the checkbox"));
        return;
      }
      $('copyright_preview').src = "/images/trans.gif";
      $('copyright_selected').checked = false;
      closePopup("confirm_image_upload");
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/shared/library/set_copyright_result?id=" + this.confirmImage.id + "&result=" + result), {asynchronous:true, evalScripts:true});
      this.image = this.confirmImage;
      this.selectCurrentImage();
    }
  },
  
  selectCurrentImage: function DLM_selectCurrentImage() {
    if(this.image == null) {
      //handle when reloaded....
      var lib = this.libraries[this.currentProcessId][1];
      if(lib.selectedCat != null && lib.selectedCat.selectedImage != null) {
        this.image = lib.selectedCat.selectedImage;
      }
      if(this.image == null) {
        alert(ml("Please select an image"));
        return;
      }
    }
    var img = this.image;
    this.close();
    this.selectImageCallback(img);
    
  }
  
});

/* added by jonathan 28/10/09 */

function get_page_pos(elem){
	var x_pos = elem.offsetLeft;
	temp_el = elem.offsetParent;
	try {
		while (temp_el != null) {
			int_x=temp_el.offsetLeft;
			if(int_x) x_pos += temp_el.offsetLeft;
			temp_el = temp_el.offsetParent;
		}
	}
	catch(ex){}
	return x_pos;
	//return 40;
}

function get_page_ypos(elem){
	var y_pos = elem.offsetTop;
	temp_el = elem.offsetParent;
	try {
		while (temp_el != null) {
			int_y=temp_el.offsetTop;
			if(int_y) y_pos += temp_el.offsetTop;
			temp_el = temp_el.offsetParent;
		}
	}
	catch(ex){}
	return y_pos;
	//return 40;
}


var TeamNameTemplates = Class.create({
  CLASSDEF: {
      name:  'TeamNameTemplates'
  },
  
  initialize: function TeamNameTemplates_initialize() {
    this.templatesByProcess = null;
  },
  
  loadTemplates: function teamNameTemplates_loadTemplates() {
    if(this.templatesByProcess !=null) return;
    this.templatesByProcess = {};
    this.allTemplates = {};
    for(var i=0; i < teamNameTemplates.length; i++) {
      var template = teamNameTemplates[i];
      var tmp = new TeamNameTemplate(template, this); //this will call register..
    }
  },
  
  getAllTemplates: function teamNameTemplates_getAllTemplates() {
    this.loadTemplates();
    return this.allTemplates;
  },
  
  register: function teamNameTemplates_register(template, processId) {
    if(this.templatesByProcess[processId] == null) {
      this.templatesByProcess[processId] = new MapList(this);
    }
    this.templatesByProcess[processId].add(template);
    this.allTemplates[template.id] = template;
  },
  
  buildHtml: function teamNameTemplates_buildHtml(processId) {
    this.loadTemplates();
    if(this.templatesByProcess[processId] == null) {
      return "No Templates Available";
    }
    var templates = this.templatesByProcess[processId];
    var html = [];
    this.selectedTemplateId = null;
    for(var i=0; i < templates.list.length; i++) {
      var template = templates.list[i];
      var clz = (i==0) ? ' class="alt"' : '';
      if(i==0) this.selectedTemplateId = template.id;
      var li = '<li' + clz + ' id="tnt_' + template.id + '" onclick="d.teamNameTemplates.select(' + template.id + ');"><img src="' + template.thumbnail + '" border="0"/></li>';
      html.push(li);
    }
    return '<ul>' + html.join('\n') + '</ul>';
  },
  
  select: function teamNameTemplates_select(templateId) {
    if(this.selectedTemplateId != null) {
      $('tnt_' + this.selectedTemplateId).className = "";
    }
    this.selectedTemplateId = templateId;
    $('tnt_' + this.selectedTemplateId).className = "alt";
  }
});


var TeamNameTemplate = Class.create({
  CLASSDEF: {
      name:  'TeamNameTemplate'
  },
  
  initialize: function TeamNameTemplate_initialize(config, templates) {
    this.id = config.id;
    this.templates = templates;
    this.name = config.n;
    this.config = config.c;
    this.thumbnail = config.t;
    for(var i=0; i < config.dp.length; i++) {
      templates.register(this, config.dp[i]);
    }
  }
});
  

var DFLAG_BASE = 1;
var DFLAG_TEXT_SHOW_DIMS = DFLAG_BASE;
var DFLAG_TEXT_SHOW_ADVANCED = DFLAG_BASE << 2;
var DFLAG_TEXT_SHOW_ADVANCED_OPEN = DFLAG_BASE << 3;
var DFLAG_IMG_SHOW_DIMS  = DFLAG_BASE << 4;
var DFLAG_IMG_SHOW_ADVANCED  = DFLAG_BASE << 5;
var DFLAG_IMG_SHOW_ADVANCED_OPEN = DFLAG_BASE << 6;

var DFLAG_SHOW_ROTATE_DEGREES = DFLAG_BASE << 7;

var DFLAG_SHOW_RULERS = DFLAG_BASE << 8;
var DFLAG_SHOW_RULERS_ON = DFLAG_BASE << 9;
var DFLAG_SHOW_GRID = DFLAG_BASE << 10;
var DFLAG_SHOW_GRID_ON = DFLAG_BASE << 11;
var DFLAG_SHOW_SAVE_DESIGN = DFLAG_BASE << 12;
var DFLAG_SHOW_EMAIL_DESIGN = DFLAG_BASE << 13;
var DFLAG_SHOW_ZOOM = DFLAG_BASE << 14;
var DFLAG_SHOW_COPY_PASTE = DFLAG_BASE << 15;

var DFLAG_SHOW_COPY_ITEM = DFLAG_BASE << 16;


var DFLAG_WARN_IF_OUTSIDE = DFLAG_BASE << 17;

var DFLAG_CLICK_AREA_SELECTS = DFLAG_BASE << 18;
var DFLAG_CLICK_AREA_ZOOMS = DFLAG_BASE << 19;
var DFLAG_SHOW_AREA_TOOLBAR = DFLAG_BASE << 20;

var DFLAG_SHOW_ADD_ITEM_INTRO = DFLAG_BASE << 21;

var DFLAG_DEFAULTS = DFLAG_TEXT_SHOW_DIMS + DFLAG_TEXT_SHOW_ADVANCED + DFLAG_IMG_SHOW_DIMS + DFLAG_IMG_SHOW_ADVANCED + 
     DFLAG_SHOW_ROTATE_DEGREES + DFLAG_SHOW_SAVE_DESIGN +  DFLAG_SHOW_EMAIL_DESIGN + DFLAG_SHOW_ZOOM + DFLAG_SHOW_COPY_PASTE +
     DFLAG_CLICK_AREA_SELECTS + DFLAG_CLICK_AREA_ZOOMS + DFLAG_SHOW_ADD_ITEM_INTRO;

var DesignerOptions = Class.create({
  CLASSDEF: {
      name: 'DesignerOptions'
  },
  
  initialize: function Do_initialize(flags) {
    this.flags = flags;
  },
  
  showTextDims: function() {
    return ((this.flags & DFLAG_TEXT_SHOW_DIMS) == DFLAG_TEXT_SHOW_DIMS);
  },
  
  showTextAdvanced: function() {
    return ((this.flags & DFLAG_TEXT_SHOW_ADVANCED) == DFLAG_TEXT_SHOW_ADVANCED);
  },

  showTextAdvancedOpen: function() {
    return ((this.flags & DFLAG_TEXT_SHOW_ADVANCED_OPEN) == DFLAG_TEXT_SHOW_ADVANCED_OPEN);
  },

  showImageDims: function() {
    return ((this.flags & DFLAG_IMG_SHOW_DIMS) == DFLAG_IMG_SHOW_DIMS);
  },

  showImageAdvanced: function() {
    return ((this.flags & DFLAG_IMG_SHOW_ADVANCED) == DFLAG_IMG_SHOW_ADVANCED);
  },
  
  showImageAdvancedOpen: function() {
    return ((this.flags & DFLAG_IMG_SHOW_ADVANCED_OPEN) == DFLAG_IMG_SHOW_ADVANCED_OPEN);
  },
  
  showRotateDegrees: function() {
    return ((this.flags & DFLAG_SHOW_ROTATE_DEGREES) == DFLAG_SHOW_ROTATE_DEGREES);
  },
  
  showRulers: function() {
    return ((this.flags & DFLAG_SHOW_RULERS) == DFLAG_SHOW_RULERS);
  },
  
  showRulersOn: function() {
    return ((this.flags & DFLAG_SHOW_RULERS_ON) == DFLAG_SHOW_RULERS_ON);
  },
  
  showGrid: function() {
    return ((this.flags & DFLAG_SHOW_GRID) == DFLAG_SHOW_GRID);
  },
  
  showGridOn: function() {
    return ((this.flags & DFLAG_SHOW_GRID_ON) == DFLAG_SHOW_GRID_ON);
  },

  showSaveDesign: function() {
    return ((this.flags & DFLAG_SHOW_SAVE_DESIGN) == DFLAG_SHOW_SAVE_DESIGN);
  },
  
  showEmailDesign: function() {
    return ((this.flags & DFLAG_SHOW_EMAIL_DESIGN) == DFLAG_SHOW_EMAIL_DESIGN);
  },
  
  showZoom: function() {
    return ((this.flags & DFLAG_SHOW_ZOOM) == DFLAG_SHOW_ZOOM);
  },
  
  showCopyPaste: function() {
    return ((this.flags & DFLAG_SHOW_COPY_PASTE) == DFLAG_SHOW_COPY_PASTE);
  },
  
  showCopyItem: function() {
    return ((this.flags & DFLAG_SHOW_COPY_ITEM) == DFLAG_SHOW_COPY_ITEM);
  },
  
  warnIfOutside: function() {
    return ((this.flags & DFLAG_WARN_IF_OUTSIDE) == DFLAG_WARN_IF_OUTSIDE);
  },
  
  clickAreaSelects: function() {
    return ((this.flags & DFLAG_CLICK_AREA_SELECTS) == DFLAG_CLICK_AREA_SELECTS);
  },
  
  clickAreaZooms: function() {
    return ((this.flags & DFLAG_CLICK_AREA_ZOOMS) == DFLAG_CLICK_AREA_ZOOMS);
  },
  
  showAreaToolbar: function() {
    return ((this.flags & DFLAG_SHOW_AREA_TOOLBAR) == DFLAG_SHOW_AREA_TOOLBAR);
  },
  
  showAddItemIntro: function() {
    return ((this.flags & DFLAG_SHOW_ADD_ITEM_INTRO) == DFLAG_SHOW_ADD_ITEM_INTRO);
  }
});


var Price = Class.create({
  CLASSDEF: {
      name: 'Price'
  },
  
  initialize: function P_initialize(cost, wsCost, rrp, crp, override, isOverriden, legacy) {
    this.cost = cost == null ? 0.0 : cost;
    this.wsCost = wsCost == null ? 0.0 : wsCost; 
    this.rrp = rrp == null ? 0.0 : rrp; 
    this.crp = rrp; //crp == null ? 0.0 : crp; 
    this.override = override;
    this.legacy = legacy; //this is a legacy amount when loading existing order or quote still in valid period.. overrides everything else....
    this.isOverriden = isOverriden == null ? false : isOverriden;
  },
  
  copy: function P_copy() {
    return new Price(this.cost, this.wsCost, this.rrp, this.crp, this.override, this.isOverriden, this.legacy);
  },
  
  add: function P_add(otherPrice) {
    var leg = null;
    if(this.legacy != null || otherPrice.legacy != null) {
      leg = this.getLegacy() + otherPrice.getLegacy();
    }
    
    return new Price(this.cost + otherPrice.cost, this.wsCost + otherPrice.wsCost, this.rrp + otherPrice.rrp, this.crp + otherPrice.crp, this.getOverride() + otherPrice.getOverride(), this.isOverriden || otherPrice.isOverriden, leg);
  },
  
  minus: function P_minus(otherPrice) {
    var leg = null;
    if(this.legacy != null || otherPrice.legacy != null) {
      leg = this.getLegacy() - otherPrice.getLegacy();
    }
    
    return new Price(this.cost - otherPrice.cost, this.wsCost - otherPrice.wsCost, this.rrp - otherPrice.rrp, this.crp - otherPrice.crp, this.getOverride() - otherPrice.getOverride(), this.isOverriden || otherPrice.isOverriden, leg);
  },
  
  markup: function P_markup(percent) {
    percent = 1.0 + (percent / 100.0);
    var leg = null;
    if(this.legacy != null) {
      leg = this.legacy * percent;  
    }
    return new Price(this.cost * percent, this.wsCost * percent, this.rrp * percent, this.crp * percent, this.getOverride() * percent, this.isOverriden, leg);
  },
  
  divide: function P_divide(div) {
    var leg = null;
    if(this.legacy != null) {
      leg = this.legacy / div;  
    }
    return new Price(this.cost / div, this.wsCost / div, this.rrp / div, this.crp / div, this.getOverride() / div, this.isOverriden, leg);
  },
  
  multiply: function P_multiply(mult) {
    var leg = null;
    if(this.legacy != null) {
      leg = this.legacy * mult;  
    }
    return new Price(this.cost * mult, this.wsCost * mult, this.rrp * mult, this.crp * mult, this.getOverride() * mult, this.isOverriden, leg);
  },
  
  round: function p_round() {
    this.cost = d.round(this.cost);
    this.wsCost = d.round(this.wsCost);
    this.rrp = d.round(this.rrp);
    this.crp = d.round(this.crp);
    this.override = d.round(this.getOverride());
    if(this.legacy != null) {
      this.legacy = d.round(this.legacy);
    }
  },
  
  //get the override amount, falling back to legacy then crp if null...
  getOverride: function() {
    if(this.isOverriden && this.override != null) {
      return this.override;
    }
    return this.getLegacy();
  },
  
  //get legacy value, failling back to crp if null
  getLegacy: function() {
    if(this.legacy == null) {
      return this.crp;
    }
    return this.legacy; 
  },
  
  //get the amount that will be charged...
  getBillAmount: function() {
    return this.getOverride();  
  },
  
  toString: function() {
    return "[cost:" + this.cost + ", wsCost:" + this.wsCost + ", rrp:" + this.rrp + ", crp:" + this.crp + ", override:" + this.override + ", isOverriden:" + this.isOverriden + ", legacy:" + this.legacy + "]";
  }
  
});

function priceFromCost(rrp, commissionRate, percentMarkup, fixedMarkup, override, isOverriden, legacy) {
  var cost = rrp * (100.0 - commissionRate) / 100.0;
  var wsCost = cost * d.wsRate;
  var rrp = d.round(wsCost * percentMarkup + fixedMarkup);
  //var crp = rrp - d.round(rrp * cDisc / 100.0);
  var crp = rrp;
  return new Price(cost, wsCost, rrp, crp, override, isOverriden, legacy);
}

function priceFromRRP(rrp, includeCost, percentMarkup, fixedMarkup) {
  var wsCost = d.round((rrp - fixedMarkup) / percentMarkup);
  var cost = (includeCost) ? d.round(wsCost / d.wsRate) : 0;
  var crp = rrp;
  return new Price(cost, wsCost, rrp, crp);
}

function priceFromRP(rp, commissionRate, percentMarkup, fixedMarkup, override, isOverriden, legacy) {
  var wsCost = (rp - fixedMarkup) * (1.0 / percentMarkup);
  var rrp = wsCost / (100.0 - commissionRate) * 100.0;
  var cost = d.round(wsCost / d.wsRate);
  var crp = rrp;
  return new Price(cost, wsCost, rrp, crp, override, isOverriden, legacy);
}

function rpToRRP(rp, commissionRate, percentMarkup, fixedMarkup) {
  var wsCost = (rp - fixedMarkup) * (1.0 / percentMarkup);
  var rrp = wsCost / (100.0 - commissionRate) * 100.0;
  return rrp;
}

function priceZero() {
  return new Price(0, 0, 0, 0, null, false, null);
}

// prority/smart queue for ajax requests.. stop thumbnail update interfering with save/add to cart...
var AjaxQueueManager = Class.create({
  CLASSDEF: {
      name: 'AjaxQueueManager'
  },
  
  initialize: function AQM_initialize() {
    this.queues = {};
  },
  
  queueRequest: function AQM_queueRequest(key, mode, options) {
    var q = this.getQueue(key);
    q.addRequest(mode, options);
  },
  
  getQueue: function AQM_getQueue(key) {
    var q = this.queues[key];
    if(q == null) {
      q = new AjaxQueue(this);
      this.queues[key] = q;
    }
    return q;
  }
});

var ajaxQueueManager = new AjaxQueueManager();

var AjaxQueue = Class.create({
  CLASSDEF: {
      name: 'AjaxQueue'
  },
  
  initialize: function AQ_initialize() {
    this.queue = [];
    this.running = null;
  },
  
  addRequest: function AQ_addRequest(mode, options) {
    if(this.running == null) {
      this.run(options);
    } else {
      if(mode == 1) { //1 = remove existing...
        log("AjaxQueue: mode == 1, clearing exisitng queue");
        this.queue = [];
      } else if(mode == 2) { //2 = use existing (dont queue if already running)
        log("AjaxQueue: mode == 2, skipping request");
        return;
      } else if(mode == 3) {// 3 = remove existing with same subkey
        var sKey = options.subKey;
        log("AjaxQueue: mode == 1, Clearing existing with subKey=" + sKey);
        var i = 0;
        while(i < this.queue.length) {
          var otherSubKey = this.queue[i].subKey;
          if(otherSubKey == sKey) {
            log("Found Existing Queued Item with subKey=" + otherSubKey);
            this.queue.splice(i,1);
          } else {
            i++;
          }
        }
      } else {
        log("AjaxQueue: mode == 0, queueing request");
      }
      this.queue.push(options);
    }
  },
  
  run: function AQ_run(options) {
    log("Running Ajax Queue Item for " + options.url);
    this.running = options;
    var self = this;
    var onComplete = options.options.onComplete;
    //override the onComplete to queue requests
    options.options.onComplete = function(transport, jsonResult) {
      if(onComplete != null) { //call the original onComplete
        onComplete(transport, jsonResult);
      }
      self.running = null;
      self.runNext();
    };
    
    if(options.parameters != null) { //we have a function which makes the params...
      options.options.parameters = options.parameters();
    }
    
    if(options.mode == 0) { //request
      var req = new Ajax.Request(d.ajaxUrl(options.url), options.options);
    } else if(options.mode == 1) { //updater
      var req = new Ajax.Updater(options.target, d.ajaxUrl(options.url), options.options);
    }
  },
  
  runNext: function AQ_runNext() {
    var next = this.queue.shift();
    if(next != null) {
      log("Running Next Ajax Queue Item");
      this.run(next);
    }
  }
});

var PEffectControl = Class.create({
    CLASSDEF: {
        name: 'PEffectControl'
    },
  
  initialize: function PEC_initialize(caption, code, effect, props, callback, iId) {
    this.caption = caption;
    this.code = code;
    this.effect = effect;
    this.props = props;
    this.id = effect[0] + "_" + code + "_" + iId;
    this.callback = callback;
  },
  
  buildHTML: function PEC_buildHTML() {
    return  '<label>'+this.caption+'</label>'+this.buildControlHTML();
  },
  
  bindControls: function PEC_bindControls() {
    
  },
  
  remove: function PEC_remove() {
    
  },
  
  loadFromState: function PEC_loadFromState(state) {
    
  },
  
  saveToState: function PEC_saveToState(state) {
    
  },
  
  validateInput: function PEC_validateInput(inputEl, defaultVal,callback) {
    if(inputEl.value=="") {
      inputEl.value = defaultVal;
      log("fixing input (blank)");
      callback();
    } else if(parseInt(inputEl.value, 10)==NaN) {
      inputEl.value = defaultVal;
      log("fixing input (NaN)");
      callback();
    } else if(parseInt(inputEl.value, 10)!=inputEl.value) {
      log("fixing input (neq)");
      inputEl.value = defaultVal;
      callback();
    }
  },
  
  onChanged: function PEC_onChanged() {
    this.callback();
  }
});

var PTextboxControl = Class.create({
    CLASSDEF: {
        name: 'PTextboxControl',
    parent: PEffectControl
    },
  
  initialize: function PTC_initialize(caption, code, effect, props, callback, iId) {
    PTextboxControl.parentClass.constructor().call(this,caption, code, effect, props, callback, iId);
  },
  
  buildControlHTML: function PTC_buildControlHTML() {
    var size = "1";
    if(this.props["s"]!=null) {
      size = this.props["s"];
    }
    return "<input type=\"text\" class=\"effectspanelfield\" name=\"" + this.id + "_ctl\" size=\"" + size + "\" value=\"\" id=\"" + this.id + "\" />";
    //return "<input name=\"" + this.id + "_ctl\" size=\"" + size + "\" value=\"" + def + "\" id=\"" + this.id "\"/>";
  },
  
  bindControls: function PTC_bindControls() {
    var _this = this;
    this.textbox = $(this.id);
    this.textbox.onkeyup = function() {
      _this.value = _this.textbox.value;
      _this.onChanged();
    };
  },
  
  loadFromState: function PTC_loadFromState(state, allowDefault) {
    var val = state[this.code];
    if(allowDefault && val == null) {
      val = this.props.d;
    }
    this.textbox.value = val;
  },
  
  saveToState: function PTC_saveToState(state) {
    state[this.code] = this.textbox.value;
  }
});

var PSliderControl = Class.create({
    CLASSDEF: {
        name: 'PSliderControl',
    parent: PEffectControl
    },
  
  initialize: function PSC_initialize(caption, code, effect, props, callback, iId) {
    PSliderControl.parentClass.constructor().call(this,caption, code, effect, props, callback, iId);
  },
  
  buildControlHTML: function PSC_buildControlHTML() {
	  return '<div id="' + this.id + '_t" style="width: 88px;" class="et_bar"><div id="' + this.id + '_s" style="" class="et_grab"> </div></div>';
  },
  
  bindControls: function PSC_bindControls() {
    var _this = this;
    this.slider = new Control.Slider(this.id + '_s',this.id + '_t', {
      range:this.props["r"],
      values:this.props["v"],
      sliderValue:this.props["d"],
      onChange: function PSC_onChange(v) {
        _this.slideValue = v;
        _this.onChanged();
      }
    });
  },
  
  loadFromState: function PSC_loadFromState(state, allowDefault) {
    var val = state[this.code];
    if(allowDefault && val == null) {
      val = this.props.d;
    }
    this.slideValue = val;
    this.slider.setValue(val);
  },
  
  saveToState: function PSC_saveToState(state) {
    state[this.code] = this.slideValue;
  },
  
  remove: function PSC_remove() {
    if(this.slider!=null) {
      this.slider.dispose();
      this.slider = null;
    }
  }
});

var PColorSliderControl = Class.create({
    CLASSDEF: {
        name: 'PColorSliderControl',
    parent: PSliderControl
    },
  
  initialize: function PCSC_initialize(caption, code, effect, props, callback, iId) {
    PColorSliderControl.parentClass.constructor().call(this,caption, code, effect, props, callback, iId);
  },
  
  buildControlHTML: function PCSC_buildControlHTML() {
    return '<div id="' + this.id + '_t" style="width:50px;background-color:#aaa;height:10px;background-image:url(' + d.pathPrefix + '/images/colors.gif);"><div id="' + this.id + '_s" style="width:3px;height:8px;cursor:e-resize;border:1px solid black;"> </div></div>';
  }
});
      
var PCheckboxControl = Class.create({
    CLASSDEF: {
        name: 'PCheckboxControl',
    parent: PEffectControl
    },
  
  initialize: function PCC_initialize(caption, code, effect, props, callback, iId) {
    PCheckboxControl.parentClass.constructor().call(this,caption, code, effect, props, callback, iId);
  },
  
  buildControlHTML: function PCC_buildControlHTML() {
    return '<input type="checkbox" class="effectspanelfield" name="' + this.id + '_ctl" value="1" id="' + this.id + '" />';
  },
  
  bindControls: function PCC_bindControls() {
    var _this = this;
    this.checkbox = $(this.id);
    this.checkbox.onclick = function() {
      if(_this.checkbox.checked) {
        _this.value = "1";
      } else {
        _this.value = "0";
      }
      _this.onChanged();
    };
  },
  
  loadFromState: function PCC_loadFromState(state, allowDefault) {
    this.value = state[this.code];
    if(allowDefault && this.value == null) {
      this.value = this.props.d;
    }
    if(this.value == "1") {
      this.checkbox.checked = true;
    } else {
      this.checkbox.checked = false;
    }
  },
  
  saveToState: function PCC_saveToState(state) {
    state[this.code] = this.value;
  }
});

var PSelectControl = Class.create({
    CLASSDEF: {
        name: 'PSelectControl',
    parent: PEffectControl
    },
  
  initialize: function PSBC_initialize(caption, code, effect, props, callback, iId) {
    PSelectControl.parentClass.constructor().call(this,caption, code, effect, props, callback, iId);
    this.value = "";
  },
  
  buildControlHTML: function PSBC_buildControlHTML() {
    var html = '<select id="' + this.id + '" class="et_effect_control_s">';
    var opts = this.props["v"];
    for(var i=0;i<opts.length;i++) {
      html += '<option value="' + opts[i][1] + '">' + opts[i][0] + '</option>';
    }
    html += '</select>';
    return html;
  },
  
  bindControls: function PSBC_bindControls() {
    this.select = $(this.id);
    this.setupListener();
  },
  
  setupListener: function PSBC_setupListener() {
    var _this = this;
    this.select.onchange = function() {
      _this.value = _this.select.value;
      _this.onChanged();
    };
  },
  
  loadFromState: function PSBC_loadFromState(state, allowDefault) {
    this.value = state[this.code];
    if(allowDefault && this.value == null) {
      this.value = this.props.d;
    }
    this.select.value = this.value;
  },
  
  saveToState: function PSBC_saveToState(state) {
    state[this.code] = this.value;
  }
});


var PRotateControl = Class.create({
    CLASSDEF: {
        name: 'PRotateControl',
    parent: PEffectControl
    },
  
  initialize: function PRC_initialize(caption, code, effect, props, callback, iId) {
    PRotateControl.parentClass.constructor().call(this,caption, code, effect, props, callback, iId);
  },
  
  buildControlHTML: function PRC_buildControlHTML() {
    this.value = 0;
    if(this.props["d"]!=null) {
      this.value = this.props["d"];
    }
    return '<div style="position:relative;width:36px;height:36px;" id="' + this.id + '_co"><img style="position:absolute;top:0px;left:0px;" src="/images/mp/circle_with_line.gif" id="' + this.id + '_c" border="0" style="margin-bottom:0px;" align="absmiddle"/></div>';
  },
  
  bindControls: function PRC_bindControls() {
    
    var _this = this;
    
    this.rotateContainer = $(this.id + "_co");
    this.rotateImg = $(this.id + "_c");
    this.rotateImg.style.cursor = "pointer";
  
    
    this.rotateWidget = new  RotateWidget(this.rotateContainer, this.rotateImg, null, null, function(d) {
        log("PRotateControl.changed");
      _this.value = d;
      _this.onChanged();
    });
  },
  
  loadFromState: function PRC_loadFromState(state, allowDefault) {
    this.value = state[this.code];
    if(allowDefault && this.value == null) {
      this.value = this.props.d;
    }
    this.rotateWidget.setValue(parseInt(this.value, 10));
  },
  
  saveToState: function PRC_saveToState(state) {
    state[this.code] = this.value;
  },
  
  remove: function PRC_remove() {
    if(this.rotateWidget!=null) {
      this.rotateWidget.del();
    }
  }
});


var RotateWidget = Class.create({
    CLASSDEF: {
        name: 'RotateWidget'
    },
  
  initialize: function RW_initialize(container, rotationWidget, l, r, t, callback, instantCallback) {
    this.container = container;
    this.rotationWidget = rotationWidget;
    this.l = l;
    this.r = r;
    this.t = t;
    this.callback = callback;
    this.instantCallback = instantCallback;
    
    this.rotCanvas = document.createElement("DIV");
    this.rotCanvas.id = "rw_" + d.getNextId();
    //this.rotCanvas.style.position = "absolute";
    //this.rotCanvas.style.width = "66px";
    //this.rotCanvas.style.height = "50px";
    this.container.appendChild(this.rotCanvas);
    this.jg = new jsGraphics(this.rotCanvas);
    
    
    
    
    this.rWidth = 36;//this.rotationWidget.width;
    this.rHeight = 36;//this.rotationWidget.height;
    
    this.rOffsetX = parseInt(this.rotationWidget.style.left, 10) -1;
    this.rOffsetY = parseInt(this.rotationWidget.style.top, 10) - 1;
    
    this.eventStartRotation =  this.startRotation.bindAsEventListener(this);
    this.eventRotateMove =  this.rotateMove.bindAsEventListener(this);
    this.eventRotateFinish =  this.rotateFinish.bindAsEventListener(this);
    Event.observe(this.rotationWidget, "mousedown", this.eventStartRotation);
    
    this.handle = document.createElement("IMG");
    this.handle.src = "/ppr/images/sizer.gif";
    this.handle.id = "rwh_" + d.getNextId();
    this.handle.style.position = "absolute";
    this.handle.style.cursor="pointer";
    this.handle.style.top = (this.rOffsetY - 3) + "px";
    this.handle.style.left = (this.rOffsetX + this.rWidth/2 - 3) + "px";
    this.handle.style.zIndex = 2999; // below 3000 so popup goes above it
    this.container.appendChild(this.handle);
    
    Event.observe(this.handle, "mousedown", this.eventStartRotation);
    
    
    if(l!=null) {
      this.rotateLeftClickEvent = this.rotateLeftClick.bindAsEventListener(this);
      this.rotateDblLeftClickEvent = this.rotateDblLeftClick.bindAsEventListener(this);
      Event.observe(this.l, "click", this.rotateLeftClickEvent);
      Event.observe(this.l, "dblclick", this.rotateDblLeftClickEvent);
    }
    
    if(r!=null) {
      this.rotateRightClickEvent = this.rotateRightClick.bindAsEventListener(this);
      this.rotateDblRightClickEvent = this.rotateDblRightClick.bindAsEventListener(this);
      Event.observe(this.r, "click", this.rotateRightClickEvent);
      Event.observe(this.r, "dblclick", this.rotateDblRightClickEvent);
    }
    if(t != null) {
      this.rotateTxtKeyUp = this.rotateTxtKeyUp.bindAsEventListener(this);
      Event.observe(this.t, "keyup", this.rotateTxtKeyUp);
      Event.observe(this.t, "blur", this.rotateTxtKeyUp);
    }
    
    
    this.zeroRotImage = true;
    
    this.value = 0;
    
    
  },
  
  del: function RW_del() {
    if(this.rotationWidget != null) {
      Event.stopObserving(this.rotationWidget, "mousedown", this.eventStartRotation);
      
      if(this.rotateDblLeftClickEvent!=null) {
        Event.stopObserving(this.l, "click", this.rotateLeftClickEvent);
        Event.stopObserving(this.l, "dblclick", this.rotateDblLeftClickEvent);
      }
      if(this.rotateDblRightClickEvent!=null) {
        Event.stopObserving(this.r, "click", this.rotateRightClickEvent);
        Event.stopObserving(this.r, "dblclick", this.rotateDblRightClickEvent);
      }
      if(this.rotateTxtKeyUp!=null) {
        Event.stopObserving(this.t, "keyup", this.rotateTxtKeyUp);
        Event.stopObserving(this.t, "blur", this.rotateTxtKeyUp);
      }
    }
  },
  
  startRotation: function RW_startRotation(event) {
    log("start rotation");
    Event.stop(event);
    //start tracking mouse movement
    Event.observe(document, "mousemove", this.eventRotateMove);
    Event.observe(document, "mouseup", this.eventRotateFinish);
    
    //store the current location of the rotation widget center
  
    var pos = Position.cumulativeOffset(this.rotationWidget);
    this.rotXPos = pos[0] + this.rWidth/2;
    this.rotYPos = pos[1] + this.rHeight/2;
    
    return true;
  },
  
  rotateMove: function RW_rotateMove(event) {
    //determine the angle from center of rotation widget to the mouse
    Event.stop(event);

    //first get size of the sides
    var mxPos = Event.pointerX(event);
    var myPos = Event.pointerY(event);
    
    var side1 = parseFloat(mxPos - this.rotXPos); //horiz
    var side2 = parseFloat(myPos - this.rotYPos); //vert
    var side3 = Math.sqrt((side1*side1) + (side2*side2)); //slope
    
    var offset = 0;
    if(side2 < 0) { //above
      if(side1 < 0) { //left
        offset = 270;
      } else { //right
        offset = 90;
      }
    } else { //below
      if(side1 < 0) { //left
        offset = 270;
      } else { //right
        offset = 90;
      }
    }
    
    var rads = Math.atan(side2/side1);
    var degs = this.radToDeg(rads);
    
    //round to closest 1 deg
    
    degs = parseInt(degs, 10);

    this.value = degs + offset;
    this.displayRotation(this.value, false, false);
    if(this.instantCallback != null) {
      this.instantCallback(this.value);
    }
    return true;
    //window.status = "wx=" + this.rot_x_pos + " wy=" + this.rot_y_pos + "mx=" + mxPos + " my=" + myPos + " side1=" + side1 + " side2=" + side2 + " rads=" + rads + " degs=" + degs; 
     
  },
  
  externalSet: function RW_externalSet(degs) {
    if(degs < 0) {
      degs += 360; 
    } else if(degs >= 360) {
      degs -= 360; 
    }
    this.value = degs;
    this.displayRotation(this.value, false, false);
    if(this.instantCallback != null) {
      this.instantCallback(this.value);
    }
  },
  
  setValue: function RW_setValue(angle) {
    this.value = parseFloat(parseInt(angle, 10));
    this.displayRotation(angle,true,true);
  },
  
  displayRotation: function RW_displayRotation(angle, calc_center, finished) {
    angle = parseFloat(parseInt(angle, 10));
    if(this.jg==null) {
      return;
    }
    if(this.t != null) {
      this.t.value = angle;
    }
    
    
    if(calc_center) {
      var pos = Position.cumulativeOffset(this.rotationWidget);
      this.rotXPos = pos[0] + this.rWidth/2;
      this.rotYPos = pos[1] + this.rHeight/2;
    }
    
    


    rotXPos = this.rOffsetX + this.rWidth/2;
    rotYPos = this.rOffsetY + this.rHeight/2;
    
    //we know the center point (rot_?_pos), the elevation (angle) and the length (this.rotation_widget.width/2)...
    var length = this.rWidth/2 - 2;
    if(this.zeroRotImage) {
      //if(this.rotation_widget.src != pp.path_prefix + d.pathPrefix + "/images/circle_without_line.gif") {
      this.zeroRotImage = false;
      this.rotationWidget.src =  d.pathPrefix + "/images/mp/rotate_circle.png";
    }
    
    var rads = this.degToRad(angle - 90);
    
    //sin ? = opp / hyp ... opp = sin ? * hyp
    var y = Math.sin(rads) * length;
    //cos ? = adj / hyp ... adj = cos ? * hyp
    var x = Math.cos(rads) * length;
    
    this.handle.style.left = (x + rotXPos - 3) + "px";
    this.handle.style.top = (y + rotYPos - 3) + "px";
    
    if((finished) && (angle==0)) {
      this.jg.clear();
      this.rotationWidget.src = d.pathPrefix + "/images/mp/rotate_circle_with_line.png";
      this.zeroRotImage = true;
      return;
    }
    
    //draw a line using the supplied angle from the center to the rotation widget to the circle
    this.jg.clear();
    
    this.jg.setZIndex(2998);
    
    //this.jg.drawEllipse(x + this.rot_x_pos - 5, y + this.rot_y_pos -5, 10,10);
    this.jg.setStroke(1);
    this.jg.setColor("#777777");
    //this.jg.setColor("#FF0000");
    
    //log("drawLine(" + angle + ":" + (rotXPos + 1) + "," +  (rotYPos + 1) + "," +  (x + rotXPos + 1) + "," + (y + rotYPos+ 1) + ");");
    
    this.jg.drawLine(rotXPos + 1, rotYPos + 1, x + rotXPos + 1, y + rotYPos+ 1);
    this.jg.paint();
    
    
    
    
    //log(this.container.innerHTML);
    
  },
  
  degToRad: function (angle) {
    return ((angle*Math.PI) / 180.0);
  },

  radToDeg: function RW_radToDeg(angle) {
    return ((angle*180.0) / Math.PI);
  },
  
  rotateFinish: function RW_rotateFinish(event) {
    Event.stopObserving(document, "mousemove", this.eventRotateMove);
    Event.stopObserving(document, "mouseup", this.eventRotateFinish);
    this.displayRotation(this.value, false, true);
    this.callback(this.value);
    //d.itemChanged(true);
  },
  
  externalRotateFinish: function RW_rotateFinish() {
    this.displayRotation(this.value, false, true);
    this.callback(this.value);
  },
  
  rotateLeftClick: function RW_rotateLeftClick(event) {
    this.doRotate(-5.0);
  },
  
  rotateRightClick: function RW_rotateRightClick(event) {
    this.doRotate(5.0);
  },
  
  rotateDblLeftClick: function RW_rotateDblLeftClick(event) {
    this.doRotate(-10.0);
  },
  
  rotateDblRightClick: function RW_rotateDblRightClick(event) {
    this.doRotate(10.0);
  },
  
  doRotate: function RW_doRotate(amount) {
    this.value += amount;
    //round to 5 deg
    this.value = parseFloat(parseInt(this.value / 5, 10)) * 5.0;
    if(this.value < 0) {
      this.value += 360;
    } else if(this.value >= 360) {
      this.value -= 360;
    }
    this.displayRotation(this.value, false, true);
    this.callback(this.value);
    if(this.instantCallback != null) {
      this.instantCallback(this.value);
    }
    //d.itemChanged(true);
  },
  
  rotateTxtKeyUp: function(event) {
    if(!isNaN(parseInt(this.t.value, 10))) {
      this.value = parseFloat(parseInt(this.t.value, 10));
      this.displayRotation(this.value, false, true);
      this.callback(this.value);
      if(this.instantCallback != null) {
        this.instantCallback(this.value);
      }
    }
  }
  
  
});


var controlClasses = {
  "S": PSliderControl,
  "CS": PColorSliderControl,
  "T": PTextboxControl,
  "R": PRotateControl,
  "CB": PSelectControl,
  "CH" : PCheckboxControl
};

var Item = Class.create({
  CLASSDEF: {
      name: 'Item'
  },
  
  initialize: function Item_initialize(id, cViewProcess, asset, options) {
    this.id = id;
    this.asset = asset;
    this.options = options;
    this.enabled = true;
    this.setLocked(dopt(options, "lk", "false") == "true");
    this.cViewProcess = cViewProcess;
    this.cViewArea = cViewProcess.configuredViewArea;
    this.cView = this.cViewArea.configuredView;
    this.ready = false;
    
    this.isWilcomEMB = (options.emb == "1");
    //if(!d.inOrderManager) {
      this.isSavedDigitized = (this.asset.digitizedState > 1);
    //}
    this.stitchCount = options.stc;
    
    this.elId = this.cView.configuredProduct.id + "_" + this.id;
    
    this.reRender = false;
    this.renderVersion = (options.rv == null) ? 0 : options.rv;
    
    this.allowHandleResizing = true;
    
    this.useCanvas = useCanvas;
    
    this.managePaneBound = false;
    if(options.z) {
      this.zIndex = options.z;
    } else {
      this.zIndex = this.cViewProcess.getNextZOrder();
    }
    
    if((this.options.l)&&(this.options.t)&&(this.options.w)&&(this.options.h)) { //options contains positioning data
      this.height = parseFloat(this.options.h);
      this.width = parseFloat(this.options.w);
      this.left = parseFloat(this.options.l);
      this.top = parseFloat(this.options.t);
    } else if((!this.startPosition())||((this.options.w)&&(this.options.h))) { //the derived object doesnt have special start positon logic (Text) or it alreeady has a width/height (copy text)
      var w =  this.cViewProcess.productProcess.fullWidth;
      var h = this.cViewProcess.productProcess.fullHeight;
      if(this.options.w == null) {
        if((!this.isPlaceholder()) && ((this.isWilcomEMB)||(this.isSavedDigitized)) ) {
          this.height = parseInt(this.asset.height, 10);
          this.width = parseInt(this.asset.width, 10);
        } else {
          //auto position
          //by default this will position itself in the largest centered position allowed.
          log("rW=" + w + ", rH=" + h);
          var dims = this.asset.scale(h,w);
          dims = this.minDims(dims);
          this.maintainAspect = true;
          this.height = parseInt(dims.h, 10);
          this.width = parseInt(dims.w, 10);
        }
      } else {
        //does have size data (no position)
        this.height = parseFloat(this.options.h);
        this.width = parseFloat(this.options.w);
      }
      if(this.options.t == null) {
        this.top = (h - this.height) / 2;
        this.left = (w - this.width) / 2;
      } else {
        this.top = this.options.t;
        this.left = this.options.l;
      }
    }
    
    this.deleteClickEvent = this.deleteClick.bindAsEventListener(this);
    this.zDownClickEvent = this.zDownClick.bindAsEventListener(this);
    this.zUpClickEvent = this.zUpClick.bindAsEventListener(this);
    
    this.eventResizeStart =  this.resizeStart.bindAsEventListener(this);
    this.eventResizeMouseUp =  this.resizeFinished.bindAsEventListener(this);
    this.eventResizeMouseMove =  this.resizeMove.bindAsEventListener(this);
    
    this.eventRotatorStart = this.rotatorStart.bindAsEventListener(this);
    this.eventRotatorMouseUp = this.rotatorFinished.bindAsEventListener(this);
    this.eventRotatorMouseMove =  this.rotatorMove.bindAsEventListener(this);
    
    this.eventMouseDown =  this.mouseDown.bindAsEventListener(this);
    
    this.eventLockToggle = this.lockToggle.bindAsEventListener(this);
    
    this.isLastZ = false;
    this.isFirstZ = false;
    this.qualityOK = true;
    
    if(options.ar == null) {
      if( (this.width / this.height) ==  this.asset.aspectRatio) {
        this.maintainAspect = true;
      } else {
        this.maintainAspect = false;
      }
    } else {
      this.maintainAspect = (options.ar == "1");
    }
    this.allowResize = true;
    
    this.alerts = [{},{},{}]; //array of alerts: error/warning/notice
    
    this.canModifyDesign = this.cViewProcess.productProcess.canModifyDesign();
  },
  
  compare: function Item_compare(other) {
    return this.zIndex - other.zIndex;
  },
  
  beforeAddNew: function Item_beforeAddNew() {
    return false;
  },
  
  continueAdd: function Item_continueAdd() {
    
  },
  
  isPlaceholder: function() {
    return false;
  },
  
  initialiseManagePane: function Item_initialiseManagePane() {
    var self = this;
    log("Item.initialiseManagePane()");
    if ($('mp1_' + this.elId)) $('mp1_' + this.elId).remove();
    this.cViewProcess.buildManagePane(this);
    this.managePane = $("mp1_" + this.elId);
    this.titleThumb = $("mp1_" + this.elId + "_stimg");
    
    var sizes = this.asset.scale(15,20);
    
    setTransPng(this.titleThumb, this.asset.url, parseInt(sizes.w, 10), parseInt(sizes.h, 10));
    //this.titleThumb.src = this.asset.url;
    //this.titleThumb.width = parseInt(sizes.w, 10);
    //this.titleThumb.height = parseInt(sizes.h, 10);
    
    this.titleBar = $("mp1_" + this.elId + "_bar");
    this.titleBar.onclick = function(ev) { self.select(); };
    
    this.titleSpan = $("mp1_" + this.elId + "_title");
    
    this.titleSize = $("mp1_" + this.elId + "_dim_value");
    this.initFeature("dim");
    this.updateTitleSize();
    
    this.titleZUp = $("mp1_" + this.elId + "_zup");
    Event.observe(this.titleZUp, "click", this.zUpClickEvent);
    this.titleZUp.style.cursor = "pointer";
    
    this.titleZDown = $("mp1_" + this.elId + "_zdown");
    Event.observe(this.titleZDown, "click", this.zDownClickEvent);
    this.titleZDown.style.cursor = "pointer";
    
    this.titleDel = $("mp1_" + this.elId + "_remove");
    Event.observe(this.titleDel, "click", this.deleteClickEvent);
    this.titleDel.style.cursor = "pointer";
    
    if(!this.canModifyDesign) {
      this.titleDel.hide();
    }
    
    this.lockContainer = $("mp1_" + this.elId + "_lock_container");
    this.lockCheckBox = $("mp1_" + this.elId + "_lock");
	this.lockIcon=$("mp1_"+this.elId+"_lock_icon");
    this.lockCheckBox.checked = this.locked;
	
	if(this.locked) this.lockIcon.style.visibility="visible";
	else this.lockIcon.style.visibility="hidden";
	
    Event.observe(this.lockCheckBox, "click", this.eventLockToggle);
    if (d.mode == DESIGNER_MODE_CONFIGURE) {
      this.lockContainer.style.display = "";
    } else {
      this.lockContainer.style.display = "none";
	  this.lockIcon.style.display="none";
    }
    
    this.managePaneBody = $("mp1_" + this.elId + "_body");
    
    this.setName(this.name);
    
    //this.iconContainer0 = $("mp0_" + this.id + "_ico_c");
    this.iconContainer1 = $("mp1_" + this.elId + "_ico_c");
    
    this.alertIcon = $("mp1_" + this.elId + "_ico_alert");
    this.alertContainers = [$("mp1_" + this.elId + "_a_error"), $("mp1_" + this.elId + "_a_warn"), $("mp1_" + this.elId + "_a_notice")];
    this.alertContainer = $("mp1_" + this.elId + "_alerts") ;
    this.initAlerts(); //lazy load any alerts added without managepane already loaded....
    this.initToolTips(this.managePane);
  },
  
  initToolTips: function Item_initToolTips(el) {
    var tt = el.getAttribute("tooltip");
    if(tt != null) new Tooltip(el, tt);
    var children = el.childNodes;
    for(var i=0; i < children.length; i++) {
      if(children[i].nodeType == 1) {
        this.initToolTips(children[i]);
      }
    }
  },
  
  setName: function Item_setName(name) {
    if((name!=null)&&(name.length > 15)) {
      this.name = name.substring(0,13) + "...";
    } else {
      this.name = name;
    }
    
    if(this.titleSpan != null) {
      this.titleSpan.innerHTML = this.name;
    }
  },
  
  addToDesigner: function Item_addToDesigner(doSelect) {
    this.el = this.buildElement();
    log("built el");
    var self = this;
    
    
    log("inited drag");
      
    if((this.options.l)||(this.options.t)||(this.options.w)||(this.options.h)) { //options contains positioning data
      log("options contains positioning data: setPosition()");
      log(this.options);
      log("w=" + this.width + ", h=" + this.height);
      this.setPosition();
      log("set Pos Done");
    } else if(!this.startPosition()) {
      
      log("set Pos");
      this.setPosition();
      log("set Pos Done");
    }
    var self = this;
    
    this.el.onmousemove = function(ev) { self.rollover(); };
    this.el.onmouseout = function(ev) { self.leave(); };
    Event.observe(this.el, "mousedown", this.eventMouseDown);
    this.positionType = this.getItemPositioning();
    this.showEL();
    if(doSelect) {
      this.select();
    }
    
    this.enableDragging();
  },
  
  enableDragging: function Item_enableDragging() {
    if(this.dragable == null && !this.locked) {
      var snapFunc = (this.isWilcomEMB || this.digitize || this.isSavedDigitized) ? this.constrainToCanvas.bind(this) : null;
      var self = this;
      if(useAlphaHack) {
        this.dragable = new Draggable(this.el.id, { 
          revert:false,
          starteffect: function Item_starteffect(element) { 
            //do nothing
          },
          endeffect: function Item_endeffect(element) { 
            //do nothing
            self.dragEnded();
          },
          change: function Item_change(dragable) {
            self.moving(dragable);
          },
          snap: snapFunc
        });
      } else {
        this.dragable = new Draggable(this.el.id,{
          revert:false,
          change: function Item_change(dragable) { self.moving(dragable); },
          endeffect: function Item_endeffect(element) {
            var t1 = new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
            self.dragEnded();
          },
          snap: snapFunc
        });
      }
    }
  },

  disableDragging: function Item_disableDragging() {
    if(this.dragable != null) {
      this.dragable.destroy();
      this.dragable = null;
    }
  
  },

  //move between products... scale and offsets in design scale
  moveToDesigner: function Item_moveToDesigner(cViewProcess, resizeScale, oldLeft, oldTop, offsetLeft, offsetTop) {
  
    if(this.el!=null) {
      this.cViewArea.canvas.removeChild(this.el);
      cViewProcess.configuredViewArea.canvas.appendChild(this.el);
    }
    var oldDScale = this.cViewProcess.productProcess.designScale;
    var scale = this.cViewProcess.productProcess.designScale / resizeScale / cViewProcess.productProcess.designScale;
  
    log("moveToDesigner scale=" + scale + " oScale=" + this.cViewProcess.productProcess.designScale + " nScale=" + cViewProcess.productProcess.designScale);
  
    if(this.managePane!=null) {
      this.managePane.parentNode.removeChild(this.managePane);
    
      cViewProcess.managePane.appendChild(this.managePane);
    }
    this.cViewProcess = cViewProcess;
    this.cViewArea = cViewProcess.configuredViewArea;
    this.cView = this.cViewArea.configuredView;
    
    log("moveToDesigner: before move: this.width=" + this.width + " this.height=" + this.height + " this.left=" + this.left + " this.top=" + this.top);
    
    this.width *= scale;
    this.height *= scale;
    
    if(this.zIndex == null) this.zIndex = this.cViewProcess.getNextZOrder();
    
  /*  this.left = this.left * oldDScale;
    log("left in design scale=" + this.left);
    this.left -= oldLeft;
    log("left after old left removed=" + this.left);
    this.left /= resizeScale;
    log("left after rescaled=" + this.left);
    this.left += offsetLeft;
    log("left after offset (" + offsetLeft + ") added=" + this.left);
    this.left /=  this.cViewProcess.productProcess.designScale;
    log("left after back in full scale=" + this.left);
    */
    
    this.left = ((((this.left * oldDScale) - oldLeft) / resizeScale) + offsetLeft) / this.cViewProcess.productProcess.designScale; //change to designer co-ords, remove old left, rescale, add new offset, put back to full scale 
    
    
    //this.left *= scale + (offsetLeft / this.cViewProcess.productProcess.designScale);
    this.top = ((((this.top * oldDScale) - oldTop) / resizeScale) + offsetTop) / this.cViewProcess.productProcess.designScale; //change to designer co-ords, remove old left, rescale, add new offset, put back to full scale
    //this.top *= scale + (offsetTop / this.cViewProcess.productProcess.designScale);
  
    this.setDirty(true, (resizeScale==1)); //dont queue updates (calling code will do this), use cached version if resizeScale == 1
    log("moveToDesigner: after move: this.width=" + this.width + " this.height=" + this.height + " this.left=" + this.left + " this.top=" + this.top);
  },
  
  startPosition: function Item_startPosition() {
    return false;
  },
   //skipCheck added because this is called when transferring designs between products and the view data is not setup yet
  setPosition: function Item_setPosition(skipCheck) {
    if(this.el==null) {
      return;
    }
    var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
    log(dims);
    this.setHeight(parseInt(dims.h, 10));
    this.setWidth(parseInt(dims.w, 10));
    if(!this.isMoving) {
      this.setTop(parseInt(dims.t, 10));
      this.setLeft(parseInt(dims.l, 10));
    }
    this.sizeChanged();
    if(skipCheck == null || skipCheck == false) {
      this.checkPosition();
    }
  },
  
  setCanvasSize: function(w,h) {
    if(this.useCanvas) {
      this.canvas.setSize( w, h);
      
      if(this.rotated) {
        
        dims = this.getRealDesignerDims();
        
        
        this.canvasImg.attr({width: dims.w, height: dims.h, x:(w - dims.w) / 2,y:(h - dims.h) / 2});
        
        this.canvasImg.rotate(this.rotatedRef.angle, w/2, h/2);
        
        
      } else {
        this.canvasImg.rotate(0, true);
        this.canvasImg.attr({width: w, height: h, x:0,y:0});
      }
      
      
  /*    var hScale = parseFloat(h) / this.canvasImgHeight;
      var wScale = parseFloat(w) / this.canvasImgWidth;
      
      
      
      var xTrans = (parseFloat(w)/2.0); - (this.canvasImgWidth / 2.0);
      var yTrans = (parseFloat(h)/2.0); - (this.canvasImgHeight / 2);
      
      log("setCanvasSize(" + w + "," + h + "): canvasImgWidth=" + this.canvasImgWidth + ", canvasImgHeight=" + this.canvasImgHeight + ", hScale=" + hScale + ",  wScale=" + wScale + ",  xTrans=" + xTrans + ", yTrans=" + yTrans); 
      this.canvasImg.scale(wScale, hScale);
      this.canvasImg.translate(xTrans, yTrans);
  
      this.canvasImgWidth = parseFloat(w);
      this.canvasImgHeight = parseFloat(h);
      */
    }
  },
  
  //used when zooming.. no need to check anything...
  quickSetPosition: function() {
    var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.el.style.height = parseInt(dims.h, 10) + "px";      
    this.el.style.width = parseInt(dims.w, 10) + "px";
    this.el.style.top = parseInt(dims.t, 10) + "px";
    this.el.style.left = parseInt(dims.l, 10) + "px";
    this.setCanvasSize( parseInt(dims.w, 10), parseInt(dims.h, 10));
  },
  
  showPanel: function Item_showPanel() {
    if (this.itemVisible()) {
      this.managePane.style.display = ""
    } else {
      this.managePane.style.display="none";
    }
  },
  
  deleteClick: function Item_deleteClick(event) {
    Event.stop(event);
    if(d.mode == DESIGNER_MODE_AMEND) {
      if(this.cViewProcess.items.list.length == 1) {
        alert(ml("You cannot delete the last item using this decoration process as it will change the price"));
        return;
      }
    }
    
    var elementName = this.cViewProcess.productProcess.process.name;
    if(elementName){
      if(confirm(ml("Are you sure you wish to delete the selected %s?", (elementName+" "+this.itemTypeName) ))) {
        this.del();
      }
    }else{
      if(confirm(ml("Are you sure you wish to delete this?"))) {
        this.del();
      }
    }
  },
  
  del: function Item_del() {
    if(this.selected) {
      this.deSelect();
      this.cViewArea.selectItem(null);
    }
    //clear any existing alerts....
    for(var i=0; i < 3; i++) {
      var level = this.alerts[i];
      var keys = [];
      for(var k in level) {
        keys.push(k);
      }
      for(var j=0; j < keys.length; j++) {
        this.removeAlert(i, keys[j]);
      }
    }
    
    
    this.cViewProcess.deleteItem(this);
    if(this.managePane!=null) {
      Event.stopObserving(this.titleZUp, "click", this.zUpClickEvent);
      Event.stopObserving(this.titleZDown, "click", this.zDownClickEvent);
      Event.stopObserving(this.titleDel, "click", this.deleteClickEvent);
      this.managePane.parentNode.removeChild(this.managePane);
    }
    /*if(this.managePane0!=null) {
      Event.stopObserving(this.titleZUp0, "click", this.zUpClickEvent);
      Event.stopObserving(this.titleZDown0, "click", this.zDownClickEvent);
      Event.stopObserving(this.titleDel0, "click", this.deleteClickEvent);
      this.managePane0.parentNode.removeChild(this.managePane0);
    }*/
    if(this.el != null) {
      Event.stopObserving(this.el, "mousedown", this.eventMouseDown);
      this.cViewArea.canvas.removeChild(this.el);
    }
    this.cViewProcess.reSortZOrder();
    this.setDirty();
    //d.itemChanged(true);
  },
  
  getElementZIndex: function Item_getElementZIndex() {
    return (this.zIndex * 4) + (this.cViewProcess.productProcess.process.zIndex * 100);
  },
  
  zUpClick: function Item_zUpClick(event) {
    Event.stop(event);
    var pos = this.cViewProcess.items.objectIndex(this.id);
    log("zUpClick: pos=" + pos);
    if(pos >= this.cViewProcess.items.list.length-1) {
      return;
    }
    //if(pos == 0) {
    //  return;
    //}
    var swap = this.cViewProcess.items.list[pos+1];
    var oldpos = swap.zIndex;
    swap.zIndex = this.zIndex;
    swap.el.style.zIndex = swap.zIndex * 4;
    this.zIndex = oldpos;
    this.el.style.zIndex = this.zIndex * 4;
    var prev = this.managePane.previousSibling;
    log(prev);
    log(this.managePane.nextSibling);
    this.managePane.parentNode.removeChild(prev);
    if(this.managePane.nextSibling == null) {
      this.managePane.parentNode.appendChild(prev);
    } else {
      this.managePane.parentNode.insertBefore(prev , this.managePane.nextSibling);
    }
    this.cViewProcess.reSortZOrder();
    this.setDirty();
    //d.itemChanged(true);
  },
  
  zDownClick: function Item_zDownClick(event) {
    Event.stop(event);
    var pos = this.cViewProcess.items.objectIndex(this.id);
    log("zDownClick: pos=" + pos);
    //if(pos >= this.cViewProcess.items.list.length-1) {
    //  return;
    //}
    if(pos == 0) {
      return;
    }
    var swap = this.cViewProcess.items.list[pos-1];
    var oldpos = swap.zIndex;
    swap.zIndex = this.zIndex;
    swap.el.style.zIndex = swap.zIndex * 4;
    this.zIndex = oldpos;
    this.el.style.zIndex = this.zIndex * 4;
    
    if(this.managePane.nextSibling.nextSibling!=null) {
      var target = this.managePane.nextSibling.nextSibling;
      var parent = this.managePane.parentNode;
      parent.removeChild(this.managePane);
      parent.insertBefore(this.managePane, target);
    } else {
      this.managePane.parentNode.appendChild(this.managePane);
    }
    
    this.cViewProcess.reSortZOrder();
    this.setDirty();
    //d.itemChanged(true);
  },
  
  showEL: function Item_showEL() {
    this.el.style.display="";
  },
  
  setEnabled: function Item_setEnabled(enabled) {
    this.enabled = enabled;
    if(this.el != null) {
      if(enabled) {
        this.enableDragging();
        this.el.style.zIndex = this.zIndex * 4;
      } else {
        this.disableDragging();
        this.el.style.zIndex = 0;
      }
        
      if(useAlphaHack) {
        if(enabled) {
          this.el.style.display="";
        } else {
          this.el.style.display="none";
        }
      } else {
        if(enabled) {
          this.el.setOpacity(1);
        } else {
          this.el.setOpacity(0.3);
        }
      }
    }
  },
  
  setLocked: function Item_setLocked(locked) {
    this.locked = locked;
    if(this.el != null) {
      if(locked) {
        this.disableDragging();
      } else {
        this.enableDragging();
      }
    }
  },
  
  rollover: function Item_rollover() {
    if(this.enabled && !this.locked) {
      if(!this.selected) {
        this.setRolloverStyle();
        //this.managePane.className = "managepanehightlight";
      }
    }
  },
  
  setRolloverStyle: function Item_setRolloverStyle() {
    if((BrowserDetect.browser == "Explorer")||(BrowserDetect.browser == "Opera")) {
      this.el.style.borderStyle="dotted";
      this.el.style.borderWidth="1px";
      this.el.style.borderColor="black";
    } else {
      this.el.style.outlineStyle="dotted";
      this.el.style.outlineWidth="1px";
      this.el.style.outlineColor="invert";
    }
  },
  
  leave: function Item_leave() {
    if(this.enabled && !this.locked) {
      if(!this.selected) {
        this.setLeaveStyle();
        //this.managePane.className = "managepaneoff";   
      }
    }
  },
  
  setLeaveStyle: function Item_setLeaveStyle() {
    if((BrowserDetect.browser == "Explorer")||(BrowserDetect.browser == "Opera")) {
      this.el.style.borderStyle="none";
      this.el.style.borderWidth="0px";
    } else {
      this.el.style.outlineWidth="0px";
    }
    this.el.style.zIndex = this.getElementZIndex();
  },
  
  mouseDown: function Item_mouseDown(event) {
    this.select(false, event);
  },
  
  lockToggle: function Item_lockToggle(event) {
    log("locked: " + this.lockCheckBox.checked);
    if (this.lockCheckBox.checked) {
      this.setLocked(true);
	  this.lockIcon.style.visibility="visible";
    } else {
      this.setLocked(false);
	  this.lockIcon.style.visibility="hidden";
    }
  },
  
  select: function Item_select(dontEnterCustomisePane, event) {
    log("select " + this.id);
    if(!this.enabled || (this.locked && d.mode!=DESIGNER_MODE_CONFIGURE)) {
      log(event);
      if(this.locked) {
        log("item is locked..not passing through....");
        return false;
      }
      if(event == null) {
        log("item is disabled..event is null..not passing through....");
        return false;
      }
      var pointer = this.cView.getLayoutMousePosition(event);
      log("item is disabled..passing through....");
      this.cView.proxyMouseDown(pointer[0],pointer[1], event);
      return false;
    }
    if(dontEnterCustomisePane != true) {
      log("selecting customize tab", true);
      d.selectTab('m','customize'); 
      //d.expandManage();
      //this.autoRepositionHandles();
    }
    
    if(!this.selected) {
      this.cViewArea.selectItem(this);
      this.selected = true;
      this.setSelectStyle();
      //this.el.style.zIndex=1000;
      if(this.managePaneBody!=null) {
        this.managePaneBody.style.display = "";
        this.titleBar.style.cursor="";
      }
      /*if(this.managePane0!=null) {
        this.managePane0.className = "managepaneselected";
      }*/
      if(this.managePane!=null) {
        this.managePane.className = "managepaneselected";
        if (!this.itemReady()) this.disablePane();
      }
      this.addHandles();
    } else {
      log("Item.select: already selected:" + this.id); 
      this.autoRepositionHandles();
    }
    
    
  },
  
  setSelectStyle: function Item_setSelectStyle() {
    if((BrowserDetect.browser == "Explorer")||(BrowserDetect.browser == "Opera")) {
      this.el.style.borderStyle="solid";
      this.el.style.borderWidth="1px";
      this.el.style.borderColor="black";
    } else {
      this.el.style.outlineStyle="solid";
      this.el.style.outlineWidth="1px";
      this.el.style.outlineColor="invert";
    }
    this.el.style.zIndex = 1000;
  },
  
  deSelect: function Item_deSelect() {
    if (!this.itemReady()) {
      log("deselected !itemReady() item");
      this.selected = false;
      return;
    }
    
    log("deselect " + this.id);
    this.setLeaveStyle();
    //this.el.style.zIndex=this.zIndex * 4;
    this.selected = false;
    this.removeHandles();
    if(this.managePaneBody!=null) {
      this.titleBar.style.cursor="pointer";
      this.managePaneBody.style.display = "none";
    }
    /*if(this.managePane0!=null) {
      this.managePane0.className = "managepaneoff2";
    }*/
    if(this.managePane!=null) {
      this.managePane.className = "managepaneoff2";
    }
  },
  
  getDims: function Item_getDims() {
    return {t: parseFloat(this.el.style.top),l:parseFloat(this.el.style.left), h:parseFloat(this.el.style.height), w:parseFloat(this.el.style.width)};
  },
  
  //get the dims for the canvas using the final dims * scale (we use design scale)
  getCanvasDims: function Item_getCanvasDims() {
    var scale = this.cViewProcess.productProcess.designScale;
    return {t: parseInt(this.top * scale, 10), l:parseInt(this.left * scale, 10), h:parseInt(this.height * scale, 10), w:parseInt(this.width * scale, 10)};
  },
  
  fromCanvasDim: function Item_fromCanvasDim(dim) {
    var scale = (d.currentCanvasType==0) ? this.cViewProcess.productProcess.layoutScale : this.cViewProcess.productProcess.designScale;
    return dim / scale;
  },
  
  toCanvasDim: function Item_toCanvasDim(dim) {
    var scale = (d.currentCanvasType==0) ? this.cViewProcess.productProcess.layoutScale : this.cViewProcess.productProcess.designScale;
    return dim * scale;
  },
  
  toCanvasDims: function Item_toCanvasDims() {
    return d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
  },
  
  moving: function Item_moving(dragable) {
    if(!this.selected) {
      this.select();
    }
    if(!this.isMoving) {
      this.moveStarted();
      this.isMoving = true;
    }
  },
  
  moveStarted: function Item_moveStarted() {
    this.removeHandles();
  },
  
  moveFinished: function Item_moveFinished() {
    log("moveFinished " + this.id);
    
    this.isMoving = false;
    var dims = d.fromCanvasDims(this.getDims(), this.cViewProcess.productProcess);
    this.width = dims.w;
    this.height = dims.h;
    this.top = dims.t;
    this.left = dims.l;
    this.addHandles();
    this.checkPosition();
  },
  
  constrainToCanvas: function Item_constrainToCanvas(x,y) {
    
    log("x=" + x + ", y=" + y);
    
    var dims = this.getDims();
    var cSize = this.cViewArea.canvasSize();
    
    if(x + dims.w > cSize.w) {
      x = cSize.w - dims.w;
    }
    if(x < 0) {
      x = 0;
    }
    if(y + dims.h > cSize.h) {
      y = cSize.h - dims.h;
    }
    if(y < 0) {
      y = 0;
    }
    return [x,y];
  },
  
  //is the item inside (0), outside (1) or cropped (2)
  getItemPositioning: function Item_getItemPositioning() {
    var idims = this.getDims();
    var cdims = this.cViewArea.canvasSize();
    
    var somethingOutside = false;
    var somethingInside = false;
    var allInside = true;
    if(idims.t > cdims.h) {//top is below
      somethingOutside = true;
      allInside = false;
    } else if(idims.t+idims.h < 0) { //bottom is above top
      somethingOutside = true;
      allInside = false;
    } else {
      if((idims.t < 0) || (idims.t+idims.h > cdims.h) ) { //not vertically all inside...
        allInside = false;
      }
      if(idims.l > cdims.w) { //left is to right of canvas
        somethingOutside = true;
        allInside = false;
      } else if(idims.l+idims.w < 0){ //right is to left of canvas
        somethingOutside = true;
        allInside = false;
      } else {
        somethingInside = true;
        if((idims.l < 0) || (idims.l+idims.w > cdims.w) ) { //not horizontally all inside...
          allInside = false;
        }
      }
    }
    if(allInside) {
      return 0;
    } else if(!somethingInside) {
      return 1;
    }
    return 2;
  },
  
  dragEnded: function Item_dragEnded() {
    //test if the div is inside the canvas.. if not delete it
    
    var newPositionType = this.getItemPositioning();
    
    log("dragEnded, newPositionType=" + newPositionType);
    if(newPositionType == 1) {
      log("del me!");
      this.del();
    } else if(this.isMoving) {
      //the dropable didnt pick this up.. fix it...
      log("Fixing drop..:" + this.isMoving);
      this.moveFinished();
    }
    
    //we want to up the render version if the item is cropped or postion type has changed
    this.setDirty(false, ((this.positionType == newPositionType) && newPositionType != 2));
    
    this.positionType = newPositionType; 
    //d.itemChanged(true);
  },
  
  //callback fired whenever the position/size has changed..
  checkPosition: function Item_checkPosition() {
    //do nothing...
  },
  
  buildSizer: function Item_buildSizer(cursor, position) {
    if((!this.allowHandleResizing)||(!this.allowResize)) {
      return this.buildReadOnlySizer(cursor, position);
    }
    if(this.sizers == null) {
      //console.debug("new sizer array");
      this.sizers = new Hash();
    }
    var sizer = d.sizer.cloneNode(true);
    sizer.id = d.getNextId();
    sizer.style.cursor = cursor;
    sizer.style.zIndex = 1010;
    sizer.positioning_mode = position;
    document.body.appendChild(sizer);
    this.sizers[position] = sizer;
    Event.observe(sizer, "mousedown", this.eventResizeStart);
    return sizer;
  },
  
  buildReadOnlySizer: function Item_buildReadOnlySizer(cursor, position) {
    if(this.sizers == null) {
      //console.debug("new sizer array");
      this.sizers = new Hash();
    }
    var sizer = d.roSizers[position].cloneNode(true);
    sizer.id = d.getNextId();
    sizer.style.zIndex = 1010;
    sizer.positioning_mode = position;
    document.body.appendChild(sizer);
    this.sizers[position] = sizer;
    return sizer;
  },
  
  buildRotator: function Item_buildSizer(cursor, position) {
    if(this.sizers == null) {
      //console.debug("new sizer array");
      this.sizers = new Hash();
    }
    var sizer = d.rotator.cloneNode(true);
    sizer.id = d.getNextId();
    sizer.style.cursor = cursor;
    sizer.style.zIndex = 1010;
    sizer.positioning_mode = position;
    document.body.appendChild(sizer);
    this.sizers[position] = sizer;
    Event.observe(sizer, "mousedown", this.eventRotatorStart);
    return sizer;
  },
  
  resizeStart: function Item_resizeStart(event) {
    if(this.handleResizeStart(event)) {
      
    } else {
      this.currentSizer = Event.element(event);
      log("resizeStart for " + this.currentSizer.positioning_mode);
      
      Event.observe(document, "mouseup", this.eventResizeMouseUp);
      Event.observe(document, "mousemove", this.eventResizeMouseMove);
      Event.stop(event);
    }
    return false;
  },
  
  handleResizeStart: function Item_handleResizeStart(event) {
    return false;
  },
  
  resizeMove: function Item_resizeMove(event) {
    //log("resizeMove");
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) {
      return false;
    }
    this._lastPointer = pointer;
    
    
    var pl = pointer[0]-PPSizerHalf;
    var pt = pointer[1]-PPSizerHalf;
    
    
    
    var pm = this.currentSizer.positioning_mode;
    
    var dims = this.cViewArea.abs(this.getDims());
    var t = dims.t;
    var l = dims.l;
    var h = dims.h;
    var w = dims.w;
    
    var ot = t;
    var ol = l;
    var oh = h;
    var ow = w;
    
    if(pm == "BR") {
      h = pointer[1] - t;
      w = pointer[0] - l;
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        pt = t + h - PPSizerHalf;
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        pl = l + w - PPSizerHalf;
      }
      this.repositionHandles("BR",t,l,h,w);
    } else if(pm == "BL") {
      h = pointer[1] - t;
      w = w + (l - pointer[0]);
      l = pointer[0];
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        pt = t + h - PPSizerHalf;
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        l = (ol + ow) - w;
        pl = l - PPSizerHalf;
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      } else {
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      }
      this.repositionHandles("BL",t,l,h,w);
    }  else if(pm == "TL") {
      h = h + (t - pointer[1]);
      w = w + (l - pointer[0]);
      t = pointer[1];
      l = pointer[0];
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        t = (ot + oh) - h;
        pt = t - PPSizerHalf;
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      } else {
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        l = (ol + ow) - w;
        pl = l - PPSizerHalf;
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      } else {
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      }
      
      this.repositionHandles("TL",t,l,h,w);
    } else if(pm == "TR") {
      h = h + (t - pointer[1]);
      w = pointer[0] - l;
      t = pointer[1];
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        t = (ot + oh) - h;
        pt = t - PPSizerHalf;
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      } else {
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        pl = l + w - PPSizerHalf;
      }
      this.repositionHandles("TR",t,l,h,w);
    }
    
    this.currentSizer.style.left = pl + "px";
    this.currentSizer.style.top = pt + "px";
    
    var dims = d.fromCanvasDims(this.getDims(),this.cViewProcess.productProcess);
    this.width = dims.w;
    this.height = dims.h;
    this.top = dims.t;
    this.left = dims.l;
    this.sizeChanged();
    return false;
  },
  
  resizeFinished:  function(event) {
    log("resizeFinished");
    this.releaseResizing();
    
    var dims = d.fromCanvasDims(this.getDims(),this.cViewProcess.productProcess);
    this.width = dims.w;
    this.height = dims.h;
    this.top = dims.t;
    this.left = dims.l;
    this.sizeChanged();
    this.checkPosition();
    //d.itemChanged(true);
    this.setDirty();
    d.selectTab('m','customize'); 
    //d.expandManage();
  },
  
  releaseResizing: function Item_releaseResizing() {
    this._lastPointer = null;
    this.currentSizer = null;
    Event.stopObserving(document, "mouseup", this.eventResizeMouseUp);
    Event.stopObserving(document, "mousemove", this.eventResizeMouseMove);
  },
  
  
  rotatorStart: function Item_rotatorStart(event) {
    log("rotatorStart");
    Event.observe(document, "mouseup", this.eventRotatorMouseUp);
    Event.observe(document, "mousemove", this.eventRotatorMouseMove);
    Event.stop(event);
    
    var x = Event.pointerX(event);
    var y = Event.pointerY(event);
    
    var el = Event.element(event);
    
    this.rotElement = $(el.cloneNode(true));
    this.rotElement.id = d.getNextId();
    document.body.appendChild(this.rotElement);
    var elPos = Position.cumulativeOffset(this.rotElement);
    
    this.rotOffsetX = x - elPos[0];
    this.rotOffsetY = y - elPos[1];
    
   
    this.rotationOffset = this.getRotation() - this.getRotationAngle(x,y);
    
    
    
    
    return false;
  },

  
  rotatorMove: function Item_rotatorMove(event) {
    log("rotatorMove");
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    if(this._lastRotPointer && (this._lastRotPointer.inspect() == pointer.inspect())) {
      return false;
    }
    this._lastRotPointer = pointer;
    
    
    var pl = pointer[0]-PPRotatorSizeHalf;
    var pt = pointer[1]-PPRotatorSizeHalf;
    
    this.rotElement.style.left = (pointer[0] - this.rotOffsetX) + "px";
    this.rotElement.style.top = (pointer[1] - this.rotOffsetY) + "px";
    
    this.rotateWidget.externalSet(this.getRotationAngle(pl,pt) + this.rotationOffset);

  },
  
  getRotationAngle: function Item_getRotationAngle(pl,pt) {
     //get the center of the current item....
    var dims = this.toCanvasDims();
    dims = this.cViewArea.abs(dims);
    //var dims = this.cViewArea.abs(this.getDims());
    var y = dims.t + (dims.h/2);
    var x = dims.l + (dims.w/2);

    
    
    //first get size of the sides

    
    var side1 = parseFloat(pl - x); //horiz
    var side2 = parseFloat(pt - y); //vert
    var side3 = Math.sqrt((side1*side1) + (side2*side2)); //slope
    
    var offset = 0;
    if(side2 < 0) { //above
      if(side1 < 0) { //left
        offset = 270;
      } else { //right
        offset = 90;
      }
    } else { //below
      if(side1 < 0) { //left
        offset = 270;
      } else { //right
        offset = 90;
      }
    }
    
    var rads = Math.atan(side2/side1);
    var degs = ((rads*180.0) / Math.PI);
    
    //round to closest 1 deg
    
    degs = parseInt(degs, 10);

    degs += offset;
    
    return degs;
  },
  
  rotatorFinished: function Item_rotatorFinished(event) {
    log("rotatorFinished");
    this._lastRotPointer = null;

    Event.stopObserving(document, "mouseup", this.eventRotatorMouseUp);
    Event.stopObserving(document, "mousemove", this.eventRotatorMouseMove);
    
    this.rotElement.remove();
    
    this.rotateWidget.externalRotateFinish();
    
    this.checkPosition();
    this.setDirty();

  },
  
  addHandles: function Item_addHandles() {
    if(this.hasHandles) {
      this.removeHandles();
    }
    //log("addHandles", true);
    var dims = this.toCanvasDims();
    dims = this.cViewArea.abs(dims);
    //var dims = this.cViewArea.abs(this.getDims());
    var t = dims.t;
    var l = dims.l;
    var h = dims.h;
    var w = dims.w;

    //console.debug(dims);
    
    var sizer = this.buildSizer("nw-resize","BR");
    
    
    this.positionHandle(sizer,"BR",t,l,h,w);
    sizer.style.display="";
    
    
    sizer = this.buildSizer("ne-resize","BL");
    this.positionHandle(sizer,"BL",t,l,h,w);
    sizer.style.display="";
    
    sizer = this.buildSizer("nw-resize","TL");
    this.positionHandle(sizer,"TL",t,l,h,w);
    sizer.style.display="";
    
    sizer = this.buildSizer("ne-resize","TR");
    this.positionHandle(sizer,"TR",t,l,h,w);
    sizer.style.display="";
    
    if(this.useCanvas) {
      sizer = this.buildRotator("pointer","ROT");
      this.positionHandle(sizer,"ROT",t,l,h,w);
      sizer.style.display="";
    }
    
    this.hasHandles = true;
    
  },
  
  removeHandles: function Item_removeHandles() {
    if(this.sizers!=null) {
      var self = this;
      this.sizers.each(function(pair) {
        if(pair.value!=null) {
          Event.stopObserving(pair.value, "mousedown", self.eventResizeStart);
          document.body.removeChild(pair.value);
        }
      });
      this.sizers = null;
    }
    this.hasHandles = false;
  },
  
  positionHandle: function Item_positionHandle(sizer, pos, t, l, h, w) {
    try {
      if(pos=="BR") {
        sizer.style.left = (l + w - PPSizerHalf) + "px";
        sizer.style.top = (t + h - PPSizerHalf) + "px";
      } else if(pos=="TL") {
        sizer.style.left = (l - PPSizerHalf) + "px";
        sizer.style.top = (t - PPSizerHalf) + "px";
      } else if(pos=="TR") {
        sizer.style.left = (l + w - PPSizerHalf) + "px";
        sizer.style.top = (t- PPSizerHalf) + "px";
      } else if(pos=="BL") {
        sizer.style.left = (l - PPSizerHalf) + "px";
        sizer.style.top = (t + h - PPSizerHalf) + "px";
      } else if(pos=="ROT") {
        sizer.style.left = (l + w + 10 + PPSizerHalf) + "px";
        sizer.style.top = (t - PPSizerHalf - 10 ) + "px";
      } else {
        log("Unknown position:" + pos);
      }
    } catch(e) {}
  },
         
  repositionHandles: function Item_repositionHandles(exclude, t,l,h,w) {
    var self = this;
    if(this.sizers==null) {
      this.addHandles();
    }
    this.sizers.each(function(pair) {
      if(pair.key != exclude) {
        //console.debug(key);
        self.positionHandle(pair.value, pair.key, t,l,h,w);
      }
    });
  },
  
  autoRepositionHandles: function Item_autoRepositionHandles() {
    //var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
    //dims = this.cViewArea.abs(dims);
    var dims = this.cViewArea.abs(this.getDims());
    this.repositionHandles("",dims.t, dims.l, dims.h, dims.w);
  },
  
  sizeChanged: function Item_sizeChanged() {
    this.updateTitleSize();
  },
  
  minDims: function Item_minDims(dims) {
    return dims;
  },
  
  //check if we can rescale this item using desiredScale to work with process...
  checkScale: function Item_checkScale(desiredScale, process) {
    return desiredScale; //default behaviour
  },
  
  serializeToOptions: function Item_serializeToOptions(o, excludeRotation) {
    if(o == null) o = {};
    o.id =this.id;
    o.p =this.cViewProcess.id;
    o.rv =this.renderVersion;
    o.lk =this.locked;
    o.w =this.width;
    o.h =this.height;
    o.l =this.left;
    o.t =this.top;
    o.z =this.zIndex;
    o.it =this.asset.getItemType();
    var aid = this.asset.id;
    if(aid!=null) {
      o.aid =aid;
    }
    
    if(this.isWilcomEMB) {
      o.emb=1;
      o.stc=this.stitchCount;
    }
    return o;
  },
  
  serialize: function Item_serialize(queryComponents, prefix, excludeRotation) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    var o = this.serializeToOptions({}, excludeRotation);
    
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    
    serializeObject(queryComponents, prefix, o);
    
    return queryComponents.join('&');
  },
  
  
  
  serializeOLD: function Item_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[id]") + "=" + encodeURIComponent(this.id));
    queryComponents.push(encodeURIComponent(prefix + "[p]") + "=" + encodeURIComponent(this.cViewProcess.id));
    queryComponents.push(encodeURIComponent(prefix + "[rv]") + "=" + encodeURIComponent(this.renderVersion));
    queryComponents.push(encodeURIComponent(prefix + "[lk]") + "=" + encodeURIComponent(this.locked));
    queryComponents.push(encodeURIComponent(prefix + "[w]") + "=" + encodeURIComponent(this.width));
    queryComponents.push(encodeURIComponent(prefix + "[h]") + "=" + encodeURIComponent(this.height));
    queryComponents.push(encodeURIComponent(prefix + "[l]") + "=" + encodeURIComponent(this.left));
    queryComponents.push(encodeURIComponent(prefix + "[t]") + "=" + encodeURIComponent(this.top));
    queryComponents.push(encodeURIComponent(prefix + "[z]") + "=" + encodeURIComponent(this.zIndex));
    queryComponents.push(encodeURIComponent(prefix + "[it]") + "=" + encodeURIComponent(this.asset.getItemType()));
    var aid = this.asset.id;
    if(aid!=null) {
      queryComponents.push(encodeURIComponent(prefix + "[aid]") + "=" + encodeURIComponent(aid));
    }
    
    if(this.isWilcomEMB) {
      queryComponents.push(encodeURIComponent(prefix + "[emb]") + "=" + encodeURIComponent(1));
      queryComponents.push(encodeURIComponent(prefix + "[stc]") + "=" + encodeURIComponent(this.stitchCount));
    }
    return queryComponents.join('&');
  },
  
  //x,y come in at layout scale...
  hitTest: function Item_hitTest(x,y) {
    var scale = this.cViewProcess.productProcess.layoutScale;
    x = x / scale;
    y = y / scale;
     
    if((x > this.left)&&(x < this.left + this.width)) {
      if((y > this.top)&&(y < this.top + this.height)) {
        return true ;
      }
    }
    log("Item.hitTest miss: scale=" + scale + " x.y=" + x + "." + y);
    log(this);
    if(x < this.left) {
      log("To Left");
    } 
    if (x > this.left + this.width) {
      log("To Right");
    } 
    if(y < this.top) {
      log("Above");
    } 
    if (y > this.top + this.height) {
      log("Below");
    }
    return false;
  },
  
  setAsset: function Item_setAsset(asset) {
    
  },
  
  checkQuality: function Item_checkQuality() {
    return false;
  },
  
  setQualityWarning: function Item_setQualityWarning(allOk) {
    
    if(allOk) {
      if(!this.qualityOK) {
        this.removeAlert(1,"quality_warning");
      }
  //    if(this.qualityIcon1!=null) {
  //      this.iconContainer1.removeChild(this.qualityIcon1);
  //      this.qualityIcon1 = null;
  //    }
    } else if(this.qualityOK) {
      this.addAlert(1,"quality_warning", "alert_quality_warning", ml("Poor Quality"), ml("This image has been resized to the point that it may appear blurred."), true);
    }
    this.qualityOK = allOk;
  },
  
  //dontUpdateRenderVersion: the image was only moved (not resized etc) so the cached version on the server is ok
  setDirty: function Item_setDirty(dontQueueUpdate, dontUpdateRenderVersion) {
    if(dontUpdateRenderVersion != true) {
      this.renderVersion++;
      this.reRender = true;
    } else {
      log("dontUpdateRenderVersion");
    }
    var queueUpdate = !(dontQueueUpdate == true);
    this.cViewArea.setChildReRender(queueUpdate);
  },
  
  clearReRender: function Item_clearReRender() {
    this.reRender = false;
  },
  
  //start updating the element with s status showing... track versions to multiple queued updates keep status showing
  startUpdating: function Item_startUpdating() {
    if(this.el == null) {
      return {v: null} ;
    }
    if(this.updateKey == null) {
      this.updateKey = asyncStart(this.el);
    }
    this.updateVersion = this.renderVersion;
    return {k: this.updateKey, v: this.updateVersion};
  },
  
  //if the updateInfo (return value of startUpdating) is latest version then finish the async
  finishUpdating: function Item_finishUpdating(updateInfo) {
    if(this.updateKey != null) {
      if(this.updateVersion == updateInfo.v) {
        asyncFinish(this.updateKey);
        this.updateKey = null;
        this.updateVersion = null;
      }
    }
  },
  
  disablePane: function Item_disablePane() {
    if(this.managePane != null && this.paneKey == null) {
      this.paneKey = asyncStart(this.managePane);
    }
  },
  
  enablePane: function Item_enablePane() {
    if(this.paneKey != null) {
      asyncFinish(this.paneKey);
      this.paneKey = null;
    }
  },
  
  //add an alert
  //level: 0=error, 1=warning, 2 = notice
  addAlert: function Item_addAlert(level, alertKey, bodyClass, alertTitle, alertMessage, warnAddToCart) {
    var alertData = {level:level, bodyClass:bodyClass, alertTitle:alertTitle, alertKey:alertKey, alertMessage:alertMessage, warnAddToCart:warnAddToCart};
    var levelData = this.alerts[level];
    levelData[alertKey] = alertData;
    if(this.alertIcon) {
      if((this.alertLevel == null)||(this.alertLevel > level)) {
        //we need to update the icon..
        this.alertIcon.className = alertLevelHeaderClasses[level];
        this.alertLevel = level;
      }
    }
    if(this.alertContainers != null) {
      //get the ol for the level
      this.addAlertToContainer(level, alertData);
    }
    this.cView.configuredProduct.setAlertIcons();
  },
  
  addAlertToContainer: function Item_addAlertToContainer(level, alertData) {
    var ol = this.alertContainers[level];
    
    //make the li to contain the alert
    
    var li = document.createElement("LI");
    li.className = alertData.bodyClass ;
    li.innerHTML = alertData.alertMessage ;
    //append li to ol of level
    ol.appendChild(li);
    ol.style.display="";
    this.alertContainer.style.display="";
    //add alert data to this.alerts
    alertData.li = li;
  },
  
  //lazy loading support
  initAlerts: function Item_initAlerts() {
    for(var level =0; level < 3; level ++) { //for each level
      for(var alertKey in this.alerts[level]) { //for each alert in the level
        var alertData = this.alerts[level][alertKey]; //get the alert data...
        if((this.alertLevel == null)||(this.alertLevel > level)) {
          //we need to update the icon..
          if(this.alertIcon) this.alertIcon.className = alertLevelHeaderClasses[level];
          this.alertLevel = level;
        }
        if(alertData.li == null) { //the alert is not displayed yet..
          this.addAlertToContainer(level, alertData);
        }
      }
    }
  },
  
  removeAlert: function Item_removeAlert(level, alertKey) {
    var levelData = this.alerts[level];
    var alertData = levelData[alertKey];
    //remove the li
    if(alertData.li != null) {
      alertData.li.parentNode.removeChild(alertData.li);
    }
    delete levelData[alertKey]; //remove the alert data from the level data
    
    //now go through each level to set correct data/displays
    var foundLevel = false ;
    for(var i=0; i < 3; i++) {
      if(hashSize(this.alerts[i]) == 0) {
        if(this.alertContainers) this.alertContainers[i].style.display="none";
      } else {
        if(!foundLevel) {
          if(this.alertLevel != i) {
            if(this.alertIcon) this.alertIcon.className = alertLevelHeaderClasses[i];
            this.alertLevel = i;
          }
          foundLevel = true;
        }
      }
    }
    if(!foundLevel) {
      if(this.alertIcon) this.alertIcon.className = "alert_icon_none";
      this.alertLevel = null;
      if(this.alertContainer) this.alertContainer.style.display="none";
    }
    this.cView.configuredProduct.setAlertIcons();
  }, 
  
  updateAlertMessage: function Item_updateAlertMessage(level, alertKey, title, message) {
    var levelData = this.alerts[level];
    var alertData = levelData[alertKey];
    alertData.title = title;
    alertData.alertMessage = message;
    if(alertData.li != null) {
      alertData.li.innerHTML = message;
    }
  },
  
  getAlerts: function Item_getAlerts(types, allAlerts) {
    for(var i=0; i < types.length; i++) {
      var level = types[i];
      log("checking alerts of type " + level);
      var alerts = allAlerts[level];
      var levelData = this.alerts[types[i]];
      for(k in levelData) {
        if(levelData[k].warnAddToCart) {
          log("Adding alert " + k);
          if(alerts[k] == null) {
            alerts[k] = {items: []};
          }
          var src = this.getSrc();
          var dims = this.fitToSize(50);
          alerts[k].items.push({id:this.id, errorThumb: src, width: dims.w, height: dims.h});
        }
      }
    }
  },
  
  fitToSize: function Item_fitToSize(side) {
    var aspectRatio = parseFloat(this.width) / parseFloat(this.height);
    if(this.width > this.height) {
      return {w: side, h: parseFloat(side) / aspectRatio};
    } else {
      return {h: side, w: parseFloat(side) * aspectRatio};
    }
  },
  
  allowFeature: function(feature) {
    return true;
  },
  
  getItemSize: function Item_getItemSize() {
    if (this.width != null && this.height != null) {
      var w = 0;
      var h = 0;
      if(this.rotated) {
        var dims = this.getRealDims();
        w = dims.w / this.cViewProcess.productProcess.perfectDPI;
        h = dims.h / this.cViewProcess.productProcess.perfectDPI;
      } else {
        w = this.width / this.cViewProcess.productProcess.perfectDPI;
        h = this.height / this.cViewProcess.productProcess.perfectDPI;
      }
      return [d.convertLengthFromInches(w).toFixed(2), d.convertLengthFromInches(h).toFixed(2)];
    } else return null;
  },
  
  updateTitleSize: function Item_updateTitleSize() {
    if (this.titleSize != null) {
      var size = this.getItemSize();
      if (size) this.titleSize.innerHTML = size[0] + d.getLengthUnit() + " x " + size[1] + d.getLengthUnit();
    }
  },
  
  setFeature: function(feature, enabled) {
    if(this.features == null) this.features = {};
    this.features[feature] = enabled;
    var el = $("mp1_"+this.elId+"_fc_" + feature);
    if(el != null) {
      if(enabled) {
        el.show();
      } else {
        el.hide();
      }
    }
  },
  
  initFeature: function(feature) {
    if((this.features == null)||(this.features[feature] == null)) return; //nothing to do....
    this.setFeature(feature, this.features[feature]);
  },
  
  itemReady: function Item_itemReady() {
    if (!this.ready) {
      if (this.top != null) {
        this.ready = true;
      }
    }
    return this.ready;
  },
  
  itemVisible: function Item_itemVisible() {
    if (d.mode != DESIGNER_MODE_CONFIGURE && this.locked) {
      return false;
    } else {
      return true;
    }
  },
  
  copyTo: function Item_copyTo(destArea) {
    var allowedProcesses = destArea.getAllowedProcesses();
    var passed = 0;
    if(allowedProcesses[this.cViewProcess.id]) {
      var process = destArea.processes[this.cViewProcess.id];
      var initData = this.serializeToOptions();
      log(initData);
      //centered (remove t/l)
      initData.l = null;
      initData.t = null; 
      initData.z = null; //need new zindex
      var newId = destArea.configuredProduct.getNextItemId();
      var asset = this.asset;
      eval("var newItem = new " + this.CLASSDEF.name + "(newId, process, asset, initData);");
      destArea.insertNewItem(newItem);
      passed += 1;
      newItem.select();
      d.itemChanged(true);
    }
    return [passed, 1];
  },
  
  copy: function() {
    var initData = this.serializeToOptions();
    
    var newId = this.cViewArea.configuredProduct.getNextItemId();
    var asset = this.asset;
    eval("var newItem = new " + this.CLASSDEF.name + "(newId, this.cViewProcess, asset, initData);");
    this.cViewArea.insertNewItem(newItem, true);
    return newItem;
  }

});

var ImageItem = Class.create({
  CLASSDEF : {
      name:  'ImageItem',
      parent: Item
  },
  
  initialize: function ImgItem_initialize(id, cViewProcess, asset, options) {
    this.itemType = 0;
    this.itemTypeName = "Image";
    
    if(asset == null) {
      alert("Unable to get image " + options.aid);
      return;
      //asset = makePlaceholderAsset();
      //loadAssetIntoItem(options.aid, this)
    } 
    
    ImageItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    
    //this.isWilcomEMB = true; for local testing....
    
    this.moveClickEvent = this.moveClick.bindAsEventListener(this);
    this.sizeUpClickEvent = this.sizeUpClick.bindAsEventListener(this);
    this.sizeDownClickEvent = this.sizeDownClick.bindAsEventListener(this);
    
    this.aspectClickEvent = this.aspectClick.bindAsEventListener(this);
    
    this.advClickEvent = this.advClick.bindAsEventListener(this);
    this.colorWayChangeEvent = this.colorWayChange.bindAsEventListener(this);
    
    this.centerHEvent = this.centerHClick.bindAsEventListener(this);
    this.centerVEvent = this.centerVClick.bindAsEventListener(this);
    this.centerEvent = this.centerClick.bindAsEventListener(this);
    
    this.workingAssetLoaded = false;
    
    if(options.c != null) {
      this.colors = options.c.split(":");
    } else {
      this.colors = null;
    }
    this.colorCount = (options.cc == null) ?  this.asset.defaultColorCount : options.cc ;
    
    this.transparentColor = options.tc;
    if(this.transparentColor==null) {
      this.transparentColor = "Transparent";
    }

    this.rotated = false;
    this.setName(asset.name);
    this.oldTabName = "effects";
    this.currentSingleEffect = null;
    this.effects = options.eff;
    if(this.effects == null) {
      this.effects = {};
    }
    this.effectId = hashFirstKey(this.effects);
    this.effect = hashFirstElement(this.effects);
    
    
    this.borderId = options.b;
    if(this.borderId==null) {
      this.borderId = 0;
    }
    
    var rw = this.width;
    var rh = this.height;
    if(this.options.rw != null && this.options.rh != null) {
      rh = parseFloat(this.options.rh);
      rw = parseFloat(this.options.rw);
    }
    var deg = options.rot == null ? 0 : parseFloat(options.rot);
    this.setReferenceRotation(deg, true, {w:rw, h:rh});
    
    this.digitize = false;
    this.splitColors = false;
    if(this.cViewProcess.productProcess.process.isWilcomEMB()) {
      this.stitchCount = options.stc;
      if((this.cViewProcess.productProcess.process.isWilcomEMB())&&(!this.isWilcomEMB && !this.isSavedDigitized)) {
        //its a digitized asset....
        
        
        this.splitColors = (options.spc == "1");
        if(!this.splitColors) {
          if(options.po == null) {
            this.percentOpaque = 1.0;
          } else {
            this.percentOpaque = parseFloat(options.po);
          }
        }
        
        this.digitize = true;
        this.digitizationFee = null; //options.dig_fee;
        
        this.digitizedAsset = d.cart.registerDigitizedAsset(this);
        var primary = this.digitizedAsset.primaryItem();
        if(primary != this) {
          //check if the primary is rotated and if so get the real width/height...
          if((primary.rotated)&&(primary.rotatedRef != null)) {
            var dims = primary.getRealDims();
            this.height = dims.h;
            this.width = dims.w;
          } else {
            this.height = primary.height;
            this.width = primary.width;
          }
          
          this.maintainAspect = primary.maintainAspect;
          
          if(!(options.l && options.t)) {
            var w = this.cViewProcess.productProcess.fullWidth;
            var h = this.cViewProcess.productProcess.fullHeight;
            this.top = (h - this.height) / 2;
            this.left = (w - this.width) / 2;
          }
          
          
          this.colors = primary.colors;
          this.colorCount = primary.colorCount
          this.transparentColor = primary.transparentColor;
          log("Set Details from primary digitization: w=" + this.width + ", h=" + this.height);
          this.setReferenceRotation(deg, true, {w:this.width, h:this.height});
        }
        
        this.refreshDigitizedAsset(DIG_EVENT_STARTUP);
        this.updateDigitizationCosts();
      } else {
        if(d.mode == DESIGNER_MODE_AMEND) {
          if(this.isSavedDigitized) {
            if((this.asset.imageType != 2)&&(this.asset.digitizedState < 3)) {
              //amending the order that ordered the digitization... lock the size...
              this.allowResize = false;
            }
          }
        }
        this.stitchCount = parseInt(this.stitchCount, 10);
        
      }
    }
    this.colorWay = options.cw;
    if(d.mode == DESIGNER_MODE_AMEND) {
      if(this.asset.hasCost()) {
          this.canModifyDesign = false;
      }
    }
    if(d.designerOptions.showImageDims()) {
      this.setFeature("dim", true);
    } else {
      this.setFeature("dim", false);
    }
    if(d.designerOptions.showImageAdvanced()) {
      this.setFeature("adv", true);
    } else {
      this.setFeature("adv", false);
    }
    
    
  },
  
  initialiseManagePane: function ImgItem_initialiseManagePane() {
    log("ImageItem.initialiseManagePane()");
    var self = this;
    ImageItem.parentClass.method("initialiseManagePane").call(this);
    // bind objects
    
    
    this.thumb = $("mp1_" + this.elId + "_img");
    if(this.thumb!=null) {
      var sizes = this.asset.scale(69, 69);
      setTransPng(this.thumb, this.asset.getSUrl(), parseInt(sizes.w, 10), parseInt(sizes.h, 10));
      //this.thumb.src = this.asset.getUrl();
      //this.thumb.width = parseInt(sizes.w, 10);
      //this.thumb.height = parseInt(sizes.h, 10);
    }
    this.moveButtons = [];
    for(var i=0; i < 9; i++) {
      var b = $("mp1_" + this.elId + "_move_" + i);
      Event.observe(b, "click", this.moveClickEvent);
      Event.observe(b, "dblclick", this.moveClickEvent);
      this.moveButtons.push(b);
    }
    //this.moveButton.style.cursor = "pointer";
    
    
    this.sizeDown = $("mp1_" + this.elId + "_sizedown");
    this.sizeDown.style.cursor = "pointer";
    Event.observe(this.sizeDown, "click", this.sizeDownClickEvent);
    Event.observe(this.sizeDown, "dblclick", this.sizeDownClickEvent);
    
    this.sizeUp = $("mp1_" + this.elId + "_sizeup");
    this.sizeUp.style.cursor = "pointer";
    Event.observe(this.sizeUp, "click", this.sizeUpClickEvent);
    Event.observe(this.sizeUp, "dblclick", this.sizeUpClickEvent);
    
    this.aspectEl = $("mp1_" + this.elId + "_ar");
    Event.observe(this.aspectEl, "click", this.aspectClickEvent);
    
    if(this.maintainAspect) {
      this.aspectEl.checked = true;
    }
    
    this.centerHEl = $("mp1_" + this.elId + "_center_h");
    Event.observe(this.centerHEl, "click", this.centerHEvent);    
    
    this.centerVEl = $("mp1_" + this.elId + "_center_v");
    Event.observe(this.centerVEl, "click", this.centerVEvent);
    
    this.centerEl = $("mp1_" + this.elId + "_center");
    Event.observe(this.centerEl, "click", this.centerEvent);
    
    this.rotateContainer = $("mp1_" + this.elId + "_rotate_container");
    this.rotateImg = $("mp1_" + this.elId + "_rotate");
    this.rotateImg.style.cursor = "pointer";
    this.rotateImgLeft = $("mp1_" + this.elId + "_rotate_l");
    this.rotateImgLeft.style.cursor = "pointer";
    this.rotateImgRight = $("mp1_" + this.elId + "_rotate_r");
    this.rotateImgRight.style.cursor = "pointer";
    
    this.rotateImgTxt = $("mp1_" + this.elId + "_rotate_t");
    
    this.rotateWidget = new  RotateWidget(this.rotateContainer, this.rotateImg, this.rotateImgLeft, this.rotateImgRight, this.rotateImgTxt, function(d) { self.rotationChanged(d); }, function(d) { self.rotationChanging(d);});
    
    if(this.options.rot) {
      this.rotateWidget.setValue(parseInt(this.options.rot, 10));
    }
    if(this.getRotation() != 0) {
      this.rotated = true;
      this.aspectEl.disabled = true;
    } else {
      this.rotated = false;
    }
    
    //this.qualitySlider = $("mp1_" + this.elId + "_q_sl");
    this.qualitySliderContainer= $("mp1_" + this.elId + "_q_c_a");
    
    //this.advButton = $("mp1_" + this.elId + "_adv");
    //this.advButton.style.cursor = "pointer";
    this.advLink = $("mp1_" + this.elId + "_adv_link");
    Event.observe(this.advLink, "click", this.advClickEvent);
    
    this.advTable = $("mp1_" + this.elId + "_advt");
    
    
    this.initColors();
    
    var tc = $("ic_effects_" + this.elId);
    if(tc != null) {
      tc.onclick = function() { 
        self.selectTab("effects", self.elId);
        return false; 
      };
    }
    tc = $("ic_border_" + this.elId);
    if(tc != null) {
      tc.onclick = function() { self.selectTab("border", self.elId); return false; };
    }
    
    
    
    if((this.itemType==0) &&(((this.isWilcomEMB)||(this.digitize)||(this.isSavedDigitized)))) {
      
      if(this.isWilcomEMB) {
        
        $("mp1_" + this.elId + "_trans_container").style.display="none";
        $("mp1_" + this.elId + "_color_row").style.display="none";
        
        this.colorwayEl = $("mp1_" + this.elId + "_colorway") ;
        if(this.asset.colorWays != null) {
          
          for(var i=0; i < this.asset.colorWays.length; i++) {
            var cw = this.asset.colorWays[i];
            addSelectOption(this.colorwayEl, cw, i, this.colorWay ); 
          }
          if(this.asset.colorWays.length > 1) {
            $("mp1_" + this.elId + "_colorways").style.display="";
          }
          Event.observe(this.colorwayEl, "change", this.colorWayChangeEvent) ;
        }
        $("mp1_" + this.elId + "_ratio_container").style.display="none";
      } else if(this.isSavedDigitized) {
        $("mp1_" + this.elId + "_trans_container").style.display="none";
        $("mp1_" + this.elId + "_color_row").style.display="none";
      } else if(!this.splitColors) {
        $("mp1_" + this.elId + "_trans_container").style.display="";
        $("mp1_" + this.elId + "_color_row").style.display="none";
      }
      $("mp1_" + this.elId + "_q_container").style.display="none";
      if (d.mode == DESIGNER_MODE_CONFIGURE) { 
        this.setFeature("effects_content", false);
      } else { 
        this.setFeature("adv", false);
        //$("mp1_" + this.elId + "_adv_link_container").style.display="none"; 
      }
      
      if(this.digitize) {
        this.updateDigitizationNotice(true);
        this.refreshDigitizedAsset(0);
      }
    }
    this.initFeature("size_arrows");
    this.initFeature("adv");
    if((this.itemType==0)&&(d.designerOptions.showImageAdvanced())&&(d.designerOptions.showImageAdvancedOpen())) {
      this.advClick();
    }
    //this.initEBPane();
  },
  
  initEBPane: function ImgItem_initEBPane() {
    var div = $("i_effects_list_" + this.elId);
    if(div!= null) {
      var html = '<ul>';
      
      cell = '<li class="et_effect" align="center" valign="top" id="i_ef_' + this.elId + '_-1" onmouseover="ieOMO(this);" onmouseout="ieOMOu(this);"  onclick="ieOMc(' + this.id + ',-1);"><img src="/images/noborder.gif"/>';
      cell += "<label>No Effect</label>";
      cell += '</li>';
      
      html += cell;
        
        
      for(var i=0;i< imageEffects.length;i++) {
        var effect = imageEffects[i];
        var sel = '';
        if(this.effectId == effect[0]) {
          sel = ' selected';
        }
        cell = '<li class="et_effect' + sel + '" align="center" valign="top" id="i_ef_' + this.elId + '_' + effect[0] + '" onmouseover="ieOMO(this);" onmouseout="ieOMOu(this);"  onclick="ieOMc(' + this.id + ',' + i + ');"><img src="/ppr/images/effects/' + effect[2] + '"/>';
        cell += "<label>"+effect[1]+"</label>";
        cell += '</li>';
        
        html += cell;
      }
      html += "";
      
      div.innerHTML = html;
      
      d.effectIconsLoaded = true;
    
    
    }
  },
  
  initColors: function ImgItem_initColors() {
    var colors = this.asset.getColors(this.colorCount);
    var allowChangeColor = true;
    if(this.isSavedDigitized) {
      allowChangeColor = false;
    } else if(this.digitize) {
      if(this.digitizedAsset.primaryItem() != this) {
        allowChangeColor = false;
      }
    }
    var buttonClass = allowChangeColor ? "mp_color_button" : "mp_color_display"; 
    if(colors != null) {
      
      var tr = $("mp1_" + this.elId + "_colors");
      if(tr == null) {
        return;
      }
      if(this.colors == null) {
        this.colors = [];
      }
      var validColorCount = 0;
      for(var i=0;i<colors.length; i++) {
        var color =colors[i];
        if(color != null) {
          validColorCount ++;
          if(this.colors.length <= i) {
            this.colors.push(color);
          } else if((this.colors[i] == null)||(this.colors[i] == "")) {
            this.colors[i] = color;
          }
        }
      }
      if(this.colors.length > validColorCount) { //the locally defined colors is larger....it should equal assets color count...
        this.colors = this.colors.slice(0, validColorCount);
      }
      tr.innerHTML = "";
      for(var i=this.colors.length-1; i >=0; i--) {
        var color = this.colors[i];
        
        var td = document.createElement("LI");
        if(color == "Transparent") {
          td.innerHTML = '<div class="' + buttonClass + '" style="width:10px;display:inline;margin-right:2px;font-size:8px;height:10px;background-image: url(/ppr/images/trans-display-small.gif);"><img src="' + d.pathPrefix + '/images/trans.gif" width="10" height="10" border="0"/></div>';
        } else {
          td.innerHTML = '<div class="' + buttonClass + '" style="width:10px;display:inline;margin-right:2px;font-size:8px;height:10px;background-color:' + color + ';"><img src="' + d.pathPrefix + '/images/trans.gif" width="10" height="10" border="0"/></div>';
        }
        tr.appendChild(td);
        var div = td.firstChild;
        if(allowChangeColor) {
          div.onclick = new bindFunc({div: div, idx: i, item: this}, function(l) { 
            l.item.setColorClick(l.div, l.idx);
          });    
        }
      }
      $("mp1_" + this.elId + "_color_row").style.display="";
    } else {
      var tcont = $("mp1_" + this.elId + "_trans_container");
      if(tcont != null) {
        tcont.style.display="";
        this.transColorButton = $("mp1_" + this.elId + "_transparent_color");
        this.transColorButton.className = buttonClass;
        if(this.transparentColor=="Transparent") {
          this.transColorButton.style.backgroundColor = "";
          this.transColorButton.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
        } else {
          this.transColorButton.style.backgroundColor = this.transparentColor;
          this.transColorButton.style.backgroundImage = "";
        }
      
        if(allowChangeColor) {
          var self = this;
          this.transColorButton.onclick =function() { 
            pwColorPicker.selectColor(null, self.transColorButton, this.transparentColor,  function(c) { self.colorCallback(c, self.transColorButton, -1); },{allow_transparency:true});
              
            //showColorPicker(self.transColorButton, function(c) { self.colorCallback(c, self.transColorButton, -1); }, true, "No Transparency");
          };
        }
      }
    }
  },
  
  setColorClick: function ImgItem_setColorClick(el, idx) {
    var self = this;
    var curColor = idx == -1 ? this.transparentColor : this.colors[idx];
    //showColorPicker(el, function(c) { self.colorCallback(c, el, idx); }, true);
    pwColorPicker.selectColor(this.cViewProcess.productProcess.id, el, curColor,  function(c) { self.colorCallback(c, el, idx); }, {allow_transparency:true});
  },
  
  colorWayChange: function ImgItem_colorWayChange(event){
    this.colorWay = this.colorwayEl.value ;
//    log(this.colorWay) ;
    this.updateImageSrc();
    this.setDirty();
  },
  
  colorCallback: function ImgItem_colorCallback(color, el, idx) {
    if(color=="Transparent") {
      el.style.backgroundColor = "";
      el.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
    } else {
      el.style.backgroundColor = color;
      el.style.backgroundImage = "";
    }
    if(idx==-1) {
      this.transparentColor = color;
      if(this.digitize && !this.splitColors) {
        this.asset.getOpacity(color, function(percent) {
            this.percentOpaque = parseFloat(percent);
            this.updateDigitizationNotice(true,true);
        }.bind(this));
      }
    } else {
      this.colors[idx] = color;
    }
    this.updateImageSrc();
    this.setDirty();
    this.updateDigitizationNotice(true,true);
    if(this.digitizedAsset != null) {
      this.digitizedAsset.changed(this,DIG_EVENT_COLOR);
    }
    //d.itemChanged(true);
  },
  
  
  buildElement: function ImgItem_buildElement() {
    if ($("d_" + this.elId)) $("d_" + this.elId).remove();
    if(this.useCanvas) {
      this.el = document.createElement("SPAN");
      this.canvas = Raphael(this.el, 10, 10);
    } else {
      this.el = document.createElement("IMG");
    }
    this.el.id = "d_" + this.elId;
    this.el.style.display = "none";
    this.el.style.position = "absolute";
    this.el.style.cursor="pointer";
    var deg = this.getRotation();
    if(this.useCanvas) {
      this.canvasImg = this.canvas.image(this.asset.getUrl(), 0, 0, 10, 10);
      this.canvasImgWidth = 10.0;
      this.canvasImgHeight = 10.0;
    } else {
      if(deg==0) {
        setTransPng(this.el, this.asset.getUrl());
      } else {
        this.el.src = "/images/trans.gif"; //it looks crap when the image appears unrotated/oversize...
      }
    }
    this.cViewArea.canvas.appendChild(this.el);
    
    //var self = this;
    
    if((deg==0)&&(this.colors == null)) {
      this.asset.loadWorkingAsset(function() { 
        this.setSrc(this.asset.getWUrl());
        log("loaded working asset");
      }.bind(this));
    } else {
      this.updateImageSrc();
      this.asset.loadWorkingAsset(function() { 
        log("loaded working asset");
      }.bind(this));
    }
    
    this.el.style.zIndex = this.getElementZIndex();
    
    return this.el;
  },
  
  del: function ImgItem_del() {
    if(this.moveButtons != null) {
      for(var i=0; i < this.moveButtons.length; i++) {
        Event.stopObserving(this.moveButtons[i], "click", this.moveClickEvent);
        Event.stopObserving(this.moveButtons[i], "dblclick", this.moveClickEvent);
      }
      Event.stopObserving(this.sizeDown, "click", this.sizeDownClickEvent);
      Event.stopObserving(this.sizeDown, "dblclick", this.sizeDownClickEvent);
      Event.stopObserving(this.sizeUp, "click", this.sizeUpClickEvent);
      Event.stopObserving(this.sizeUp, "dblclick", this.sizeUpClickEvent);
      Event.stopObserving(this.aspectEl, "click", this.aspectClickEvent);
      if(this.colorwayEl != null) {
        Event.stopObserving(this.colorwayEl, "change", this.colorWayChangeEvent) ;
      }
      Event.stopObserving(this.centerHEl, "click", this.centerHEvent);    
      Event.stopObserving(this.centerVEl, "click", this.centerVEvent);
      Event.stopObserving(this.centerEl, "click", this.centerEvent);
      Event.stopObserving(this.advLink, "click", this.advClickEvent);
    } 
    if(this.rotateWidget!=null) {
      this.rotateWidget.del();
    }
    if(this.digitizedAsset != null) {
      this.digitizedAsset.removeItem(this);
    }
    ImageItem.parentClass.method("del").call(this);
  },
  
  setSrc: function ImgItem_setSrc(url, updateInfo) {
    var self = this;
    var rotCount = this.rotationCounter;
    var rotRef = this.rotatedRef;
    this.currentSrc = url;
    
    if(this.el) {
      var iel = this.useCanvas ? null : this.el;
      setTransparentImage(this.el, iel, url, function() {
          if(this.useCanvas) {
            self.canvasImg.attr({src:url});
          } else {
            if(self.itemType == 0) {
              self.translateRotated(rotCount, rotRef);
              self.checkPosition();
            }
          }
          if(updateInfo != null) {
            if(updateInfo.checkCropIssue == true) {
              window.setTimeout( function() {
                  var refreshJoin = "?";
                  if(self.currentSrc.indexOf("?") != -1) {
                    refreshJoin = "&";
                  }
                  var newUrl = self.currentSrc + refreshJoin + "refresh";
                  log("Resetting Src Because of useCroppedImageHack to " + newUrl);
                  if(this.useCanvas) {
                    self.canvasImg.attr({src:newUrl});
                  } else if(useAlphaHack) {
                    $(self.el.id).style.fontSize = "1px";
                    $(self.el.id).style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + newUrl + '", sizingMethod="scale")';
                  } else {
                    $(self.el.id).src = newUrl;
                  }
              }, 1000);
              
            }
          }
      });
    }
  },
  
  getSrc: function ImgItem_getSrc() {
    if(this.currentSrc == null) {
      if(this.cView.configuredProduct.id > 0) {
        return "/image_gen/get_configured_product_asset?cid=" + this.id + "&cid=" + this.cView.configuredProduct.id + "&cp=" + this.cView.configuredProduct.id + "&cpv=" + this.cView.id + "&cpva=" + this.cViewArea.id + "&cpvap=" + this.cViewProcess.id;
      } else {
        return null;
      }
    } else {
      return this.currentSrc;
    }
  },
  
  select: function ImgItem_select(dontEnterCustomisePane, event) {
    ImageItem.parentClass.method("select").call(this, dontEnterCustomisePane,event);
  },
  
  moveClick: function ImgItem_moveClick(event) {
    Event.stop(event);
    var liEl = Event.findElement(event, "li");
    var deltaX = this.cViewProcess.productProcess.fullWidth / 20;
    var deltaY = this.cViewProcess.productProcess.fullHeight / 20;
    
    var vDelta = liEl.className.substr(0,1);
    var hDelta = liEl.className.substr(2,1);
    
    if(vDelta == "t") {
      this.top -= deltaY;
    } else if(vDelta == "b") {
      this.top += deltaY;
    }
    if(hDelta == "l") {
      this.left -= deltaX;
    } else if(hDelta == "r") {
      this.left += deltaX;
    }
    
    var newPositionType = this.getItemPositioning();
    
    if(newPositionType == 1) {
      log("outside!!!");
      this.del();
      return;
    }
      
      
    this.setPosition();
    if(this.selected) {
      this.autoRepositionHandles();
    }
    this.setDirty();
    
  },
  
  sizeUpClick: function ImgItem_sizeUpClick(event) {
    Event.stop(event);
    if(!this.allowResize) {
      return;
    }
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      var hinc = this.rotatedRef.h / 50;
      var maxHeight = this.rotatedRef.h + (10 * hinc);
      if(this.height < maxHeight) {
        this.height += hinc;
        var winc = this.rotatedRef.w / 50;
        this.width += winc;
        this.left -= winc/2;
        this.top -= hinc/2;
      } else {
        log("max height (" + maxHeight + ") reached");
        return;
      }
    } else {
      var dX = this.width / 20;
      var dY = this.height / 20;
      
      this.width += dX;
      this.height += dY;
      this.left -= dX/2;
      this.top -= dY/2;
      if(!this.rotated) { //the aspect ratio could have changed... recalc ref rotation data
        this.setReferenceRotation(0, true);
      }
      this.updateDigitizationNotice(true,true);
    }
    
    
    this.setPosition();
    if(this.selected) {
      this.autoRepositionHandles();
    }
    this.setDirty();
    //d.itemChanged(true);
    if(!this.checkQuality()) {
      d.showQualityWarning();
    }
  },
  
  sizeDownClick: function ImgItem_sizeDownClick(event) {
    Event.stop(event);
    if(!this.allowResize) {
      return;
    }
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      
      
      var hinc = this.rotatedRef.h / 50;
      var minHeight = this.rotatedRef.h - (10 * hinc);
      if(this.height > minHeight) {
        this.height -= hinc;
        var winc = this.rotatedRef.w / 50;
        this.width -= winc;
        this.left += winc/2;
        this.top += hinc/2;
      } else {
        log("min height (" + minHeight + ") reached");
        return;
      }
    } else {
      var dX = this.width / 20;
      var dY = this.height / 20;
      
      this.width -= dX;
      this.height -= dY;
      this.left += dX/2;
      this.top += dY/2;
      if(!this.rotated) { //the aspect ratio could have changed... recalc ref rotation data
        this.setReferenceRotation(0, true);
      }
      this.updateDigitizationNotice(true,true);
    }
    
    
  
    this.setPosition();
    if(this.selected) {
      this.autoRepositionHandles();
    }
    this.setDirty();
    //d.itemChanged(true);
    
  },
  
  aspectClick: function ImgItem_aspectClick(event) {
    this.maintainAspect = this.aspectEl.checked;
    if(this.maintainAspect) {
      var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
      this.setHeight(parseInt(dims.h, 10));
      var dims = d.fromCanvasDims(this.getDims(),this.cViewProcess.productProcess);
      this.width = dims.w;
      this.autoRepositionHandles();
      this.updateDigitizationNotice(true,true);
    }
  },
  
  centerClick: function ImgItem_centerClick(event) {
    var w =  this.cViewProcess.productProcess.fullWidth;
    var h = this.cViewProcess.productProcess.fullHeight;
    this.top = (h - this.height) / 2;
    this.left = (w - this.width) / 2;
    this.setPosition();
    this.autoRepositionHandles();
    this.setDirty();
    Event.stop(event);
  },  
  
  centerVClick: function ImgItem_centerVClick(event) {
    var h = this.cViewProcess.productProcess.fullHeight;
    this.top = (h - this.height) / 2;
    this.setPosition();
    this.autoRepositionHandles();
    this.setDirty();
    Event.stop(event);
  },
  
  centerHClick: function ImgItem_centerHClick(event) {
    var w =  this.cViewProcess.productProcess.fullWidth;
    this.left = (w - this.width) / 2;
    this.setPosition();
    this.autoRepositionHandles();
    this.setDirty();
    Event.stop(event);
  },
  
  advClick: function ImgItem_advClick(event) {
    if(this.advTable.style.display=="none") {
      this.initAdvanced();
      this.advTable.style.display="";
	  this.advLink.className="alt";
      //this.advButton.src=d.pathPrefix + "/images/mp/arrow_up.gif";
    } else {
      this.advTable.style.display="none";
	  this.advLink.className=null;
      //this.advButton.src=d.pathPrefix + "/images/mp/arrow_down.gif";
    }
    if(event != null) Event.stop(event);
    return false;
  },
  
  initAdvanced: function ImgItem_initAdvanced() {
    if(!this.advancedInit) {
      this.initEBPane();
      this.advancedInit = true;
    }
  },
  
  rotationChanged: function ImgItem_rotationChanged(deg) {
    
    if(this.rDelay != null) {
      window.clearTimeout(this.rDelay);
    }
    if(this.rotationCounter==null) {
      this.rotationCounter = 1;
    } else {
      this.rotationCounter ++;
    }
    this.setReferenceRotation(deg);
  
    var x = this.left + this.width / 2;
    var y = this.top + this.height / 2;
    
    
    
    log("rotationChanged: w=" + this.rotatedRef.w + "(" + this.width + ")" + " ,h=" + this.rotatedRef.h + "(" + this.height + ") ,deg=" + this.rotatedRef.angle);
    
    this.width = this.rotatedRef.w;
    this.height = this.rotatedRef.h;
    this.left = x - this.rotatedRef.w/2;
    this.top = y - this.rotatedRef.h/2;
    
    //this.allowResize = false; //should this be commented out?
    this.removeHandles();
    this.addHandles();
    if(this.useCanvas) {
      this.setDirty();
    } else {
      var self = this;
      this.rDelay = window.setTimeout( function() { 
        self.updateImageSrc();
        self.setDirty();
      }, 100);
    }
    
    log(deg);
    if(deg != 0) {
      this.rotated = true;
      this.aspectEl.disabled = true;
    } else {
      this.rotated = false;
      this.aspectEl.disabled = this.lockedDigitization();
    }
    this.updateTitleSize();
    this.checkPosition();
  },
  
  rotationChanging: function(deg) {
    if(this.useCanvas) {
      if(this.rotationCounter==null) {
        this.rotationCounter = 1;
      } else {
        this.rotationCounter ++;
      }
      this.setReferenceRotation(deg);
    
      if(deg != 0) {
        this.rotated = true;
        this.aspectEl.disabled = true;
      } else {
        this.rotated = false;
        this.aspectEl.disabled = this.lockedDigitization();
      }
    
      var x = this.left + this.width / 2;
      var y = this.top + this.height / 2;
      
      
      
      //log("rotationChanged: w=" + this.rotatedRef.w + "(" + this.width + ")" + " ,h=" + this.rotatedRef.h + "(" + this.height + ") ,deg=" + this.rotatedRef.angle);
      
      this.width = this.rotatedRef.w;
      this.height = this.rotatedRef.h;
      this.left = x - this.rotatedRef.w/2;
      this.top = y - this.rotatedRef.h/2;
      
      //this.allowResize = false; //should this be commented out?
      this.removeHandles();
      //this.addHandles();
      this.quickSetPosition();
    }
  },
  
  setReferenceRotation: function ImgItem_setReferenceRotation(deg, ignoreFrom, passedDims) {
    var dims = {};
    if(passedDims != null) {
      dims = passedDims;
    } else if((this.rotatedRef != null)&&(ignoreFrom!=true)) {
      dims = this.fromRotatedDims({w: this.width, h:this.height}, this.rotatedRef.angle); //toRotatedDims needs the actual dims, not the bounding box..
      //log("setReferenceRotation (from): w=" + dims.w + "(" + this.width + ")" + " ,h=" + dims.h + "(" + this.height + ") ,deg=" + this.rotatedRef.angle);
      
    } else {//first time called...TODO: handle loading rotated image...
      dims.w = this.width;
      dims.h = this.height;
      //log("setReferenceRotation (init): w=" + dims.w + " ,h=" + dims.h);
    }
    this.rotatedRef = this.toRotatedDims(dims, deg);
    //log("toRotatedDims: w=" + this.rotatedRef.w + " ,h=" + this.rotatedRef.h + " deg=" + deg);
    this.rotatedRef.a = parseFloat(this.rotatedRef.w) / parseFloat(this.rotatedRef.h);
  },
  
  //after a rotation we need to recalc the size...
  translateRotated: function ImgItem_translateRotated(rotCount, rotatedRef) {   
    if((this.rotationCounter == rotCount)&&(this.rotationCounter != null)) {
      this.rotationCounter = null; //only do this once... we need to rotate again for this to get called...
      if(rotatedRef != null) {
        var x = this.left + this.width / 2;
        var y = this.top + this.height / 2;
        
        this.width = rotatedRef.w;
        this.height = rotatedRef.h;
        this.left = x - this.rotatedRef.w/2;
        this.top = y - this.rotatedRef.h/2;
      }
      var dims = this.toCanvasDims(); 
      
      this.setHeight(parseInt(dims.h, 10));
      this.setWidth(parseInt(dims.w, 10));
      this.setTop(parseInt(dims.t, 10));
      this.setLeft(parseInt(dims.l, 10));
      //this.allowResize = true; //should this be commented out?
      if(this.selected) {
        this.removeHandles();
        this.addHandles();
      }
    }
    
  },
  
  //convert the unrotated size to the bounding box of the rotated dims
  toRotatedDims: function ImgItem_toRotatedDims(dims, angle) {
    var pi2 = Math.PI / 2.0;

    var oldWidth = parseFloat(dims.w);
    var oldHeight = parseFloat(dims.h);

    if(angle == null) {
      angle = parseFloat(this.getRotation());
    }
    // Convert degrees to radians
    var theta = angle * Math.PI / 180.0;
    var locked_theta = theta;

    // Ensure theta is now [0, 2pi)
    while (locked_theta < 0.0) {
      locked_theta += 2 * Math.PI;
    }

    
    var adjacentTop=0, oppositeTop=0;
    var adjacentBottom=0, oppositeBottom=0;

    // We need to calculate the sides of the triangles based
    // on how much rotation is being done to the bitmap.
    //   Refer to the first paragraph in the explaination above for 
    //   reasons why.
    if ((locked_theta >= 0.0 && locked_theta < pi2) || (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2))) {
      adjacentTop = parseFloat(Math.abs(Math.cos(locked_theta))) * oldWidth;
      oppositeTop = parseFloat(Math.abs(Math.sin(locked_theta))) * oldWidth;

      adjacentBottom = parseFloat(Math.abs(Math.cos(locked_theta))) * oldHeight;
      oppositeBottom = parseFloat(Math.abs(Math.sin(locked_theta))) * oldHeight;
    } else {
      adjacentTop = parseFloat(Math.abs(Math.sin(locked_theta))) * oldHeight;
      oppositeTop = parseFloat(Math.abs(Math.cos(locked_theta))) * oldHeight;

      adjacentBottom = parseFloat(Math.abs(Math.sin(locked_theta))) * oldWidth;
      oppositeBottom = parseFloat(Math.abs(Math.cos(locked_theta))) * oldWidth;
    }

    var newWidth = adjacentTop + oppositeBottom;
    var newHeight = adjacentBottom + oppositeTop;

    
    return {w:newWidth, h:newHeight, rw:oldWidth, rh:oldHeight, angle:angle, oa:oldWidth/oldHeight  };
  },
  
  //convert the size from the bounding box to the rotated dims (uses hack).. assumes dims are in actual size (not canvas size)
  //this looks at the ratio of the current bounding box to the last stored bounding box and applies that ratio to the real size stored at same time as last stored bounding box
  fromRotatedDims: function ImgItem_fromRotatedDims(dims, angle) {
    //compare the size between this.rotatedRef and size.. use ratio to determine final size..
    if(angle == null) {
      angle = parseFloat(this.getRotation());
    }
    if(this.rotatedRef == null) {
      this.setReferenceRotation(); //make a bounding box from the original asset size..
    }
    //compare ratio of heights...
    var ratio = parseFloat(this.rotatedRef.h) / parseFloat(dims.h);
    return {h: this.rotatedRef.rh / ratio, w: this.rotatedRef.rw / ratio};
    //this.height = this.asset.height / ratio;
    //this.width = this.asset.width / ratio;
  },
  
  //calls above using current dims to get non rotated dims of the object..
  getRealDims: function ImgItem_getRealDims() {
    return this.fromRotatedDims({w:this.width, h:this.height});;
  },
  
  //calls above using current dims to get non rotated dims of the object..
  getRealDesignerDims: function ImgItem_getRealDesignerDims() {
    return this.fromRotatedDims({w:parseFloat(this.el.style.width), h:parseFloat(this.el.style.height)});
  },
  
  updateImageSrc: function ImgItem_updateImageSrc() {
    var deg = this.getRotation();
    if(this.useCanvas) deg=0;
    if((deg==0)&&(this.colors == null)&&(!this.hasEffects())&&(this.borderId==0)&&(this.transparentColor=="Transparent")) {
      var url = this.asset.getWUrl() ;
      if (this.colorWay){
        log("*** Colorways") ;
        log(this.asset.colorWays) ;
        var cw_i = this.colorWay ;
        if(cw_i > 0) url = url.replace(/thumb300/, "colorway-"+cw_i) ;
      }
      this.setSrc(url);
    } else {
      var oa = (this.rotatedRef == null)? 0 : this.rotatedRef.oa;
      
      var cw_t = this.colorWay ? "&cw="+this.colorWay : "" ;
      
      var url = d.pathPrefix + "/designer/get_image?id=" + this.asset.id + this.getColorParam() + "&deg=" + deg + "&bid=" + this.borderId + 
        "&tc=" + encodeURIComponent(this.getIeSafeColorParam(this.transparentColor)) +cw_t+
        "&" + this.serialiseState("es").join('&') +"&oa=" + encodeURIComponent(oa);
      this.setSrc(url);
      
      //var colors = this.getColorParam();
      //this.setSrc(d.pathPrefix + "/designer/rotate_image?id=" + this.asset.id + "&deg=" + deg + colors);
      
    }
  },
  
  getColorParam: function ImgItem_getColorParam() {
    var colors = "";
    if(this.colors != null) {
      colors = "&colors=";
      for(var i=0;i<this.colors.length;i++) {
        if(i != 0) {
          colors +=encodeURIComponent(":");
        }
        colors += encodeURIComponent(this.getIeSafeColorParam(this.colors[i]));
      }
      colors += "&cc=" + this.colorCount;
    }
    return colors;
  },
  
  getIeSafeColorParam: function ImgItem_getIeSafeColorParam(color) {       
    if((color == null)||(color == "")) {
        return "";
    }
    if(color.substr(0, 1) == "#") {
      return color.substr(1, 6);
    }
    return color;
  },
  
  setHeight: function ImgItem_setHeight(height) {
    height = parseFloat(height);
    //log("setHeight:" + height);
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var elHeight = null;
    var elWidth = null;
    var retVal = true;
    if((this.isWilcomEMB)||(this.isSavedDigitized)) { //check the resize is within 20%
      
      var newHeight = this.fromCanvasDim(height);
      var delta = Math.abs(this.rotatedRef.h - newHeight);
      var twentyPercent = this.rotatedRef.h / 5;
      if(delta > twentyPercent + 1) {
        log("delta (" + delta + ") > twentyPercent (" + twentyPercent + ") changing size...");
        if(newHeight < this.rotatedRef.h) {
          newHeight = this.rotatedRef.h - twentyPercent;
        } else {
          newHeight = this.rotatedRef.h + twentyPercent;
        }
        height = this.toCanvasDim(newHeight);
        elHeight = height;
        //this.el.style.height = height + "px";
        
        var width = parseInt(height * this.rotatedRef.a, 10);
        elWidth = width;
        //this.el.style.width = width + "px";
        retVal=false;
        //return false;
      } else {
        //this.el.style.height = height + "px";
        elHeight = height;
        var width = parseInt(height * this.rotatedRef.a, 10);
        elWidth = width;
        //this.el.style.width = width + "px";
        retVal=true;
        //return true;
      }
    } else {
      //this.el.style.height = height + "px";
      elHeight = height;
      if((this.maintainAspect)||(this.rotated)) {
        var width = parseInt(height * this.rotatedRef.a, 10);
        //this.el.style.width = width + "px";
        elWidth = width;
      }
      retVal=true;
      //return true;
    }
    if(elHeight!= null) {
      this.el.style.height = elHeight + "px";
    } else {
      elHeight = parseInt(this.el.style.height);
    }
    if(elWidth!= null) {
      this.el.style.width = elWidth + "px";
    } else {
      elWidth = parseInt(this.el.style.width);
    }
    this.setCanvasSize(elWidth,elHeight);
    return retVal;
  },
  
  
  setWidth: function ImgItem_setWidth(width) {
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      return false;
    } else if((this.maintainAspect)||(this.rotated)) {
      return false;
    }
    this.el.style.width = width + "px";
    this.setCanvasSize(width, parseInt(this.el.style.height));
    return true;
  },
  
  setTop: function ImgItem_setTop(top) {
    this.el.style.top = top + "px";
  },
  setLeft: function ImgItem_setLeft(left) {
    this.el.style.left = left + "px";
  },
  
  resizeFinished:  function(event) {
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      this.releaseResizing();
      var dims = d.fromCanvasDims(this.getDims(),this.cViewProcess.productProcess);
      
      //var rDims = this.fromRotatedDims(dims);
      
      this.width = dims.w;
      this.height = dims.h;
      this.top = dims.t;
      this.left = dims.l;
      
      
      this.sizeChanged();
      this.checkPosition();
      //d.itemChanged(true);
      this.setDirty();
      d.selectTab('m','customize'); 
      //d.expandManage();
      
    } else {
      ImageItem.parentClass.method("resizeFinished").call(this);
      if(!this.checkQuality()) {
        d.showQualityWarning();
      }
      this.manualQuality = this.quality; //keep track of the last quality that we manually set...
      if(!this.rotated) { //the aspect ratio could have changed... recalc ref rotation data
        this.setReferenceRotation(0, true);
      }
      this.updateDigitizationNotice(true,true);
    }
  },
  
  //callback fired whenever the position/size has changed..
  checkPosition: function ImgItem_checkPosition() {
    if(this.digitize || this.isWilcomEMB || this.isSavedDigitized) {
      var pos = this.getItemPositioning();
      if(pos != 0) {
        if(this.cropWarning != true) {
          this.addAlert(0,"crop_error", "alert_crop_error", ml("Image outside bounds"), ml("Embroidery images must be completely inside the decoration area. You will need to either move or resize this item before you will be able to save."), true);
          this.cropWarning = true;
        }
        
      } else if(this.cropWarning == true) {
        this.removeAlert(0,"crop_error");
        this.cropWarning = false;
      }
    } else if(d.designerOptions.warnIfOutside()) {
      var pos = this.getItemPositioning();
      if(pos != 0) {
        if(this.cropWarning != true) {
          this.addAlert(1,"crop_warning", "alert_crop_error", ml("Image outside bounds"), ml("You have placed the image partially outside the decoration area and will be cropped."), true);
          this.cropWarning = true;
        }
      } else if(this.cropWarning == true) {
        this.removeAlert(1,"crop_warning");
        this.cropWarning = false;
      }
    }
  },
  
  
  sizeChanged: function ImgItem_sizeChanged() {
    ImageItem.parentClass.method("sizeChanged").call(this);
    this.setQuality();
  },
  
  //called after item is moved to another product
  validate: function ImgItem_validate() {
    this.setQualityWarning(true); //reset back to ok...
    this.checkPosition();
    this.sizeChanged();
  },
  
  setQuality: function ImgItem_setQuality() {
    if(this.isWilcomEMB || this.digitize || this.isSavedDigitized) {
      return;
    }
    
    if(this.qualitySliderContainer == null) {
      return;
    }
    var q = this.getQuality(this.width,this.height,this.cViewProcess.productProcess);
    this.quality = q;
    
    if(q < 0.05) {
      q = 0.05;
    } else if(q > 1) {
      q = 1;
    }
    
    if(q < 0.25) {
      //this.qualitySlider.src = d.pathPrefix + "/images/mp/quality_bad.gif";
	  this.qualitySliderContainer.className="quality_bad";
    } else if(q < 0.75) {
      //this.qualitySlider.src = d.pathPrefix + "/images/mp/quality_med.gif";
	  this.qualitySliderContainer.className="quality_med";
    } else {
      //this.qualitySlider.src = d.pathPrefix + "/images/mp/quality_good.gif";
	  this.qualitySliderContainer.className="quality_good";
    }
    
    //this.qualitySlider.style.width = (60.0 * q) + "px";
	this.qualitySliderContainer.style.width = (60.0 * q) + "px";
    
    this.setQualityWarning(this.checkQuality());
  },
  
  getQuality: function ImgItem_getQuality(width, height, productProcess) {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var dims = this.fromRotatedDims({w:width, h:height});
    var dpi = (parseFloat(this.asset.width) / parseFloat(dims.w)) * productProcess.perfectDPI;
    var dpi2 = (parseFloat(this.asset.height) / parseFloat(dims.h)) * productProcess.perfectDPI;
    //Not sure how this will work with skewing... probably better than below.... 
    //var dpi = (parseFloat(this.asset.width) / parseFloat(width)) * productProcess.perfectDPI;
    //var dpi2 = (parseFloat(this.asset.height) / parseFloat(height)) * productProcess.perfectDPI;
    if(dpi2 < dpi) {
      dpi = dpi2;
    }
    
    var pdpi = productProcess.perfectDPI;
    var mdpi = productProcess.minDPI;
    var ddpi = dpi - mdpi;
    
    var qRange = pdpi - mdpi;
    if(qRange == 0) {
      qRange = 1;
      ddpi += 1;
    }
    
    var q = ddpi / qRange;
    
    log("getQuality: dpi=" + dpi + " minDPI=" + mdpi + " perfectDPI=" + pdpi + " q=" + q + " dims.w=" + dims.w + " dims.h=" + dims.h + " this.width=" + this.width + " this.height=" + this.height + " this.asset.width=" + this.asset.width + " productProcess.perfectDPI=" + productProcess.perfectDPI);
    
    return q;
  },
  
  checkQuality: function ImgItem_checkQuality() {
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      return true;
    } else if(this.quality < 0.2) {
      return false;
    }
    return true;
  },
  
  minDims: function ImgItem_minDims(dims) {
    
    var startDPI = this.cViewProcess.productProcess.minDPI + ( (this.cViewProcess.productProcess.perfectDPI - this.cViewProcess.productProcess.minDPI)/3);
    
    
    
    var mScaleRefactor = this.cViewProcess.productProcess.perfectDPI  / startDPI;
    
    //var minScale = this.layoutManager.layoutData.lScale / mScaleRefactor;
    
    log("mScaleRefactor=" + mScaleRefactor);
    
    var mW = this.asset.width * mScaleRefactor;
    var mH = this.asset.height * mScaleRefactor;
    
    
    //log("mW=" + mW + " mH=" + mH + " dims.w=" + dims.w + " dims.h=" + dims.h);
    //var mW = this.asset.width * 72 / parseFloat(startDPI);
    //var mH = this.asset.height * 72 / parseFloat(startDPI);
    
    if(mW < dims.w) {
      dims.w = mW;
    }
    if(mH < dims.h) {
      dims.h = mH;
    }
    
    log(dims);
    return dims;
  },
  
  //check if we can rescale this item using desiredScale to work with process...
  //desiredScale is the rescale in design canvas sizes...
  checkScale: function ImgItem_checkScale(desiredScale, process) {
    
    
    //we have the rescale factor when in design canvas....
    
    //convert to design scale -> desiredScale -> process.unscale_from_design_scale
    
    var scale = this.cViewProcess.productProcess.designScale / desiredScale / process.designScale;
    
    var testHeight = parseFloat(this.height) * scale;
    var testWidth = parseFloat(this.width) * scale;
    var scaleFailed = false;
    if(!this.cViewProcess.productProcess.process.usesDPI) {
      //we cannot rescale this image more then 20%....
      var vDiff = parseFloat(testHeight - this.asset.height) / parseFloat(this.asset.height);
      var hDiff = parseFloat(testWidth - this.asset.width) / parseFloat(this.asset.width);
      
      if(Math.abs(vDiff) > 0.2 || Math.abs(hDiff) > 0.2 ) {
        log("rescale is over 20%: desiredScale=" + desiredScale + ", scale=" + scale + ", testHeight=" + testHeight + " asset.height=" + this.asset.height + ", testWidth=" + testWidth + " asset.height=" + this.asset.height);
        var reScale = 0;
        //we are scaling over 20%... lets cap it at 20%....
        if(Math.abs(vDiff) > Math.abs(hDiff)) {
          //vertically scaled more...
          if(vDiff > 0) {
            reScale = testHeight / (this.asset.height * 1.2);
          } else {
            reScale = testHeight / (this.asset.height * 0.8);
          }
        } else {
          if(hDiff > 0) {
            reScale = testWidth / (this.asset.width * 1.2);
          } else {
            reScale = testWidth / (this.asset.width * 0.8);
          }
        }
        testHeight /= reScale;
        testWidth /= reScale;
        scaleFailed = true;
        log("fixed scale: testHeight=" + testHeight + " testWidth=" +testWidth);
      }
    } else {
      
      // if(testHeight > this.height) { //only care if we scaled up....
        var quality = this.getQuality(testWidth,testHeight,process);
        log("Checking scale, scale=" +scale + " testHeight=" +testHeight + " testWidth=" + testWidth + " quality=" + quality + " this.asset.width=" + this.asset.width + " this.asset.height=" + this.asset.height );
        if((quality < 0.33) && (this.manualQuality == null || this.manualQuality > quality)) { //below 33% quality and below last manually set quality (if manually set)...
          var targetDPI = process.minDPI + (process.perfectDPI - process.minDPI) / 3; // 1/3 between min and perfect dpi
          
          var reScale = 0;
          var dpi = (parseFloat(this.asset.width) / parseFloat(testWidth)) * process.perfectDPI;
          var dpi2 = (parseFloat(this.asset.height) / parseFloat(testHeight)) * process.perfectDPI;
          if(dpi2 < dpi) {
            //height is the bigger problem...
            var betterHeight = parseFloat(this.asset.height) / (targetDPI / process.perfectDPI);
            reScale = testHeight / betterHeight;
            log("Check Scale Failed: targetDPI=" + targetDPI + " height_dpi=" + dpi2 + " width_dpi=" + dpi + " betterHeight=" + betterHeight + " reScale=" + reScale);
          } else {
            var betterWidth = parseFloat(this.asset.width) / (targetDPI / process.perfectDPI);
            reScale = testWidth / betterWidth;
            log("Check Scale Failed: targetDPI=" + targetDPI + " height_dpi=" + dpi2 + " width_dpi=" + dpi + " betterWidth=" + betterWidth + " reScale=" + reScale);
          }
          testHeight /= reScale;
          testWidth /= reScale;
          scaleFailed = true;
          log("Check Scale Failed: testHeight=" + testHeight + " testWidth=" + testWidth);
          
        } else {
          log("Scale check passed this.manualQuality=" + this.manualQuality);
        }
      // } else {
      //   log("Skipping scale check because rescaled smaller");
      // }
    }
    
    if(scaleFailed) { //we could rescale amount wanted.. we have recalced the final w/h.. lets get the scale back from that...
      scale = testHeight / parseFloat(this.height);
      log("scale1=" + scale);
      var scale2 = scale * process.designScale;
      log("scale2=" + scale2);
      
      desiredScale = this.cViewProcess.productProcess.designScale / scale2;
      log("scale3=" + desiredScale);
      
      scale2 = this.cViewProcess.productProcess.designScale / desiredScale / process.designScale;
      
      testHeight = parseFloat(this.height) * scale2;
      testWidth = parseFloat(this.width) * scale2;
      log("scale=" + scale + " scale2=" + scale2 + " testHeight=" + testHeight + " testWidth=" + testWidth + " check q=" + this.getQuality(testWidth,testHeight,process));
      
    }
    return desiredScale; //default behaviour
  },
  
  serializeToOptions: function Item_serializeToOptions(o, excludeRotation) {
    o = ImageItem.parentClass.method("serializeToOptions").call(this,o);
    var colors = "";
    if(this.colors != null) {
      for(var i=0;i<this.colors.length;i++) {
        colors += this.colors[i] + ":";
      }
    }
    o.c = colors;
    if(excludeRotation != true) {
      if(this.rotateWidget!=null) {
        o.rot = this.rotateWidget.value;
      } else if(this.options.rot) { 
        o.rot = this.options.rot;
      }
    }

    if(this.maintainAspect) {
      o.ar = "1";
    } else {
      o.ar = "0";
    }
    if(this.itemType==0) {
      var realSize = this.getRealDims();
      o.rw = realSize.w;
      o.rh = realSize.h;
      
      o.b=this.borderId;
      o.tc = this.transparentColor;
      
      if((this.digitize)&&(!this.splitColors)) {
        o.spc=0;
        o.po = this.percentOpaque;
      } else if(this.splitColors) {
        o.spc = 1;
      }
      if((this.digitize)&&(this.digitizedAsset.primaryItem() == this)) {
        o.pri_dig=1;
      }
      if((this.digitize)&&(this.digitizedAsset.product != null)) {
        o.dig_p = this.digitizedAsset.product.clientId;
      }
      
      
      if(this.colorCount != null) {
        o.cc = this.colorCount;  
      }
      
      if (this.colorWay != null) {
        o.cw = this.colorWay ;
      }
      
      //effects
      var eo = {};
      var hasEf = false;
      for(k in this.effects) {
        var e = this.effects[k];
        if(e != null) {
          var hasOpt = false;
          eoo = {};
          for(ck in e) {
            eoo[ck] = e[ck];
            hasOpt = true;
          }
          if(hasOpt) {
            hasEf = true;
            eo[k] = eoo;
          }
        }
      }
      if(hasEf) {
        o.eff = eo;
      }
    }
    return o;
  },
  
  
  serializeOLD: function ImgItem_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    
    ImageItem.parentClass.method("serialize").call(this,queryComponents,prefix);
    //this.parentCall("serialize",queryComponents);
    
    var colors = "";
    if(this.colors != null) {
      for(var i=0;i<this.colors.length;i++) {
        colors += this.colors[i] + ":";
      }
    }
    queryComponents.push(encodeURIComponent(prefix + "[c]") + "=" + encodeURIComponent(colors));
    
    if(this.rotateWidget!=null) {
      queryComponents.push(encodeURIComponent(prefix + "[rot]") + "=" + encodeURIComponent(this.rotateWidget.value));
    } else if(this.options.rot) { 
      queryComponents.push(encodeURIComponent(prefix + "[rot]") + "=" + encodeURIComponent(this.options.rot));
    }

    if(this.maintainAspect) {
      queryComponents.push(encodeURIComponent(prefix + "[ar]") + "=" + encodeURIComponent("1"));
    } else {
      queryComponents.push(encodeURIComponent(prefix + "[ar]") + "=" + encodeURIComponent("0"));
    }
    if(this.itemType==0) {
      var realSize = this.getRealDims();
      queryComponents.push(encodeURIComponent(prefix + "[rw]") + "=" + encodeURIComponent(realSize.w));
      queryComponents.push(encodeURIComponent(prefix + "[rh]") + "=" + encodeURIComponent(realSize.h));
      
      queryComponents.push(encodeURIComponent(prefix + "[b]") + "=" + this.borderId);
      queryComponents.push(encodeURIComponent(prefix + "[tc]") + "=" + encodeURIComponent(this.transparentColor));
      
      if((this.digitize)&&(!this.splitColors)) {
        queryComponents.push(encodeURIComponent(prefix + "[spc]") + "=0");
        queryComponents.push(encodeURIComponent(prefix + "[po]") + "=" + encodeURIComponent(this.percentOpaque));
      } else if(this.splitColors) {
        queryComponents.push(encodeURIComponent(prefix + "[spc]") + "=1");
      }
      if((this.digitize)&&(this.digitizedAsset.primaryItem() == this)) {
        queryComponents.push(encodeURIComponent(prefix + "[pri_dig]") + "=1");
      }
      
      if(this.colorCount != null) {
        queryComponents.push(encodeURIComponent(prefix + "[cc]") + "=" + encodeURIComponent(this.colorCount));  
      }
      
      if (this.colorWay != null) {
        queryComponents.push(encodeURIComponent(prefix + "[cw]") + "=" + encodeURIComponent(this.colorWay)) ;
      }
      this.serialiseState(prefix + "[eff]",  queryComponents);
    }
  },
  
  getRotation: function ImgItem_getRotation() {
    if(this.rotateWidget!=null) {
      return this.rotateWidget.value;
    } else if(this.options.rot) { 
      return this.options.rot;
    }
    return 0;
  },
  
  setAsset: function ImgItem_setAsset(asset) {
    this.asset = asset;
    this.setName(asset.name);
    if(this.el != null) {
      this.updateImageSrc();
      /*
      if(this.getRotation() != 0) {
        this.setSrc(d.pathPrefix + "/designer/rotate_image?id=" + this.asset.id + "&deg=" + this.getRotation() + "&ts=" + new Date().getTime());
      } else {
        this.setSrc(this.asset.getUrl());
        var self = this;
        this.asset.loadWorkingAsset(function() { self.setSrc(self.asset.getWUrl()); log("loaded set asset");});
      }
      */
    }
    if(this.thumb!=null) {
      var sizes = this.asset.scale(100,100);
      this.thumb.src = this.asset.getWUrl();
      this.thumb.width = parseInt(sizes.w, 10);
      this.thumb.height = parseInt(sizes.h, 10);
    }
    
    if(this.titleThumb!=null) {
      var sizes = this.asset.scale(15,20);
      this.titleThumb.src = this.asset.getWUrl();
      this.titleThumb.width = parseInt(sizes.w, 10);
      this.titleThumb.height = parseInt(sizes.h, 10);
    }
    if(this.el != null) {
      this.initColors();
      this.setQuality();
      this.setPosition();
    }
  },
  
  selectTab: function ImgItem_selectTab(tab, id) {
    if(this.oldTabName != null) {
      if(this.oldTabName==tab) {
        return;
      }
      var oldTab = $('i_' + this.oldTabName + "_tab_" + id);
      oldTab.style.display='none';
      oldTab = $('i_' + this.oldTabName + "_" + id);
      oldTab.className = "et_unselected_tab";
    }
    var newTab = $('i_' + tab + "_tab_" + id);
    newTab.style.display='';
    newTab = $('i_' + tab + "_" + id);
    newTab.className = "et_selected_tab";
    this.oldTabName = tab;
    if(!this.borderInit) {
      var html = '<ul>';
      for(var i=0; i < borderEffects.size(); i++) {
        var border = borderEffects[i];
        var url = "";
        if(border[0] == 0) {
          url = d.pathPrefix + "/images/noborder.gif";
        } else {
          url = d.pathPrefix + "/border/image/" + border[0] + "/thumb-ppr.gif";
        }
        var clazz = "et_effect";
        if(this.borderId == border[0]) {
          clazz = "et_effect select";
        }
        html += '<li class="' + clazz + '" id="i_b_' + this.elId + '_' + border[0] + '" onmouseover="ieOMO(this);" onmouseout="ieOMOu(this);"  onclick="ibOMc(' + this.id + ',' + i + ');"><img src="' + url + '"/></li>';
      }
      html += "</ul>";
      var div = $("i_borders_list_" + this.elId);
      div.innerHTML = html;
      this.borderInit = true;
      d.borderIconsLoaded = true;
    
    }
  },
  
  getWorkingState: function ImgItem_getWorkingState() {
    if(this.workingState == null) {
      this.workingState = {};
      for(k in this.effects) {
        var e = this.effects[k];
        var t = {};
        for(ek in e) {
          t[ek] = e[ek];
        }
        this.workingState[k] = t;
      }
    }
    return this.workingState;
  },
  
  effectClick: function ImgItem_effectClick(eIdx) {
    var oldId = this.effectId == null ? -1 : this.effectId;
    $("i_ef_" + this.elId + '_' + oldId).className = "et_effect";
    
    
    var effect = null;
    var eCode = null;
      
    if(eIdx == -1) {
      this.effectId = null;
      $("i_ef_" + this.elId + '_' + eIdx).className = "et_effect select";
    } else {
      effect = imageEffects[eIdx];
      eCode = effect[0];
      
      this.effectId = eCode;
      $("i_ef_" + this.elId + '_' + eCode).className = "et_effect select";
    }
    this.effect = {};
    
    this.loadEffectPanel(this.effect, effect);
    this.effectChanged();
  },
  
  loadEffectPanel: function ImgItem_loadEffectPanel(state, effect) {
    
    var html = "<div class='effect_controls'>";
    html+='<div class="effect_header">';
	
    if(effect!=null) {
      html += '<h4>' + effect[1] + '</h4>';
      html+='</div>';
    } else {
      html += '<h4>No Effects Applied</h4>';
      html+='</div>';
    }
    this.settingEffectsPanel = true;
    
    
    var self = this;
    var callback = function() { self.effectChanged(); };
    
    this.currentControls = {};
    this.currentEffect = effect;
    var lineHtml="";
    if(effect!=null) {
      for(var i=4; i < effect.length;i++) {
        var controlDef = effect[i];
        if(controlDef[2]!="P") {
          var clz = controlClasses[controlDef[2]];
          log(controlDef[2]);
          var control = new clz(controlDef[0], controlDef[1], effect, controlDef[3],callback, this.elId);
          this.currentControls[control.code] = control;
          
          lineHtml += '<li>' + control.buildHTML() + '</li>';
        } else {
          state[controlDef[1]] = "1";
        }
      }
    }
    
    html += ''+
    '<div class="et_list et_effect_control">'+
      '<div class="et_right">'+
        '<ul>'+lineHtml+'</ul>'+
        '<div class="et_submit">'+
        '</div>'+
      '</div>'+
    '</div>'+
    '</div>';
    
    var panel = $("i_aeffects_list_" + this.elId);
    panel.innerHTML = html;
    
    for(k in this.currentControls) {
      var control = this.currentControls[k];
      control.bindControls();
      control.loadFromState(state, true);
    }
    this.settingEffectsPanel = false;
  },
  
  effectChanged: function ImgItem_effectChanged() {
    if(this.settingEffectsPanel){
      return;
    }
    for(k in this.currentControls) {
      var control = this.currentControls[k];
      control.saveToState(this.effect);
    }
    this.effects = {};
    if(this.effectId != null) {
      this.effects[this.effectId] = this.effect;
    }
    
    this.updateImageSrc();
    this.setDirty();
  },
  
  serialiseState: function ImgItem_serialiseState(prefix, queryComponents) {
    if(prefix==null) {
      prefix = "t[" + this.id + "][eff]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }

    for(k in this.effects) {
      var e = this.effects[k];
      if(e != null) {
        log(e);
        for(ck in e) {
          //log("serialiseStateA:" + ck);
          queryComponents.push(encodeURIComponent(prefix + "[" + k + "][" + ck + "]") + "=" + encodeURIComponent(e[ck]));
        }
      }
      
    }
    
    return queryComponents;
  },
  
  hasEffects: function ImgItem_hasEffects() {
    return (this.effectId != null);
  },
  
  borderClick: function ImgItem_borderClick(bIdx) {
    var border = borderEffects[bIdx];
    if(this.borderId == border[0]) {
      return;
    }
    $("i_b_" + this.elId + '_' + this.borderId).className = "et_effect";
    this.borderId = border[0];
    $("i_b_" + this.elId + '_' + this.borderId).className = "et_effect select";
    this.updateImageSrc();
  },
  
  //find the percent of an image thats opaque...
  calculatePercentOpaque: function ImgItem_calculatePercentOpaque() {
    if(!this.splitColors) {
      return this.percentOpaque;
    } else {
      var totalPercent = parseFloat(this.asset.getMetaData(["split_colors", this.colorCount, "percent_transparent"], 0));
      var counts = this.asset.getMetaData(["split_colors", this.colorCount, "counts"], null);
      for(var i=0; i < this.colorCount; i++) {
        if((this.colors[i] != null) && (this.colors[i] == "Transparent")) {
          var colorPercent = counts[i];
          totalPercent += colorPercent;
        }
      }
      log("Percent Transparent = " + totalPercent);
      return 1.0 - totalPercent;
    }
  },
  
  //get area in sq inches of the image
  calculateArea: function ImgItem_calculateArea() {
    //get the real width/height of image
    var realSize = this.getRealDims();
    //convert to inches
    var dpi = parseFloat(this.cViewProcess.productProcess.perfectDPI);
    this.widthInches = parseFloat(realSize.w) / dpi;
    this.heightInches = parseFloat(realSize.h) / dpi;
    //make area
    log("dpi=" + dpi + ", widthInches=" + this.widthInches + ", heightInches=" + this.heightInches);
    var area = this.widthInches * this.heightInches;
    return area;
  },
  
  updateDigitizationCosts: function ImgItem_updateDigitizationCosts() {
    var percentOpaque = this.calculatePercentOpaque();
    var area = this.calculateArea();
    this.digitizationArea = area * percentOpaque;
    var process = this.cViewProcess.productProcess.process;
    
    this.digitizationFeeRRP = process.digitizationFeePerInch ? process.digitizationFee * this.digitizationArea : process.digitizationFee;
    
    log("process.digitizationFeePerInch=" + process.digitizationFeePerInch + ", process.digitizationFee=" + process.digitizationFee + ", this.cView.configuredProduct.getPercentMarkup=" + this.cView.configuredProduct.getPercentMarkup(true));
    
    var digFee = priceFromCost(this.digitizationFeeRRP, d.commissionRate, this.cView.configuredProduct.getPercentMarkup(true), 0.0); 
    this.digitizationFee = digFee.rrp;
    
    
    if(this.digitizedAsset.primaryItem() == this) {
      log("Primary Digitization Found");
    } else {
      log("Secondary Digitization Found");
    }
    this.stitchCount = parseInt(this.digitizationArea * process.stitchesPerInch);
    log("updateDigitizationCosts: digitizationArea=" + this.digitizationArea +",  digitizationFee=" + this.digitizationFee + ", stitchCount=" + this.stitchCount, true);
  },
  
  updateDigitizationNotice: function ImgItem_updateDigitizationNotice(updateCosts, updatePrice) {
    if(this.digitize) {
      if(updateCosts)  this.updateDigitizationCosts();
      var notice = null;
      
      var crp = d.round(this.digitizationFee * (100.0 - cDisc) / 100.0);
      
      if(this.digitizedAsset.primaryItem() == this) {
        if(!this.splitColors) {
          notice = ml('Digitizing Fee = %1s<br />Size =%2ssq inches<br/>You are looking at the original version of the image you have uploaded. The image will be professionally digitized to at most %3s colors.',
          [d.formatPrice(d.roundPrice(crp), "fee_currency_code", true, d.roundPrice(this.digitizationFee)), this.digitizationArea.toFixed(2), this.cViewProcess.productProcess.process.maxColors]);
        } else {
          notice = ml('Digitizing Fee = %1s<br />Size =%2ssq inches<br/>Colors = Approximated. Final thread colors may vary.',
          [d.formatPrice(d.roundPrice(crp), "fee_currency_code", true, d.roundPrice(this.digitizationFee)), this.digitizationArea.toFixed(2)]);
        }
      } else {
        if(!this.splitColors) {
          notice = ml('The price to digitize this image is included against another item in your cart.<br />Size =%1ssq inches<br/>You are looking at the original version of the image you have uploaded. The image will be professionally digitized to at most %3s colors.',
          [this.digitizationArea.toFixed(2), this.cViewProcess.productProcess.process.maxColors]);
        } else {
          notice = ml('The price to digitize this image is included against another item in your cart.<br />Size =%1ssq inches<br/>Colors = Approximated. Final thread colors may vary.',
          [this.digitizationArea.toFixed(2)]);
        }
      }
      if(this.digNotice != true) {
        this.addAlert(2,"digitization_notice", "alert_digitization_notice", ml("Digitization Required"), notice, false);
        this.digNotice = true;
      } else {
        this.updateAlertMessage(2, "digitization_notice", ml("Digitization Required"), notice) ;
      }
      
      if(updatePrice) {
        d.currentProductType.updatePrice();
      }
    }
  },
  
  //check if this is the primary/secondary asset... restrict accordingly
  refreshDigitizedAsset: function ImgItem_refreshDigitizedAsset(event) {
    log("refreshDigitizedAsset: this.digitizedAsset.items.list.length=" + this.digitizedAsset.items.list.length);
    if(this.digitizedAsset.items.list.length > 1) { //start locking down...
      this.allowResize = false;
      var primaryItem = this.digitizedAsset.primaryItem();
      var isPrimary = (this == primaryItem); 
      if(this.hasDigLockNotice) {
        if((event & DIG_EVENT_PRIMARY) == DIG_EVENT_PRIMARY) { //the warning message may have changed...remove it so it can be redone....
          this.removeAlert(1,"dig_lock");
          this.hasDigLockNotice = false;
        }
      }
      if((event & DIG_EVENT_PRIMARY) == DIG_EVENT_PRIMARY) { 
        this.initColors();
      } else if((event & DIG_EVENT_COLOR) == DIG_EVENT_COLOR) {
        if(!isPrimary) { //the colro changed on the primary.. reflect here...
          this.colors = primaryItem.colors;
          this.colorCount = primaryItem.colorCount
          this.transparentColor = primaryItem.transparentColor;
          this.updateDigitizationNotice(true, true);
        }
        this.initColors();
        this.updateImageSrc();
      }
      
      if(!this.hasDigLockNotice) {
        if(isPrimary) {
          this.addAlert(1, "dig_lock", "alert_digitization_lock", ml("Digitization Lock"), ml("This image is now locked because it has been added multiple times for digitization."), false);
        } else {
          this.addAlert(1, "dig_lock", "alert_digitization_lock", ml("Digitization Lock"), ml("This image is now locked because it has been added multiple times for digitization. To change colors go to the first instance of this image."), false);
        }
        this.hasDigLockNotice = true;
      }
      if(this.aspectEl != null) this.aspectEl.disabled = true;
      this.setFeature("size_arrows", false);
    } else {
      this.allowResize = true;
      if(this.hasDigLockNotice) { //we are moving from lock to unlocked....
        this.removeAlert(1,"dig_lock");
        this.hasDigLockNotice = false;
        this.initColors();
      } else {
        if((event & DIG_EVENT_COLOR) == DIG_EVENT_COLOR) {
          this.initColors();
        }
      }
      if(this.aspectEl != null) this.aspectEl.disabled = this.rotated;
      this.setFeature("size_arrows", true);
    }
  },
  
  isSecondaryDigitization: function() {
    if(this.digitize) {
      //its a digitized asset....
      var primary = this.digitizedAsset.primaryItem();
      if(primary != this) {
        return true;
      }
    }
    return false;
  },
  
  lockedDigitization: function() {
    if(this.digitize) {
      if(this.digitizedAsset.items.list.length > 1) {
        return true;
      }
    }
    return false;
  }
  
});

var TextItem = Class.create({
    CLASSDEF: {
        name:  'TextItem',
        parent: ImageItem
    },
  
  initialize: function TxtItem_initialize(id, cViewProcess, asset, options) {
    TextItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    this.allowHandleResizing = false;
    this.itemType = 1;
    this.itemTypeName = "Text";
    this.textIsUtf8 = false;
    this.editClickEvent = this.editClick.bindAsEventListener(this);
    this.editSaveClickEvent = this.editSaveClick.bindAsEventListener(this);
    this.editCancelClickEvent = this.editCancelClick.bindAsEventListener(this);
    
    this.sizeFieldChangedEvent = this.sizeFieldChanged.bindAsEventListener(this);

    this.fs = dopt(options, "fs", (10).mm_to_pts());  // fs = Font Size
    
    this.text = dopt(options, "text", "");
    this.fontFace = dopt(this.options, "font", null);
    if(this.fontFace==null) {
      this.fontFace = dopt(this.options, "font", this.isWilcomEMB ? "Arial Rounded" : "Arial");
      this.checkUtf8();
    }
    this.fontSpec = pwFontManager.allFonts[this.fontFace];
    if(this.fontSpec != null) {
      this.minFontSize = this.fontSpec.minSize;
    } else {
      this.minFontSize = null;
    }
    this.setName(this.text);
    this.isBold = dopt(options, "b", "1");
    this.isItalics = dopt(options, "i", "0");
    
    this.textColorVal = dopt(options, "tc", "#FF3333");
    this.gradientColorVal = dopt(options, "gc", "#FFFFFF");
    this.strokeColorVal = dopt(options, "sc", "#000000");
    this.effectColorVal = dopt(options, "ec", "#000000");
    this.blurVal = dopt(options, "bl", "2");
    this.glowStrength = dopt(options, "gs", "2");
    this.shadow_x = dopti(options, "sh_x", "2");
    this.shadow_y = dopti(options, "sh_y", "2");
    this.shape = dopt(options, "s", "0");
    this.xAmp = dopt(options, "s_x", "0");  
    this.yAmp = dopt(options, "s_y", "35");
    
    this.gradientTypeVal = dopt(this.options, "g", "0");
    this.effectTypeVal = dopt(this.options, "e", "0");

    this.strokeWidth = dopti(this.options, "sw", "0");
    
    this.align = dopti(options, "al", "0");
    
    this.oldTabName = "effects";
    if(d.designerOptions.showTextDims()) {
      this.setFeature("dim", true);
    } else {
      this.setFeature("dim", false);
    }
    if(d.designerOptions.showTextAdvanced()) {
      this.setFeature("adv", true);
    } else {
      this.setFeature("adv", false);
    }
    
  },
  
  initialiseManagePane: function TxtItem_initialiseManagePane() {
    log("TextItem.initialiseManagePane()");
    TextItem.parentClass.method("initialiseManagePane").call(this);
    
    this.editButton = $("mp1_" + this.elId + "_edit");
    this.editButton.style.cursor = "pointer";
    Event.observe(this.editButton, "click", this.editClickEvent);
    
    if(!this.canModifyDesign) {
      this.editButton.hide();
    }
    
    this.editSaveButton = $("mp1_" + this.elId + "_save_edit");
    this.editSaveButton.style.cursor = "pointer";
    Event.observe(this.editSaveButton, "click", this.editSaveClickEvent);
    
    this.editCancelButton = $("mp1_" + this.elId + "_cancel_edit");
    this.editCancelButton.style.cursor = "pointer";
    Event.observe(this.editCancelButton, "click", this.editCancelClickEvent);
    
    this.editContainer = $("mp1_" + this.elId + "_edit_text_container");
    this.editTextArea = $("mp1_" + this.elId + "_edit_text");
    
    var self = this;
    
    if((self.isWilcomEMB)&&(this.canModifyDesign)) {
      this.sizeField = $("mp1_" + this.elId + "_sizefield");
      $("mp1_"+this.elId+"_sizefield_container").show();
      this.setSizeText();
      Event.observe(this.sizeField, "keyup", this.sizeFieldChangedEvent);
    }
    
    if(!this.canModifyDesign) {
      $("mp1_"+this.elId+"_li_arrows").hide();
    }
    
    this.fontButtonDiv = $("mp1_" + this.elId + "_font_button");
    this.fontTextImg = $("mp1_" + this.elId + "_font_text");
    
    this.setFontImage();
    this.fontButtonDiv.onclick= function() {
      if(!self.canModifyDesign) {
        alert(ml("You cannot change the font as it may change the price of your order"));
        return;
      }
			self.checkUtf8();
      pwFontManager.selectFont(self.fontFace,  self.isWilcomEMB ? "Arial Rounded" : "Arial", self.isWilcomEMB, function(face, fontData) {
          self.fontFace = face;
          self.fontSpec = fontData;
          self.minFontSize = fontData.minSize;
          if(self.isWilcomEMB) {
            self.checkFontSize();
          }
          self.fontStyleChanged("face");
          self.setFontImage();
          self.setDirty();
          self.setText();
      });
    };
    /*
    this.fontInput = $("mp1_" + this.id + "_fonts");
    this.fontInput.onchange = function(el) { self.setText();};

    this.buildFontDropdown();
    //this.fontInput.value = 
    */
    
    this.aligns = [$("mp1_" + this.elId + "_al"), $("mp1_" + this.elId + "_ac"), $("mp1_" + this.elId + "_ar")];
    this.aligns[0].onclick = function() { self.setAlign(0,true);};
    this.aligns[1].onclick = function() { self.setAlign(1,true);};
    this.aligns[2].onclick = function() { self.setAlign(2,true);};
    
    this.setAlign(this.align, false);
    
    
    this.bButton = $("mp1_" + this.elId + "_b");
    this.iButton = $("mp1_" + this.elId + "_i");
    
    if(this.isBold=="0") {
      this.bButton.className = "button_up";
    } else {
      this.bButton.className = "button_down";
    }
    this.bButton.onclick = function() {
      self.fontStyleChanged("bold");
      if(self.isBold=="1") {
        self.isBold = "0";
        self.bButton.className = "button_up";
      } else {
        self.isBold = "1";
        self.bButton.className = "button_down";
      }
      self.setDirty();
      self.setText();
      //d.itemChanged(true);
    };
    
    if(this.isItalics=="0") {
      this.iButton.className = "button_up";
    } else {
      this.iButton.className = "button_down";
    }
    this.iButton.onclick = function() {
      self.fontStyleChanged("italics");
      if(self.isItalics=="1") {
        self.isItalics = "0";
        self.iButton.className = "button_up";
      } else {
        self.isItalics = "1";
        self.iButton.className = "button_down";
      }
      self.setDirty();
      self.setText();
      //d.itemChanged(true);
    };
 
    
    this.strokePanel = $("ep_stroke_" + this.elId);
    this.gradientPanel = $("ep_gradient_" + this.elId);
    this.effectPanel = $("ep_effect_" + this.elId);
    this.shape_panel = $("ep_shape_" + this.elId);
    
    var tc = $("etc_stroke_" + this.elId);
    tc.onclick = function() { self.selectTab("stroke", self.elId); return false; };
    tc = $("etc_gradient_" + this.elId);
    tc.onclick = function() { self.selectTab("gradient", self.elId); return false; };
    tc = $("etc_effects_" + this.elId);
    tc.onclick = function() { self.selectTab("effects", self.elId); return false; };
    tc = $("etc_shape_" + this.elId);
    tc.onclick = function() { self.selectTab("shape", self.elId); return false; };
    
    
    
    this.gradientType = $("ep_gradienttype_" + this.elId);
    this.gradientType.onchange = function() { self.gradientTypeVal = this.value; self.fontStyleChanged("gradient"); self.setDirty(); self.setText(); };
    
    this.textColor = $("mp1_" + this.elId + "_cb");
    this.gradientColor = $("ep_gradient_color_" + this.elId);
    this.effectColor = $("ep_effectcolor_" + this.elId);
    this.strokeColor = $("ep_stroke_color_" + this.elId);
    
    this.textColor.style.backgroundColor = this.textColorVal;
    this.textColor.onclick = function() { self.fontStyleChanged("text-color"); self.setColorClick(self.textColor, 1); };
    this.gradientColor.style.backgroundColor = this.gradientColorVal;
    this.gradientColor.onclick = function() { self.fontStyleChanged("gradient-color"); self.setColorClick(self.gradientColor, 2); };
    this.effectColor.style.backgroundColor = this.effectColorVal;
    this.effectColor.onclick = function() { self.fontStyleChanged("effect-color"); self.setColorClick(self.effectColor, 3); };
    this.strokeColor.style.backgroundColor = this.strokeColorVal;
    this.strokeColor.onclick = function() { self.fontStyleChanged("stroke-color"); self.setColorClick(self.strokeColor, 4); };
    
    
    this.sliderStroke = new Control.Slider('ep_stroke_width_' + this.elId + '_s','ep_stroke_width_' + this.elId + '_t', {
      range:$R(0,20),
      values:$R(0,20),
      sliderValue:this.strokeWidth,
      onChange:function(v){ self.strokeWidth=v; self.fontStyleChanged("stroke-width"); self.setDirty(); self.setText(); }
    });
    
    this.effectType = $("ep_effecttype_" + this.elId);
    this.effectType.onchange = function() { self.effectTypeVal = this.value; self.fontStyleChanged("effect"); self.setEffectType(true);};
    
    //this.glowStrength = $("ep_glow_strength_" + id);
    //this.glowStrength.onclick = function(el) { _this.setDirty(); _this.setText();};
    
    this.sliderGlow = new Control.Slider('ep_g_str_' + this.elId + '_s','ep_g_str_' + this.elId + '_t', {
      range:$R(0,10),
      sliderValue:this.glowStrength,
      onChange:function(v){ self.glowStrength=v; self.fontStyleChanged("glow"); self.setDirty(); self.setText(); }
    });
    
    this.sliderBlur = new Control.Slider('ep_b_str_' + this.elId + '_s','ep_b_str_' + this.elId + '_t', {
      range:$R(0,10),
      sliderValue:this.blurVal,
      onChange:function(v){ self.blurVal=v;  self.fontStyleChanged("blur"); self.setDirty(); self.setText(); }
    });
    
    this.shadowEffects = $("shadow_effects_" + this.elId); 
    this.glowEffects = $("glow_effects_" + this.elId); 
    
    this.shape_select = $("warp_shape_select_"+this.elId);
//    this.shape

    if(!this.allowFeature("text-warp")) {
      $("et_shape_" + this.elId).hide();
    } else {

      if (this.isWilcomEMB) {
        
        var shapes_li = "<ol class='emb_shapes'>" ;
        for(var k in embShapes) {
          var shape = embShapes[k] ;
          shapes_li += "<li id='emb_shape_"+this.elId+"_"+k+"' value='"+k+"' class='"+shape.code+"'><span>"+shape.c+"</span></li>" ;
        }
        shapes_li += "</ol>" ;
        
        this.shape_select.innerHTML = shapes_li ;
        
        for (var k in embShapes) {
          var emb_ele = $('emb_shape_'+this.elId+'_'+k) ;
          var that = this ;
          if (this.shape == k) emb_ele.addClassName("alt") ;
          emb_ele.observe("click", function(e) {
            var ele = Event.element(e);
            if (ele.tagName.toUpperCase() != "LI") ele = ele.parentNode ;
  //          log("** changing element "+that.elId+", from shape "+that.shape+" to "+ele.value) ;
            $("emb_shape_"+that.elId+"_"+that.shape).removeClassName("alt") ;
            ele.addClassName("alt") ;
            self.shape = ele.value ;
            self.setShapeType(true, true);
          }.bind(that));
        }
        
      } else {
        this.shapes = $("ep_shape_sel_down_" + this.elId);
  
        addSelectOption(this.shapes, "None", "0"); 
        addSelectOption(this.shapes, "Arched", "5"); 
        for(var k in availableShapes) {
          var shape = availableShapes[k];
          addSelectOption(this.shapes, shape["c"], k); 
        }
        this.shapes.value = self.shape; // TODO: test this works in IE.
        this.shapes.onchange = function() {
          self.shape = self.shapes.value;
          self.setShapeType(true);
        };
      }
    
      this.xAmpC = $("ep_s_x_" + this.elId);
      this.yAmpC = $("ep_s_y_" + this.elId);
      
      this.sliderX = new Control.Slider('ep_x_str_' + this.elId + '_s','ep_x_str_' + this.elId + '_t', {
        range:$R(0,100),
        sliderValue:this.xAmp,
        onChange:function(v){self.xAmp=v;   self.setDirty(); self.setText();}
      });
      this.sliderY = new Control.Slider('ep_y_str_' + this.elId + '_s','ep_y_str_' + this.elId + '_t', {
        range:$R(0,100),
        sliderValue:this.yAmp,
        onChange:function(v){self.yAmp=v; self.setDirty(); self.setText(); }
      });
    }
    
    this.offsetWidget = $("mp_" + this.elId + "_offset");
    this.offsetHandleWidget = $("mp_" + this.elId + "_offset_handle");
    
    this.offsetHandleWidget.style.top = (this.shadow_y + 18 - offsetWidgetSize) + "px";
    this.offsetHandleWidget.style.left = (this.shadow_x + 18 - offsetWidgetSize) + "px";
    
    //log("this.offsetHandleWidget.style.top=" + this.offsetHandleWidget.style.top);
    //log(this.shadow_x);
    //log(offsetWidgetSize);
    
    this.eventStartOffset =  this.startOffset.bindAsEventListener(this);
    this.eventOffsetMove =  this.offsetMove.bindAsEventListener(this);
    this.eventOffsetFinish =  this.offsetFinish.bindAsEventListener(this);
    Event.observe(this.offsetHandleWidget, "mousedown", this.eventStartOffset);
    
    //this.fsInput.value = dopt(this.options, "fs", "23");
    //this.b_input.checked = (dopt(this.options, "b", "true")=="true")? true:false;
    //this.i_input.checked = (dopt(this.options, "i", "false")=="true")? true:false;
    
    this.gradientType.value = this.gradientTypeVal;//dopt(this.options, "g", "0");
    this.effectType.value = this.effectTypeVal;//dopt(this.options, "e", "0");
    this.setEffectType(false);
    if(this.allowFeature("text-warp")) {
      this.setShapeType(false);
    }
    this.setText();

    if(this.isWilcomEMB) {
      $("advanced_pane_tabs_" + this.elId).hide();
      this.selectTab("shape", this.elId);
      
      $("mp1_" + this.elId + "_alc").hide();
    }
    if(!this.allowFeature("text-align")) {
      $("mp1_" + this.elId + "_alc").hide();
    }
    if((d.designerOptions.showTextAdvanced())&&(d.designerOptions.showTextAdvancedOpen())) {
      this.advClick();
    }
  },
  
  fontStyleChanged: function TxtItem_fontStyleChanged(attribute) {
    
  },
  
  setFontImage: function TxtItem_setFontImage() {
    if(this.fontTextImg!=null) {
      var font = pwFontManager.allFonts[this.fontFace];
      if(font == null) {
        alert("Cannot find font definition for '" + this.fontFace + "'");
      }
      setTransPng(this.fontTextImg, font.getSampleURL(this.isWilcomEMB), 70, 20);
    }
  },
  
  buildFontDropdown: function TxtItem_buildFontDropdown() {
    if(this.fontInput==null) {
      return;
    }
    while(this.fontInput.firstChild) {
      this.fontInput.removeChild(this.fontInput.firstChild);
    }
    for(var k in availableFonts) {
      if((!this.textIsUtf8)||(availableFonts[k].u)) {
        var opt = document.createElement("OPTION");
        opt.text = k;
        opt.value = k;
        if(BrowserDetect.browser == "Explorer") {
          this.fontInput.options.add(opt);
        } else {
          this.fontInput.appendChild(opt);
        }
      }
    }
  },
  
  moveToDesigner: function TxtItem_moveToDesigner(cViewProcess, resizeScale, oldLeft, oldTop, offsetLeft, offsetTop) {
    /*if(this.el!=null) {
      this.cViewArea.canvas.removeChild(this.el);
      cViewProcess.configuredViewArea.canvas.appendChild(this.el);
    }
    
    if(this.managePane!=null) {
      this.managePane.parentNode.removeChild(this.managePane);
      
      cViewProcess.managePane.appendChild(this.managePane);
    }
    this.cViewProcess = cViewProcess;
    this.cViewArea = cViewProcess.configuredViewArea;
    this.cView = this.cViewArea.configuredView;
    */
    var scale = this.cViewProcess.productProcess.designScale / resizeScale / cViewProcess.productProcess.designScale;
    log("Text Rescale: " + scale + " will change fs from " + this.fs + " to " + (this.fs * scale));
    this.fs = this.fs * scale;
    TextItem.parentClass.method("moveToDesigner").call(this, cViewProcess, resizeScale, oldLeft, oldTop, offsetLeft, offsetTop);
    
    this.setText();
  },
  //do nothing: text can rescale to desired scale....
  checkScale: function TxtItem_checkScale(desiredScale, process) {
    return desiredScale;
  },
  
  del: function TxtItem_del() {
    if(this.editButton!=null) {
      Event.stopObserving(this.editButton, "click", this.editClickEvent);
      Event.stopObserving(this.editSaveButton, "click", this.editSaveClickEvent);
      Event.stopObserving(this.editCancelButton, "click", this.editCancelClickEvent);
      //Event.stopObserving(this.advButton, "click", this.advClickEvent);
    }
    TextItem.parentClass.method("del").call(this);
  },
  
  //does this item handle its own start positions?
  startPosition: function TxtItem_startPosition(options) {
    if(options != null && options.t != null) {
      this.top = options.t;
      this.left = options.l;
    } else {
      this.doCenter = true;
    }
    return true;
  },
  
  setRolloverStyle: function TxtItem_setRolloverStyle() {
    
  },
  
  setLeaveStyle: function TxtItem_setLeaveStyle() {
    this.el.style.zIndex = this.getElementZIndex();
  },

  setSelectStyle: function TxtItem_setSelectStyle() {
    this.el.style.zIndex = 1000;
  },
  
  setAlign: function TxtItem_setAlign(align, fromGUI) {
    this.aligns[this.align].className = "button_up";
    this.aligns[align].className = "button_down";
    this.align = align;
    if(fromGUI) {
      this.fontStyleChanged("align");
      this.setDirty();
      this.setText();
      //d.itemChanged(true);
    }
  },
  
  setColorClick: function TxtItem_setColorClick(el, type) {
    var self = this;
    var color = null;
    if(type==1) {
      color = this.textColorVal;
    } else if(type==2) {
      color = this.gradientColorVal;
    } else if(type==3) {
      color = this.effectColorVal;
    } else if(type==4) {
      color = this.strokeColorVal;
    }
    
    pwColorPicker.selectColor(this.cViewProcess.productProcess.id, el, color,  function(c) { self.colorCallback(c, el, type); }, {});
    //showColorPicker(el, function(c) { self.colorCallback(c, el, type); });
  },
  
  colorCallback: function TxtItem_colorCallback(color, el, type) {
    el.style.backgroundColor = color;
    if(type==1) {
      this.textColorVal = color;
    } else if(type==2) {
      this.gradientColorVal = color;
    } else if(type==3) {
      this.effectColorVal = color;
    } else if(type==4) {
      this.strokeColorVal = color;
    }
    this.setDirty();
    this.setText();
    
    //d.itemChanged(true);
  },
  
  setSizeText: function TxtItem_setSizeText() {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.fontStyleChanged("size");
    var num = parseFloat(this.fs).pts_to_mm() ;
    num = Math.round(num*Math.pow(10,2))/Math.pow(10,2);
    if (this.sizeField) { this.sizeField.value = num; };
  },
  
  quickResize: function TxtItem_quickResize(originalSize) {
    var reScale = parseFloat(originalSize) / parseFloat(this.fs);
    this.width /= reScale;
    this.height /= reScale;
    this.setPosition();
    if(this.selected && !this.isMoving) {
      this.autoRepositionHandles();
    }
  },
  
  checkFontSize: function() {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var num = parseFloat(this.fs).pts_to_mm() ;
    num = Math.round(num*Math.pow(10,2))/Math.pow(10,2);
    var minSize = (this.minFontSize == null) ? 5 : this.minFontSize;
    if(num < minSize) {
      var originalSize = this.fs;
      this.fs = (minSize).mm_to_pts();
      this.setDirty();
      this.setText(false, 3);
      this.setSizeText() ;
      this.quickResize(originalSize) ;
      this.setSizeText();
    }
  },
  
  sizeUpClick: function TxtItem_sizeUpClick(event) {
    Event.stop(event);
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var originalSize = this.fs;
    var newSize = parseFloat(this.fs).pts_to_mm() + 1 ;
    if(newSize <= 50 || !this.isWilcomEMB) {
      this.fontStyleChanged("size");
      this.fs = (newSize).mm_to_pts() ;
      this.setDirty();
      this.setText(false, 3);
      //d.itemChanged(true);
      this.setSizeText() ;
      this.quickResize(originalSize) ;
    }
  },
  
  sizeDownClick: function TxtItem_sizeDownClick(event) {
    Event.stop(event);
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var originalSize = this.fs;
    var newSize = parseFloat(this.fs).pts_to_mm() - 1 ;
    var minSize = (this.minFontSize == null) ? 5 : this.minFontSize;
    if(newSize >= minSize || !this.isWilcomEMB) {
      this.fontStyleChanged("size");
      this.fs = (newSize).mm_to_pts() ;
      this.setDirty();
      this.setText(false, 3);
      //d.itemChanged(true);
      this.setSizeText() ;
      this.quickResize(originalSize) ;
    }
  },

  sizeFieldChanged:function(event) {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var val = parseFloat(this.sizeField.getValue()) ;
    log("size changed from "+parseFloat(this.fs).pts_to_mm().round()+" to "+val) ;
    if (val == parseFloat(this.fs).pts_to_mm().round()) return ;
    if (isNaN(val)) {
      if (this.sizeField.getValue() == "") {
        log("Caught empty text, don't do anything") ;
        return ;
      } else {
        this.sizeField.value = parseFloat(this.fs).pts_to_mm().round() ;
        log("Caught some dodgy text, resetting to last known number") ;
        return ;
      }
    }
    var originalSize = this.fs ;
    var minSize = (this.minFontSize == null) ? 5 : this.minFontSize;
    if (val < minSize) return ;
    if (val > 50) {
      this.sizeField.value = 50 ;
      if (this.fs == (50).mm_to_pts()) return ;
      this.fs = (50).mm_to_pts() ;
      log("Got a large number,"+val+" change the size to "+parseFloat(this.fs).pts_to_mm()) ;
    }else {
      this.fs = val.mm_to_pts() ;
      log("Got a new number,"+val+" change the size") ;
    }
    
    this.fontStyleChanged("size");
    this.quickResize(originalSize) ;
    this.setDirty();
    this.setText(false, 3);
    //d.itemChanged(true);
    //this.setSizeText() ;
  },
  
  startOffset: function TxtItem_startOffset(event) {
    log("starting offset");
    Event.stop(event);
    //start tracking mouse movement
    Event.observe(document, "mousemove", this.eventOffsetMove);
    Event.observe(document, "mouseup", this.eventOffsetFinish);
    
    //store the current location of the rotation widget center
    var pos = Position.cumulativeOffset(this.offsetWidget);
    var mxPos = Event.pointerX(event);
    var myPos = Event.pointerY(event);
    var pos2 = Position.cumulativeOffset(this.offsetHandleWidget);
    
    
    this.offset_x_pos = pos[0] + mxPos - pos2[0];
    this.offset_y_pos = pos[1] + myPos - pos2[1];
    return true;
  },
  
  offsetMove: function TxtItem_offsetMove(event) {
    //determine the angle from center of rotation widget to the mouse
    Event.stop(event);
    var mxPos = Event.pointerX(event);
    var myPos = Event.pointerY(event);
    
    var t = (myPos - this.offset_y_pos);
    var l = (mxPos - this.offset_x_pos);
    
    if(t + offsetWidgetSize >36) {
      t = 36 - offsetWidgetSize;
    }
    if(l + offsetWidgetSize >36) {
      l = 36 - offsetWidgetSize;
    }
    if(t < 0 - offsetWidgetSize ) {
      t = 0 - offsetWidgetSize;
    }
    if(l < 0 - offsetWidgetSize ) {
      l = 0 - offsetWidgetSize;
    }
    this.offsetHandleWidget.style.top = t + "px";
    this.offsetHandleWidget.style.left = l + "px";
  },
  
  offsetFinish: function TxtItem_offsetFinish(event) {
    Event.stopObserving(document, "mousemove", this.eventOffsetMove);
    Event.stopObserving(document, "mouseup", this.eventOffsetFinish);
    
    this.shadow_y = parseInt(this.offsetHandleWidget.style.top, 10) + offsetWidgetSize - 18;
    this.shadow_x = parseInt(this.offsetHandleWidget.style.left, 10) + offsetWidgetSize - 18;

    this.setDirty();
    this.setText();
  },
  
  setEffectType: function TxtItem_setEffectType(fromUI) {
    var val = this.effectType.options[this.effectType.selectedIndex].value;
    if(val==0) {
      this.shadowEffects.style.display='none';
      this.glowEffects.style.display='none';
    } else if(val==1) {
      this.shadowEffects.style.display='';
      this.glowEffects.style.display='none';
    } else {
      this.shadowEffects.style.display='none';
      this.glowEffects.style.display='';
    }
    if(fromUI) {
       this.setDirty();
       this.setText();
    }
  },
  
  setShapeType: function TxtItem_setShapeType(fromUI, emb) {
    if(!emb) emb = false ;
    
    if(this.shape == "5") {
      this.yAmpC.style.display='';
    } else {
      var shape = emb ? embShapes[this.shape] : availableShapes[this.shape];
      if(shape==null) {
        this.xAmpC.style.display='none';
        this.yAmpC.style.display='none';
      } else {
        if(shape["x"]) {
          this.xAmpC.style.display='';
        } else {
          this.xAmpC.style.display='none';
        }
        if(shape["y"]) {
          this.yAmpC.style.display='';
        } else {
          this.yAmpC.style.display='none';
        }
      }
    }
    if(fromUI) {
       this.setDirty();
       this.setText();
    }
  },
                
  selectTab: function TxtItem_selectTab(tab, id) {
    if(this.oldTabName != null) {
      if(this.oldTabName==tab) {
        return;
      }
      var oldTab = $('ep_' + this.oldTabName + "_" + id);
      oldTab.style.display='none';
      oldTab = $('et_' + this.oldTabName + "_" + id);
      oldTab.className = "et_unselected_tab";
    }
    var newTab = $('ep_' + tab + "_" + id);
    newTab.style.display='';
    newTab = $('et_' + tab + "_" + id);
    newTab.className = "et_selected_tab";
    this.oldTabName = tab;
  },
  
  beforeAddNew: function TxtItem_beforeAddNew() {
    popup('new_text_popup');
    $("new_text").focus();
    return true;
  },
  
  continueAdd: function TxtItem_continueAdd() {
    this.text = $("new_text").value;
    $("new_text").value = "";
    this.setName(this.text);
    this.checkUtf8();
  },
  
  buildElement: function TxtItem_buildElement() {
    if ($("d_" + this.elId)) $("d_" + this.elId).remove();
    if(this.useCanvas) {
      this.el = document.createElement("SPAN");
      this.canvas = Raphael(this.el, 10, 10);
      this.canvasImg = this.canvas.image("/images/trans.gif", 0, 0, 10, 10);
      this.canvasImgWidth = 10.0;
      this.canvasImgHeight = 10.0;
    } else {
      this.el = document.createElement("IMG");
    }
    this.el.id = "d_" + this.elId;
    this.el.style.display = "none";
    this.el.style.position = "absolute";
    this.el.style.cursor="pointer";
	
    if(!this.useCanvas) {
      if(useAlphaHack) {
        this.el.src = d.pathPrefix + "/images/trans.gif";
      }
    }
    this.cViewArea.canvas.appendChild(this.el);
    this.el.style.zIndex = this.getElementZIndex();
    
    return this.el;
  },
  
  sizeChanged: function TxtItem_sizeChanged() {
    this.updateTitleSize();
  },
  
  editClick: function TxtItem_editClick(event) {
    Event.stop(event);
    if(!this.canModifyDesign) {
      alert(ml("You cannot edit the text as it may change the price of your order"));
      return;
    }
    this.select();
    this.editTextArea.value = this.text;
    this.editButton.style.display = 'none';
    this.managePaneBody.style.display = 'none';
    this.editContainer.style.display = '';
    return false;
  },
  
  editSaveClick: function TxtItem_editSaveClick(event) {
    if(this.editTextArea.value=="") {
      alert(ml("NO_TEXT"));
      return;
    }
    this.text = this.editTextArea.value;
    this.checkUtf8();
    this.setName(this.text);
    this.editButton.style.display = '';
    this.managePaneBody.style.display = '';
    this.editContainer.style.display = 'none';
    this.setDirty();
    this.setText();
  },
  
  editCancelClick: function TxtItem_editCancelClick(event) {
    this.editButton.style.display = '';
    this.managePaneBody.style.display = '';
    this.editContainer.style.display = 'none';
  },
  
  
  rotationChanged: function TxtItem_rotationChanged(deg) {
    var self = this;
    if(this.rotDelay != null) {
      window.clearTimeout(this.rotDelay);
    }
    if(this.useCanvas) {
      this.setDirty();
      this.autoRepositionHandles();
    } else {
      this.rotDelay = window.setTimeout( function() { 
        self.setDirty();
        self.setText(false); 
        //d.itemChanged(true);
      }, 100);
    }
    return;
  },
  
  serializeToOptions: function Item_serializeToOptions(o, excludeRotation) {
    o = TextItem.parentClass.method("serializeToOptions").call(this,o, excludeRotation);
    
    o.font = this.fontFace;

    o.fs = this.fs;
    o.scale = this.cViewProcess.productProcess.designerScale;
                                                      
    o.al = this.align;
                                                                                              
    o.b = this.isBold;
    o.i = this.isItalics;
      
    o.text =  this.text;
                                                      
    o.tc = this.textColorVal;
    o.gc = this.gradientColorVal;
    o.sc = this.strokeColorVal;
    o.ec = this.effectColorVal;
                                                      
    o.sw = this.strokeWidth;
                                                      
    //o.g = this.gradientType.value;
    o.g = this.gradientTypeVal;
                                                      
    //o.e = this.effectType.value;
    o.e = this.effectTypeVal;
    o.gs = this.glowStrength;
    o.bl = this.blurVal;
                                                      
    o.sh_x = this.shadow_x;
    o.sh_y = this.shadow_y;
                                                      
    o.s = this.shape;
    o.s_x = this.xAmp;
    o.s_y = this.yAmp;
    
    return o;
  },
  
  
  serializeOLD: function TxtItem_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    TextItem.parentClass.method("serialize").call(this,queryComponents,prefix);
    
    //if(this.fontInput != null) {
      //queryComponents.push(encodeURIComponent(prefix + "[font]") + "=" + encodeURIComponent(this.fontInput.value));
      queryComponents.push(encodeURIComponent(prefix + "[font]") + "=" + encodeURIComponent(this.fontFace));
      
      
    //}
    queryComponents.push(encodeURIComponent(prefix + "[fs]") + "=" + encodeURIComponent(this.fs));
    queryComponents.push(encodeURIComponent(prefix + "[scale]") + "=" + encodeURIComponent(this.cViewProcess.productProcess.designerScale));
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[al]") + "=" + encodeURIComponent(this.align));
                                                      
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[b]") + "=" + encodeURIComponent(this.isBold));
    queryComponents.push(encodeURIComponent(prefix + "[i]") + "=" + encodeURIComponent(this.isItalics));
                                                      
    var txt = encodeURIComponent(this.text);          
    queryComponents.push(encodeURIComponent(prefix + "[text]") + "=" + txt);
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[tc]") + "=" + encodeURIComponent(this.textColorVal));
    queryComponents.push(encodeURIComponent(prefix + "[gc]") + "=" + encodeURIComponent(this.gradientColorVal));
    queryComponents.push(encodeURIComponent(prefix + "[sc]") + "=" + encodeURIComponent(this.strokeColorVal));
    queryComponents.push(encodeURIComponent(prefix + "[ec]") + "=" + encodeURIComponent(this.effectColorVal));
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[sw]") + "=" + encodeURIComponent(this.strokeWidth));
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[g]") + "=" + encodeURIComponent(this.gradientType.value));
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[e]") + "=" + encodeURIComponent(this.effectType.value));
    queryComponents.push(encodeURIComponent(prefix + "[gs]") + "=" + encodeURIComponent(this.glowStrength));
    queryComponents.push(encodeURIComponent(prefix + "[bl]") + "=" + encodeURIComponent(this.blurVal));
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[sh_x]") + "=" + encodeURIComponent(this.shadow_x));
    queryComponents.push(encodeURIComponent(prefix + "[sh_y]") + "=" + encodeURIComponent(this.shadow_y));
                                                      
    queryComponents.push(encodeURIComponent(prefix + "[s]") + "=" + encodeURIComponent(this.shape));
    queryComponents.push(encodeURIComponent(prefix + "[s_x]") + "=" + encodeURIComponent(this.xAmp));
    queryComponents.push(encodeURIComponent(prefix + "[s_y]") + "=" + encodeURIComponent(this.yAmp));
    return queryComponents.join('&');
  },
  
  setText: function TxtItem_setText(doDelay, pass) {
    //var updateInfo = this.startUpdating();
    if(doDelay==null) {
      doDelay = true;
    }
    if(pass == null) {
      pass = 1;
    }
    var self = this;
    if(this.textDelay != null) {
      window.clearTimeout(this.textDelay);
      this.textDelay = null;
    }
    if(doDelay) {
      this.textDelay = window.setTimeout( function() { self.setText(false); }, 100);
      return;
    }
    
    var url = "";
    if(pass != 3) {
    //startAsyncAction();
    //if(useAlphaHack) { //we are now always using 2 stage method...
      url =  d.pathPrefix + "/designer/prepare_text?id=" + this.id + "&cp=" + this.cView.configuredProduct.id + "&cpv=" + this.cView.id + "&cpva=" + this.cViewArea.id + "&cpvap=" + this.cViewProcess.id + "&pass=" + pass;
      var excludeRotation=this.useCanvas;
      
      ajaxQueueManager.queueRequest("save_product/" + this.cView.configuredProduct.id, 3, {
          mode: 0,
          subKey: "text_" + this.id,
          url: url,
          options: {asynchronous:true, evalScripts:true, parameters:this.serialize([],"a", excludeRotation)}
      });
    }
    if(pass == 1 || pass == 3) { //we gen the preview on first pass, high quality on second...
      this.textDelay = window.setTimeout( function() { self.setText(false, 2); }, 1000);
    }
    
  },
  
  tbTransformed: function TxtItem_tbTransformed(data) {
    log("tbTransformed" );
    log(data);
    var checkCropIssue = true;
    //finishAsyncAction();
    if(parseInt(data.version, 10) == this.renderVersion) { //we have made more changes just update the image....
      this.width = parseInt(data.width, 10); // / this.layoutManager.layoutData.dScale;
      this.height = parseInt(data.height, 10);  // / this.layoutManager.layoutData.dScale;
      if(this.useCanvas) {
        this.setReferenceRotation(0, true, {w:this.width, h:this.height});  //store the rotated ref from the servers size dims.
        if(this.rotated) {
          this.setReferenceRotation(this.getRotation());
          this.width = this.rotatedRef.w;
          this.height = this.rotatedRef.h;
        }
      }
    }
    if(this.doCenter) {
      this.top = (this.cViewProcess.productProcess.fullHeight - this.height) / 2;
      this.left = (this.cViewProcess.productProcess.fullWidth - this.width) / 2;
      this.doCenter = false;
    }
    if(this.isWilcomEMB) {
      if(this.stitchCount != parseInt(data.thread_count, 10)) {
        this.stitchCount = parseInt(data.thread_count, 10);
        d.currentProductType.updatePrice();
      }
      if(data.text_quality == "preview") {
        checkCropIssue=false;
      }
    }
    
    this.setPosition();
    //var url = d.pathPrefix + "/text/get_prepared_text_bling?f=" + encodeURIComponent(fname) + "&ts=" + new Date().getTime();
    this.setSrc(data.url, {v: data.version, checkCropIssue:checkCropIssue});
    if(this.selected && !this.isMoving) {
      this.autoRepositionHandles();
    }
    
    this.enablePane();
  },
  
  
  //check if uses utf-8 characters and limit fonts available...
  checkUtf8: function() {
    var usedUtfPages = this.usedUtf8();
		if(usedUtfPages != null) {
      pwFontManager.utf8Pages = usedUtfPages;
			//check the current font supports utf8...
			var f = pwFontManager.allFonts[this.fontFace];
			if(f == null) {
        log("checkUtf8: unable to find font");
        //return;
      } else if(f.supportsPages(usedUtfPages)) {
        log("checkUtf8: font supports utf8");
				return;
			}
      log("checkUtf8: font does not support utf8, this.isWilcomEMB=" + this.isWilcomEMB);
			//current font doesnt support it... go set font to first utf8 font...
			for(var k in pwFontManager.allFonts) {
        var f = pwFontManager.allFonts[k];
				if((f.embFont == this.isWilcomEMB)&&(f.supportsPages(usedUtfPages))) {
					this.fontFace = f.name;
          log("Changed font to " + f.name);
					this.setFontImage();
					return;
				}
			}
      log("No Fonts Support UTF8");
		} else {
      pwFontManager.utf8Pages = null;
		}
	},
  
  usedUtf8: function TxtItem_usesUtf8() {
    var usedPages = {};
    var foundLanguage = false;
    for(var i=0;i < this.text.length; i++) {
      var theChar = this.text.charCodeAt(i);
      if(theChar != 32) { //ignore spaces....
        var pages = this.getUtfCodePage(theChar);
        if(pages == null) {
          alert("Unable to get code page for " + theChar);
        } else {
          var codes = pages[3];
          if(codes == null) codes = pages[2];
          usedPages[codes] = true;
          foundLanguage = true;
          //log(theChar + "=" + codes);
        }
      }
    }
    return foundLanguage ? usedPages : null;
  },
  
  getUtfCodePage: function TxtItem_getUtfCodePage(i) {
    var left = 0;
    var right = utf8CodePages.length - 1;
    while (left <= right) {
      var mid = parseInt((left + right)/2);
      var page = utf8CodePages[mid];
      if(page[0] <= i && page[1] >= i)
        return page;
      else if (page[0] < i)
        left = mid + 1;
      else
        right = mid - 1;
    }
    return null;
  },
  
  setHeight: function TxtItem_setHeight(height) {
    log("setHeight: " + height);
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.el.style.height = height + "px";
    if(this.el.style.width != "" && this.el.style.width != null) {
      this.setCanvasSize(parseFloat(this.el.style.width), parseFloat(this.el.style.height));
    }
    return true;
  },
  
  setWidth: function TxtItem_setWidth(width) {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.el.style.width = width + "px";
    if(this.el.style.height != "" && this.el.style.height != null) {
      this.setCanvasSize(parseFloat(this.el.style.width), parseFloat(this.el.style.height));
    }
    return true;
  }
  
});

var TeamItem = Class.create({
  CLASSDEF: {
      name:  'TeamItem',
      parent: TextItem
  },
  
  initialize: function TeamItem_initialize(id, cViewProcess, asset, options) {
    this.teamNameTemplate = d.teamNameTemplates.getAllTemplates()[options.team_t];
    TeamItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    this.cView.configuredProduct.getTeamNames().registerTeamNameItem(this);
    
    if(options.team_w == null) {
      this.teamWidth = 95;
    } else {
      this.teamWidth = options.team_w;
    }
    
    this.needFit = false;
    if(options.team_s == null) {
      this.needSizeData = true;
    } else {
      this.needSizeData = false;
      this.teamSizing = new TeamItemSizing(options.team_s);
    }
    
    this.setName("Team Name");
  },
  
  del: function TeamItem_del() {
    this.cView.configuredProduct.getTeamNames().unregisterTeamNameItem(this);
    TextItem.parentClass.method("del").call(this);
  },
  
  initialiseManagePane: function TeamItem_initialiseManagePane() {
    log("TeamItem.initialiseManagePane()");
    TeamItem.parentClass.method("initialiseManagePane").call(this);
    $("mp1_" + this.elId + "_tn_width_c").show();
    var self = this;
    this.sliderTeamWidth = new Control.Slider('tn_width_' + this.elId + '_s','tn_width_' + this.elId + '_t', {
      range:$R(0,100),
      sliderValue:this.teamWidth,
      onSlide:function(v){ self.setTeamWidth(v);}
    });
  },
  
  setTeamWidth: function(val) {
    this.teamWidth = val;
    
    var oldWidth = this.width;
    
    this.width = this.cViewProcess.productProcess.fullWidth * this.teamWidth / 100;
    
    var diff = this.width - oldWidth;
    
    this.left -= (diff/2); //keep centered...
    this.setPosition();
    if(this.selected && !this.isMoving) {
      this.autoRepositionHandles();
    }
    this.setText(); 
    this.setDirty();
  },
  
  //does this item handle its own start positions?
  startPosition: function TeamItem_startPosition() {
    //if(this.teamNameTemplate.config.fit != null) {
      this.needFit = true;
    //  log("Setting needFit = true");
   // } else {
     // this.doCenter = true;
    //  log("NOT Setting needFit = true");
    //}
    this.needSizeData = true;
    return true;
  },
  
  serializeToOptions: function Item_serializeToOptions(o) {
    o = TeamItem.parentClass.method("serializeToOptions").call(this,o);
    o.team_t = this.teamNameTemplate.id;
    o.team_w = this.teamWidth;
    
    if((this.cView.configuredProduct.serializingForSave != true)&&(this.cView.configuredProduct.getTeamNames().selectedTeamName!=null)) {
      i.team_n = this.cView.configuredProduct.getTeamNames().selectedTeamName.serializeToOptions();
    }
    if(this.needSizeData) {
      o.team_size =1;
    } else {
      o.team_s = this.teamSizing.serializeToOptions();
    }
    if(this.needFit) {
      o.team_fit =1;
    }
    return o;
  },
  
  serializeOLD: function TeamItem_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    TeamItem.parentClass.method("serialize").call(this,queryComponents,prefix);
    queryComponents.push(encodeURIComponent(prefix + "[team_t]") + "=" + encodeURIComponent(this.teamNameTemplate.id));
    queryComponents.push(encodeURIComponent(prefix + "[team_w]") + "=" + encodeURIComponent(this.teamWidth));
    
    if((this.cView.configuredProduct.serializingForSave != true)&&(this.cView.configuredProduct.getTeamNames().selectedTeamName!=null)) {
      this.cView.configuredProduct.getTeamNames().selectedTeamName.serialize(queryComponents, prefix + "[team_n]");
    }
    if(this.needSizeData) {
      queryComponents.push(encodeURIComponent(prefix + "[team_size]") + "=1");
    } else {
      this.teamSizing.serialize(queryComponents, prefix + "[team_s]");
    }
    if(this.needFit) {
      queryComponents.push(encodeURIComponent(prefix + "[team_fit]") + "=1");
    }
    return queryComponents.join('&');
  },
  
  tbTransformed: function TeamItem_tbTransformed(data) {
    if(this.needSizeData) {
      if(data.sized) {
        this.teamSizing = new TeamItemSizing(data.size_data);
        this.needSizeData = false;
      }
    }
    if(this.needFit) {
      if(data.fitted) {
        if(data.fit_font_size != null) {
          this.fs = data.fit_font_size;
        }
        this.width = parseInt(data.width, 10);
        this.height = parseInt(data.height, 10);
        if(this.teamNameTemplate.config.fit != null) {
          var margin = 0;
          //vertical fit...
          if(this.teamNameTemplate.config.fit.margin != null) margin = ((this.cViewProcess.productProcess.fullHeight * this.teamNameTemplate.config.fit.margin) / 100);
          log("vertical margin: " + margin + " valign=" + this.teamNameTemplate.config.fit.vertical_align);
          
          if(this.teamNameTemplate.config.fit.vertical_align == "top") {
             this.top = margin;
          } else if(this.teamNameTemplate.config.fit.vertical_align == "bottom") {
            this.top = (this.cViewProcess.productProcess.fullHeight - this.height) - margin;
            if(this.top<0) this.top = 0;
          } else {
            this.top = (this.cViewProcess.productProcess.fullHeight - this.height) / 2;
          }
          //horizontal fit...
          margin = 0;
          if(this.teamNameTemplate.config.fit.margin != null) margin = ((this.cViewProcess.productProcess.fullWidth * this.teamNameTemplate.config.fit.margin) / 100);
          log("horizontal margin: " + margin + " halign=" + this.teamNameTemplate.config.fit.horizontal_align);
          if(this.teamNameTemplate.config.fit.horizontal_align == "left") {
             this.left = margin;
          } else if(this.teamNameTemplate.config.fit.horizontal_align == "right") {
            this.left = (this.cViewProcess.productProcess.fullWidth - this.width) - margin;
            if(this.left<0) this.left = 0;
          } else {
            this.left = (this.cViewProcess.productProcess.fullWidth - this.width) / 2;
          }
        } else{
          this.doCenter=true;
        }
        this.needFit = false;
        log("Setting needFit = false");
      }
    }
    TeamItem.parentClass.method("tbTransformed").call(this,data);
  },
  
  quickResize: function TeamItem_quickResize(originalSize) {
    var reScale = parseFloat(originalSize) / parseFloat(this.fs);
    //this.width /= reScale;
    this.height /= reScale;
    this.setPosition();
    if(this.selected && !this.isMoving) {
      this.autoRepositionHandles();
    }
  },
  
  editClick: function TeamItem_editClick(event) {
    this.select();
    this.cView.configuredProduct.getTeamNames().editTeamNames();
    return false;
  },
  
  //for now this will only be called on features team names do not support...
  allowFeature: function(feature) {
    return false;
  },
  
  fontStyleChanged: function TeamItem_fontStyleChanged(attribute) {
    switch(attribute) {
      case "face":
      case "bold":
      case "italics":
      case "stroke-width":
      case "effect":
      case "glow":
      case "blur":
        this.needSizeData = true;
        break;
    }
  }
});


var TeamItemSizing = Class.create({
  CLASSDEF: {
      name:  'TeamItemSizing'
  },
  
  initialize: function TeamItemSizing_initialize(options) {
    this.total_height = options.total_height;
    this.spacing = options.spacing;
    if(options.sections instanceof Array) {
      this.sections = options.sections;
    } else {
      this.sections = [options.sections];
    }
  },
  
  serialize: function TeamItemSizing_serialize(queryComponents, prefix) {
    queryComponents.push(encodeURIComponent(prefix + "[total_height]") + "=" + encodeURIComponent(this.total_height));
    queryComponents.push(encodeURIComponent(prefix + "[spacing]") + "=" + encodeURIComponent(this.spacing));
    for(var i=0; i < this.sections.length; i++) {
      queryComponents.push(encodeURIComponent(prefix + "[sections][]") + "=" + encodeURIComponent(this.sections[i]));
    }
  },
  
  serializeToOptions: function TeamItemSizing_serializeToOptions() {
    var o = {};
    o.total_height=this.total_height;
    o.spacing=this.spacing;
    o.sections = [];
    for(var i=0; i < this.sections.length; i++) {
      o.sections.push(this.sections[i]);
    }
    return o;
  }
  
});
    

var PlaceholderItem = Class.create({
  CLASSDEF: {
      name:  'PlaceholderItem',
      parent: Item
  },
  
  initialize: function PItem_initialize(id, cViewProcess, asset, options) {
    PlaceholderItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    this.useCanvas = false;
    this.allowHandleResizing = true;
    this.itemType = 3;
    this.itemTypeName = "Placeholder";
    this.editClickEvent = this.editClick.bindAsEventListener(this);
    this.editSaveClickEvent = this.editSaveClick.bindAsEventListener(this);
    this.editCancelClickEvent = this.editCancelClick.bindAsEventListener(this);
    this.text = dopt(options, "text", "");
    this.setName(this.text);
  },
  
  initialiseManagePane: function PItem_initialiseManagePane() {
    log("PlaceholderItem.initialiseManagePane()");
    PlaceholderItem.parentClass.method("initialiseManagePane").call(this);
    
    this.editButton = $("mp1_" + this.elId + "_edit");
    this.editButton.style.cursor = "pointer";
    Event.observe(this.editButton, "click", this.editClickEvent);
    

    this.editSaveButton = $("mp1_" + this.elId + "_save_edit");
    this.editSaveButton.style.cursor = "pointer";
    Event.observe(this.editSaveButton, "click", this.editSaveClickEvent);
    
    this.editCancelButton = $("mp1_" + this.elId + "_cancel_edit");
    this.editCancelButton.style.cursor = "pointer";
    Event.observe(this.editCancelButton, "click", this.editCancelClickEvent);
    
    this.editContainer = $("mp1_" + this.elId + "_edit_text_container");
    this.editTextArea = $("mp1_" + this.elId + "_edit_text");
    
    var self = this;
    $('mp1_' + this.elId + '_convert_to_image').onclick = function() {
      self.cViewArea.selectItem(null);
      d.addImage(self.cViewProcess.id, { toReplace: self} );
    };
    
    $('mp1_' + this.elId + '_convert_to_text').onclick = function() {
      self.cViewArea.selectItem(null);
      d.addText(self.cViewProcess.id, { toReplace: self} );
    };
    
    
    if(!this.canModifyDesign) {
      $("mp1_"+this.elId+"_li_arrows").hide();
    }
  },
  
  buildElement: function TxtItem_buildElement() {
    if ($("d_" + this.elId)) $("d_" + this.elId).remove();
    this.el = document.createElement("IMG");
    this.el.id = "d_" + this.elId;
    this.el.style.display = "none";
    this.el.style.position = "absolute";
    this.el.style.cursor="pointer";
	
    if(useAlphaHack) {
      this.el.src = d.pathPrefix + "/images/trans.gif";
    }
    this.cViewArea.canvas.appendChild(this.el);
    this.el.style.zIndex = this.getElementZIndex();
    
    setTransPng(this.el, this.getSrc());
    
    return this.el;
  },
  
  isPlaceholder: function() {
    return true;
  },
  
  moveToDesigner: function PItem_moveToDesigner(cViewProcess, resizeScale, oldLeft, oldTop, offsetLeft, offsetTop) {
    PlaceholderItem.parentClass.method("moveToDesigner").call(this, cViewProcess, resizeScale, oldLeft, oldTop, offsetLeft, offsetTop);
    this.setPlaceholderImage();
  },
  
  //do nothing: placeholder can rescale to desired scale....
  checkScale: function PItem_checkScale(desiredScale, process) {
    return desiredScale;
  },
  
  del: function PItem_del() {
    if(this.editButton!=null) {
      Event.stopObserving(this.editButton, "click", this.editClickEvent);
      Event.stopObserving(this.editSaveButton, "click", this.editSaveClickEvent);
      Event.stopObserving(this.editCancelButton, "click", this.editCancelClickEvent);
      //Event.stopObserving(this.advButton, "click", this.advClickEvent);
    }
    PlaceholderItem.parentClass.method("del").call(this);
  },
  
  //does this item handle its own start positions?
  startPosition: function PItem_startPosition() {
    return false;
  },
  
  setHeight: function ImgItem_setHeight(height) {
    height = parseFloat(height);
    this.el.style.height = height + "px";
  },
  
  setWidth: function ImgItem_setWidth(width) {
    this.el.style.width = width + "px";
    return true;
  },
  
  setTop: function ImgItem_setTop(top) {
    this.el.style.top = top + "px";
  },
  setLeft: function ImgItem_setLeft(left) {
    this.el.style.left = left + "px";
  },
  
  setReferenceRotation: function() {
    
  },
  
  resizeFinished: function() {
    PlaceholderItem.parentClass.method("resizeFinished").call(this);
    this.setPlaceholderImage();
  },
  
  editClick: function PItem_editClick(event) {
    Event.stop(event);
    this.select();
    this.editTextArea.value = this.text;
    this.editButton.style.display = 'none';
    this.managePaneBody.style.display = 'none';
    this.editContainer.style.display = '';
    return false;
  },
  
  editSaveClick: function PItem_editSaveClick(event) {
    this.text = this.editTextArea.value;
    this.setName(this.text);
    this.editButton.style.display = '';
    this.managePaneBody.style.display = '';
    this.editContainer.style.display = 'none';
    this.setDirty();
    this.setPlaceholderImage();
  },
  
  editCancelClick: function PItem_editCancelClick(event) {
    this.editButton.style.display = '';
    this.managePaneBody.style.display = '';
    this.editContainer.style.display = 'none';
  },
  

  serializeToOptions: function Item_serializeToOptions(o) {
    o = PlaceholderItem.parentClass.method("serializeToOptions").call(this,o);
     
    o.text =  this.text;
                                                      
    return o;
  },
  
  getSrc: function() {
    return "/designer/placeholder_image?txt=" + encodeURIComponent(this.text) + "&h=" + this.height + "&w=" + this.width + "&scale=" + this.cViewProcess.productProcess.designScale;
  },
  
  setPlaceholderImage: function() {
    if(this.el == null) return;
    setTransparentImage(this.el, this.el, this.getSrc());
  }
  
  
});

var Asset = Class.create({
  CLASSDEF: {
      name: 'Asset'
  },
  
  initialize: function A_initialize(options, designer) {
    

    this.id = options.id;
    this.baseUrl = options.bURL; //base url
    this.urlOptions = {
       bURL: options.bURL,
       tURL: options.tURL,
       sURL: options.sURL,
       url: options.url
    };
    
    this.sUrl = this.baseUrl + "/" + options.sURL; //small url
    this.url = this.baseUrl + "/" + options.tURL; //thumb url
    this.wUrl = this.baseUrl + "/" + options.url; //working url
    this.width = options.width;
    this.height = options.height;
    
    this.type = options.type;
    this.usesTransparency = options.trans;
    this.name = options.name;
    this.colors = options.c;
    this.colorWays = options.cw;
    this.defaultColorway = options.dcw;
    this.stitchCount = options.tc;
    this.processes = options.p;
    this.ownerType = options.ot;
    this.imageType = options.it;
    this.defaultColorCount = options.dcc;
    this.metaData = options.meta;
    
    this.aspectRatio = parseFloat(this.width) / parseFloat(this.height);
    var d = this.scale(100,100);
    this.tHeight = d.h;
    this.tWidth = d.w;
    this.workingAssetLoaded = false;
    this.version = 0;
    this.decorationLibraryId = options.dl;
    this.digitizedProcessId = options.dp;
    this.digitizedState = options.dps;
    //NTA: we need to set the "real asset id" which requires asset.to_json to include self.asset_id
    //this will also need LibraryImage to be updated (they duplicate functionality)
    
  },
  
  getUrl: function A_getUrl() {
    //log("getting url");
    if(this.version==0) {
      return this.url;
    }
    return this.addUrlParameter(this.url,this.version);
  },
  
  getWUrl: function A_getWUrl() {
    if(this.version==0) {
      return this.wUrl;
    }
    return this.addUrlParameter(this.wUrl, this.version);
  },
  
  getSUrl: function A_getSUrl() {
    //log("getting url");
    if(this.version==0) {
      return this.sUrl;
    }
    return this.addUrlParameter(this.sUrl,this.version);
  },
  
  addUrlParameter: function A_addUrlParameter(url, param) {
    if(url.indexOf("?") == -1) {
      return url + "?" + param;
    } else {
      return url + "&" + param;
    }
  },
  
  scale: function A_scale(mh,mw) {
    return this.scaleDims(this.height, this.width, mh, mw);
  },
  
  scaleDims: function A_scaleDims(h,w,mh,mw) {
    var s1 = mh / h;
    var s2 = mw / w;
    
    if(s1 > s2) {
      s1 = s2;
    }
    return {h: h * s1, w: w * s1};
  },
  
  getItemType: function A_getItemType() {
    return 0;
  },
  
  loadWorkingAsset: function A_loadWorkingAsset(callback) {
    if(this.workingAssetLoaded) {
      callback();
      return;
    }
    if(this.waCallbacks != null) {
      this.waCallbacks.push(callback);
      return;
    }
    log("building callback img");
    this.waCallbacks = [callback];
    var cbimg = document.createElement("IMG");
    cbimg.style.display = "none";
    //var self = this;
    
    backgroundLoadImage(this.getWUrl(), function(data) {
        
        this.workingAssetLoaded = true;
        for(var i=0;i<this.waCallbacks.length;i++) {
          this.waCallbacks[i]();
        }
        this.waCallbacks = null;
        
    }.bind(this), null);
    
    /*document.body.appendChild(cbimg);
    log("appended img");
    cbimg.src = this.getWUrl();
    if(cbimg.complete) {
      self.workingAssetLoaded = true;
      for(var i=0;i<self.waCallbacks.length;i++) {
        self.waCallbacks[i]();
      }
      self.waCallbacks = null;
      document.body.removeChild(cbimg);
    } else {
      cbimg.onload = function() {
        log("loaded");
        self.workingAssetLoaded = true;
        for(var i=0;i<self.waCallbacks.length;i++) {
          self.waCallbacks[i]();
        }
        self.waCallbacks = null;
        document.body.removeChild(cbimg);
      };
    }*/
  },
  
  refresh: function A_refresh() {
    var a = $("g_0_" + this.id);
    if(a!=null) {
      a.src = this.getUrl();
    }
    a = $("g_1_" + this.id);
    if(a!=null) {
      a.src = this.getUrl();
    }
  },
  
  getMetaData: function A_getMetaData(keys) {
    var curMap = this.metaData; 
    for(var i=0; i < keys.length; i++) {
      if(curMap == null) {
        return null;
      }
      curMap = curMap[keys[i]];
    }
    return curMap;
  },
  
  setMetaData: function A_setMetaData(keys, value) {
    if(this.metaData == null) {
      this.metaData = {};
    }
    var curMap = this.metaData;
    var subMap = null;
    for(var i=0; i < keys.length - 1; i++) {
      subMap = curMap[keys[i]];
      if(subMap == null) {
        subMap = {};
        curMap[keys[i]] = subMap;
      }
      curMap = subMap;
    }
    curMap[keys[keys.length-1]] = value;
    return value;
  },
  
  getColors: function A_getColors(colorCount) {
    if(colorCount == 8 && this.colors != null) {
      return this.colors; //backwards compatability
    }
    if(colorCount == null) {
      colorCount = this.defaultColorCount;
    }
    if(colorCount == null) {
      return;
    }
    return this.getMetaData(["split_colors", colorCount, "colors"]);
  },
  
  getOpacity: function A_getOpacity(transparentColor, callback) {
    if((transparentColor==null) && (!this.usesTransparency)) {
      //no need to check with server....
      callback(1.0);
    } else if((transparentColor==null) && (this.getMetaData("percent_opaque") != null)) {
      //we already have the percent opaque...
      callback(parseFloat(this.getMetaData("percent_opaque")));
    } else {
      var ajax = new Ajax.Request(d.ajaxUrl("/ppr/shared/library/get_percent_opaque/" + this.id + ((transparentColor==null) ? "" : ("?trans_color=" + encodeURIComponent(transparentColor)))) , {
            asynchronous:true, 
            evalScripts:true, 
            onSuccess: function A_onSuccess(transport) {
              var data =  transport.responseText.evalJSON();
              callback(parseFloat(data.percent_opaque));
            },
            onFailure: function A_onFailure(transport) {
              alert(ml("An error occured getting opacity details of image"));
            }
        });
    }
  },
  
  hasCost: function A_hasCost() {
    if(this.decorationLibraryId == 0 || this.decorationLibraryId == null) {
      return false; // we aren't a decorationLibrary so we have no charge
    } else {
      return (d.chargeDecorationLibraries[this.decorationLibraryId].range1price > 0);
    }
  },
  
  // find the unit cost of a decoration library asset
  itemCost: function A_itemCost() {
    log("asset " + this.id + " decorationLibraryId " + this.decorationLibraryId);
    if(this.decorationLibraryId == 0 || this.decorationLibraryId == null) {
      return 0; // we aren't a decorationLibrary so we have no charge
    } else {
      // find all usages of this asset in all products
      var qty = 0;
      for (var i=0; i < d.cart.products.length; i++) {
       product = d.cart.products[i];
       var product_asset_qty = 0;
       for (var view_id in product.views) {
         var view = product.views[view_id];
         for (var area_id in view.areas) {
           var area = view.areas[area_id];
           for (var item_id in area.allItems) {
             var item = area.allItems[item_id];
             if(item.asset.id == this.id ) { //NTA: check asset.real_id as well
               product_asset_qty++;
             }
           };
         };
       };
       qty += (product_asset_qty * product.qty);
      };

      dec_price = d.chargeDecorationLibraries[this.decorationLibraryId];
      if(dec_price != null) {
        if(qty >= 0 && qty <= 4) {
          var rangeprice = dec_price.range1price;
        } else if (qty >= 5 && qty <= 12) {
          var rangeprice = dec_price.range2price;
        } else if (qty >= 13 && qty <= 24) {
          var rangeprice = dec_price.range3price;
        } else if (qty >= 25 && qty <= 48) {
          var rangeprice = dec_price.range4price;
        } else if (qty >= 49 && qty <= 72) {
          var rangeprice = dec_price.range5price;
        } else if (qty >= 73 && qty <= 144) {
          var rangeprice = dec_price.range6price;
        } else if (qty >= 145) {
          var rangeprice = dec_price.range7price;
        }
        
        rangeprice = rangeprice + (d.declibMarkup / 100.0) * rangeprice;
        rangeprice = rangeprice + d.aMarkupRate  * rangeprice;
        return rangeprice;
      } else {
        return 0;
      }
    }
  }
});

var TextAsset = Class.create({
  CLASSDEF : {
      name:  'TextAsset',
      parent: Asset
  },
  initialize: function TA_initialize(options) {
    this.height = 16;
    this.width = 14;
    this.url = d.pathPrefix + "/images/mp/text_icon.gif";
  },
  getItemType: function TA_getItemType() {
    return 1;
  }
});

var TeamNameAsset = Class.create({
  CLASSDEF : {
      name:  'TeamNameAsset',
      parent: Asset
  },
  initialize: function TN_initialize(options) {
    this.height = 16;
    this.width = 14;
    this.url = d.pathPrefix + "/images/mp/text_icon.gif";
  },
  getItemType: function TN_getItemType() {
    return 2;
  }
});


var PlaceholderAsset = Class.create({
  CLASSDEF : {
      name:  'PlaceholderAsset',
      parent: Asset
  },
  initialize: function TA_initialize(options) {
    this.height = 16;
    this.width = 14;
    this.url = d.pathPrefix + "/images/mp/text_icon.gif";
  },
  getItemType: function TA_getItemType() {
    return 3;
  }
});



/* images */
var type_0 = '<div id="mp1_[ID]" class="managepaneoff" stopdeselect="true">' +
        '<div class="managepane_header" id="mp1_[ID]_bar" style="cursor:pointer;">' +
		'<ul class="mp_title">' +
			'<li id="mp1_[ID]_ico_c" class="icon"><span id="mp1_[ID]_ico_alert" class="alert_icon_none"></span><img id="mp1_[ID]_stimg" border="0"/></li>' +
			'<li class="title"><span class="managepane_title" id="mp1_[ID]_title"></span></li>' + 
			'<li id="mp1_[ID]_fc_dim" class="dimension">' +
				'<span id="mp1_[ID]_dim_value"></span>' +
			'</li>'+
		'</ul>'+ 
		  /* controls */
       '<ul id="mp1_[ID]_controls" class="mp_control">'+ 
	   		'<li class="lock_icon" id="mp1_[ID]_lock_icon" tooltip="' + ml('This image is locked so it will not be able to be changed when users customise this design') + '">locked</li>'+
	   		'<li id="mp1_[ID]_q_container" class="quality">' +
				'<div id="mp1_[ID]_q_c" class="quality_container" tooltip="' + ml('Indication of quality: \'Green\' is good, \'Yellow\' is of reduced quality, and \'Red\' is bad quality') + '">'+
					'<span id="mp1_[ID]_q_c_a" class="quality_good">'+
						'&nbsp;' +
					'</span>'+
				'</div>' +
			  '</li>' + 
            '<li class="up"><span id="mp1_[ID]_zup" tooltip="' + ml('Move this image higher up so it prints on top of other design elements') + '">up</span></li>'+
            '<li class="down"><span id="mp1_[ID]_zdown" tooltip="' + ml('Move this image lower down so it prints below other design elements') + '">down</span></li>' + 
			'<li><a href="#" id="mp1_[ID]_remove" class="delete" tooltip="' + ml('Remove this image') + '">delete</a></li>' + 
			/*quality stuff */
			 
          '</ul>' + 
		  
		 
        '</div>' +
		
		/* body of image layer */
        '<div class="managepane_body" id="mp1_[ID]_body" style="display:none;" >' +
		'<div class="advanced_button" id="mp1_[ID]_fc_adv"><a href="#" id="mp1_[ID]_adv_link"><span>' + ml("advanced") + '<!--<img src="[PATH_PREFIX]/images/mp/arrow_down.gif" id="mp1_[ID]_adv" border="0"/>--></span></a></div>'+
        '<div class="pane_alerts" id="mp1_[ID]_alerts" style="display:none;"><ol id="mp1_[ID]_a_error" class="error" style="display:none;"></ol><ol id="mp1_[ID]_a_warn" class="warn" style="display:none;"></ol><ol id="mp1_[ID]_a_notice" class="notice" style="display:none;"></ol></div>' +
          '<div class="layer_container">'+
            /* image */
            '<div id="mp1_[ID]_tn" class="layer_left">' +
              '<ul class="image_options">'+
			  	'<li class="image"><label>' + ml('image') + '</label><img id="mp1_[ID]_img" border="0" width="69" /></li>' +
				'<li id="mp1_[ID]_color_row" style="display:none;" class="colors"><label>' + ml('colors') + '</label><ol id="mp1_[ID]_colors"></ol></li>'+
				'<li id="mp1_[ID]_colorways" style="display:none;"><select id="mp1_[ID]_colorway"></select></li>'+
			'</ul>'+
			   '<ul class="mp_sel_color">' +
			   		'<li id="mp1_[ID]_trans_container" style="display:none;" class="select_transparent"><label tooltip="' + ml('Choose a color in your image that you want to be transparent') + '"><img id="mp1_[ID]_transparent_color" class="mp_color_button" src="[PATH_PREFIX]/images/trans.gif" style="height: 10px; margin: 2px 5px 0 0; width: 10px;" />select transparent</label></li>' +
              '</ul>' +
            '</div>'+
            
            /* contains controls */
            '<div class="layer_right">'+
              
              '<ul class="layer_controls">'+
			  	/* movement */
                '<li id="mp1_[ID]_fc_movement" class="movement">' +
                  '<label>' + ml("position") + '</label>' +
				  '<div>'+
                  '<!--<img src="../../images/mp/movement.jpg" id="mp1_[ID]_move"/>-->' +
				 				 '<ul>'+
									/* ----row 1 */
									'<li class="t_l"><a href="#" id="mp1_[ID]_move_0" tooltip="' + ml('Move image up and to the left') + '">&nbsp;</a></li>'+
									'<li class="t_c"><a href="#" id="mp1_[ID]_move_1" tooltip="' + ml('Move image up') + '">&nbsp;</a></li>'+
									'<li class="t_r"><a href="#" id="mp1_[ID]_move_2" tooltip="' + ml('Move image up and to the right') + '">&nbsp;</a></li>'+
									/* ----row 2 */
									'<li class="c_l"><a href="#" id="mp1_[ID]_move_3" tooltip="' + ml('Move image to the left') + '">&nbsp;</a></li>'+
									'<li class="c_c"><a href="#" id="mp1_[ID]_move_4">&nbsp;</a></li>'+
									'<li class="c_r"><a href="#" id="mp1_[ID]_move_5" tooltip="' + ml('Move image to the right') + '">&nbsp;</a></li>'+
									/* ----row 3 */
									'<li class="b_l"><a href="#" id="mp1_[ID]_move_6" tooltip="' + ml('Move image down and to the left') + '">&nbsp;</a></li>'+
									'<li class="b_c"><a href="#" id="mp1_[ID]_move_7" tooltip="' + ml('Move image down') + '">&nbsp;</a></li>'+
									'<li class="b_r"><a href="#" id="mp1_[ID]_move_8" tooltip="' + ml('Move image down and to the right') + '">&nbsp;</a></li>'+
								'</ul>'+
				  '</div>'+
                '</li>' +
				
				/* size */
        '<li id="mp1_[ID]_fc_size_arrows" class="arrows">' +
          '<label>' + ml("size") + '</label>' +
          '<ol>'+
             '<li class="arrow_up"><a href="#" id="mp1_[ID]_sizeup" tooltip="' + ml('Increase image size') + '">&nbsp;</a></li>' +
             '<li class="arrow_down"><a href="#" id="mp1_[ID]_sizedown" tooltip="' + ml('Decrease image size') + '">&nbsp;</a></li>' +
          '</ol>'+
        '</li>' +
				
				/* rotate */
				'<li id="mp1_[ID]_fc_rotate" class="rotate">'+
					'<label>' + ml("rotate") + '</label>' +
					'<div id="mp1_[ID]_rotate_container" class="rotate_container">' +
						'<img src="[PATH_PREFIX]/images/mp/rotate_circle_with_line.png" id="mp1_[ID]_rotate" style="position:absolute;left:15px;top:13px;"/><br/>' +
						'<a class="rotate_left" id="mp1_[ID]_rotate_l" tooltip="' + ml('Rotate image to the left') + '">&nbsp;</a>' +
						'<a class="rotate_right" id="mp1_[ID]_rotate_r" tooltip="' + ml('Rotate image to the right') + '">&nbsp;</a>'+
							'<span class="degrees"><input type="text" value="0" id="mp1_[ID]_rotate_t"/>&deg;</span>'+
					'</div>' +
				'</li>'+
				/* align */
				'<li class="center_both">'+
					'<label>' + ml("align") + '</label>' +
					'<ol>'+
						'<li class="center_h"><a href="#" id="mp1_[ID]_center_h" tooltip="' + ml('Center the image horizontally') + '">&nbsp;</a></li>'+
						'<li class="center_v"><a href="#" id="mp1_[ID]_center_v" tooltip="' + ml('Center the image vertically') + '">&nbsp;</a></li>'+
						'<li class="center"><a href="#" id="mp1_[ID]_center" tooltip="' + ml('Center the image both horizontally and vertically') + '">&nbsp;</a></li>'+
					'</ol>'+
				'</li>'+
              '</ul>' +
			  
			  /* aspect ratio */
				'<div class="mp_advanced">'+
					'<div class="ratio" id="mp1_[ID]_ratio_container"><input type="checkbox" name="mp1_[ID]_ar" id="mp1_[ID]_ar"/><label for="mp1_[ID]_ar" tooltip="' + ml('When resizing, make the width/height stay in proportion so the image does not become distorted') + '">' + ml('resize proportionally') + '</label></div>' +
				'</div>'+
            /* closes container */
            '</div>' +
          '</div>'+
        
        /* advanced effects */
        '<div id="mp1_[ID]_advt" style="clear: both; display: none;" class="advanced_pane" >' +
		
			'<div id="mp1_[ID]_lock_container" class="lock_container" style="display: none;" tooltip="' + ml('This will stop users customizing this design from changing this image') + '">'+
				'<input type="checkbox" name="mp1_[ID]_lock" id="mp1_[ID]_lock" />'+
				'<label for="mp1_[ID]_lock" >' + ml('lock this layer') + '</label>'+
				'</ol>'+
			'</div>'+
		
			'<div class="et_container" id="mp1_[ID]_fc_effects_content">'+
				'<ul class="et_tabs">'+
					 '<li id="i_effects_[ID]" class="et_selected_tab"><a href="#" id="ic_effects_[ID]">' + ml("Effects") + '</a></li>' +
					'<li id="i_border_[ID]"><a href="#" id="ic_border_[ID]">' + ml("Border") + '</a></li>' +
				'</ul>'+
			
				'<div class="et_content">'+
					'<div id="i_effects_tab_[ID]" class="et_etab">' +
						//'<table cellspacing="0">' +
						  //'<tr>' +
							//'<td width="160">' +
							'<div class="et_elist">' +
								'<div class="et_scroll" id="i_effects_list_[ID]">' +
									'Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/> ' +
								'</div>' +
							  '</div>' +
							//'</td>' +
							//'<td width="190">' +
							//  '<div >' +
								'<div class="et_props" id="i_aeffects_list_[ID]">' +
								  ml('No Effects Applied: Click on an effect from the list to the left to apply it to your image.')  +
								'</div>' +
							//  '</div>' +
							//'</td>' +
						  //'</tr>' +
						//'</table>' +
					'</div>' +
					'<div id="i_border_tab_[ID]" style="display:none;">' +
						//'<table>' +
						  //'<tr>' +
							//'<td width="340">' +
							  '<div class="et_list">' +
								'<div class="et_scroll" id="i_borders_list_[ID]">' +
								'Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/> ' +
								'</div>' +
							  '</div>' +
							//'</td>' +
						  //'</tr>' +
						//'</table>' +
					'</div>' +
				'</div>'+
			'</div>'+
		 /* close of the advanced tab */
        '</div>'+
      '</div>' +
    '</div>';

/* text */
var type_1 = '<div id="mp1_[ID]" class="managepaneoff" stopdeselect="true">' +
      '<div class="managepane_header" id="mp1_[ID]_bar" style="cursor:pointer;">' +
        '<ul class="mp_title">' +
			'' +
			'<li id="mp1_[ID]_ico_c" class="icon"><span id="mp1_[ID]_ico_alert" class="alert_icon_none"></span><img id="mp1_[ID]_stimg" /></li>' +
			'<li class="title"><span class="managepane_title" id="mp1_[ID]_title"></span></li>' + 
		  '<li id="mp1_[ID]_fc_dim" class="dimension">' +
				'<span id="mp1_[ID]_dim_value"></span>' +
			'</li>'+
        '</ul>'+
        '<ul id="mp1_[ID]_controls" class="mp_control">'+
          '<li class="lock_icon" id="mp1_[ID]_lock_icon" tooltip="' + ml('This text is locked so it will not be able to be changed when users customise this design') + '">locked</li>'+
          '<li><a href="#" id="mp1_[ID]_edit" class="change" tooltip="' + ml('Edit text') + '">' + ml('edit text') + '</a></li>' +
          '<li class="up"><span id="mp1_[ID]_zup" tooltip="' + ml('Move this text higher up so it prints on top of other design elements') + '">up</span></li>'+
          '<li class="down"><span id="mp1_[ID]_zdown" tooltip="' + ml('Move this text lower down so it prints below other design elements') + '">down</span></li>' +   
		  '<li><a href="#" id="mp1_[ID]_remove" class="delete" tooltip="' + ml('Remove this text') + '">delete</a></li>' +
        '</ul>' + 
      '</div>' +
      '<div id="mp1_[ID]_body" style="display:none;" class="mp_body_below">' +
        /* ---- */
		'<div class="advanced_button" id="mp1_[ID]_fc_adv"><a href="#" id="mp1_[ID]_adv_link"><span>' + ml("advanced") + '<!--<img src="[PATH_PREFIX]/images/mp/arrow_down.gif" id="mp1_[ID]_adv" border="0"/>--></span></a></div>'+
        
		'<div class="managepane_body">' +
			'<div class="pane_alerts" id="mp1_[ID]_alerts" style="display:none;"><ol id="mp1_[ID]_a_error" class="error" style="display:none;"></ol><ol id="mp1_[ID]_a_warn" class="warn" style="display:none;"></ol><ol id="mp1_[ID]_a_notice" class="notice" style="display:none;"></ol></div>' +
			/*==========layer right stuff */
			'<div class="layer_container">' +
				'<div class="layer_right">' +
					'<ul class="layer_controls">' +
						
						/* ----move */
						'<li id="mp1_[ID]_fc_movement" class="movement">'+
							'<label>' + ml("position") + '</label>' +
							'<div>'+
								'<!--<img src="[PATH_PREFIX]/images/mp/movement.jpg" id="mp1_[ID]_move" style="display: block;" />-->'+
								'<ul>'+
									/* ----row 1 */
									'<li class="t_l"><a href="#" id="mp1_[ID]_move_0" tooltip="' + ml('Move text up and to the left') + '">&nbsp;</a></li>'+
									'<li class="t_c"><a href="#" id="mp1_[ID]_move_1" tooltip="' + ml('Move text up') + '">&nbsp;</a></li>'+
									'<li class="t_r"><a href="#" id="mp1_[ID]_move_2" tooltip="' + ml('Move text up and to the right') + '">&nbsp;</a></li>'+
									/* ----row 2 */
									'<li class="c_l"><a href="#" id="mp1_[ID]_move_3" tooltip="' + ml('Move text to the left') + '">&nbsp;</a></li>'+
									'<li class="c_c"><a href="#" id="mp1_[ID]_move_4">&nbsp;</a></li>'+
									'<li class="c_r"><a href="#" id="mp1_[ID]_move_5" tooltip="' + ml('Move text to the right') + '">&nbsp;</a></li>'+
									/* ----row 3 */
									'<li class="b_l"><a href="#" id="mp1_[ID]_move_6" tooltip="' + ml('Move text down and to the left') + '">&nbsp;</a></li>'+
									'<li class="b_c"><a href="#" id="mp1_[ID]_move_7" tooltip="' + ml('Move text down') + '">&nbsp;</a></li>'+
									'<li class="b_r"><a href="#" id="mp1_[ID]_move_8" tooltip="' + ml('Move text down and to the right') + '">&nbsp;</a></li>'+
								'</ul>'+
							'</div>'+
						'</li>'+
						
						/* ----size */
						'<li id="mp1_[ID]_fc_size_arrows" class="arrows">' +
							'<label>'+ml("size")+'</label>'+
							'<ol>' +
								'<li class="arrow_up"><a href="#" id="mp1_[ID]_sizeup" tooltip="' + ml('Increase text size') + '">&nbsp;</a></li>' +
								'<li class="arrow_down"><a href="#" id="mp1_[ID]_sizedown" tooltip="' + ml('Decrease text size') + '">&nbsp;</a></li>' +
							'</ol>' +
						'</li>' +
						
						/* ----rotate */
						'<li id="mp1_[ID]_fc_rotate" class="rotate">'+
							'<label>'+ml("rotate")+'</label>'+
							'<div id="mp1_[ID]_rotate_container" class="rotate_container">' +
								'<img src="[PATH_PREFIX]/images/mp/rotate_circle_with_line.png" id="mp1_[ID]_rotate" style="position: absolute; left: 15px; top: 13px;"/><br/>' +
								'<a class="rotate_left" id="mp1_[ID]_rotate_l" tooltip="' + ml('Rotate text to the left') + '">&nbsp;</a>' +
								'<a class="rotate_right" id="mp1_[ID]_rotate_r" tooltip="' + ml('Rotate text to the right') + '">&nbsp;</a>'+
								'<span class="degrees"><input type="text" value="0" id="mp1_[ID]_rotate_t"/>&deg;</span>'+
							'</div>' +
						
						'</li>' +
						/* align */
						'<li class="center_both">'+
							'<label>' + ml("align") + '</label>' +
							'<ol>'+
								'<li class="center_h"><a href="#" id="mp1_[ID]_center_h" tooltip="' + ml('Center the text horizontally') + '">&nbsp;</a></li>'+
								'<li class="center_v"><a href="#" id="mp1_[ID]_center_v" tooltip="' + ml('Center the text vertically') + '">&nbsp;</a></li>'+
								'<li class="center"><a href="#" id="mp1_[ID]_center" tooltip="' + ml('Center the text both horizontally and vertically') + '">&nbsp;</a></li>'+
							'</ol>'+
						'</li>'+
						
						/* ----??? (some embroidery thing?) */
						'<li id="mp1_[ID]_tn_width_c" style="display:none;" tooltip="' + ml('Specify the width of the team name area to determine the final printing size of longer names') + '">' +
							'<label>'+ml("width")+'</label>'+
							'<div id="tn_width_[ID]_t" style="width: 88px;" class="et_bar">' +
								'<div id="tn_width_[ID]_s" class="et_grab"> </div>' +
							'</div>'+
						'</li>' +
					'</ul>' +
				'</div>' +
				
				/* ----rest of the stuff (font related stuff) */
				'<div class="layer_left">' +
					'<div class="font_options">' + 
					  '<label>' + ml('text options') + '</label>'+
						/* ----color */
						'<div class="cell">'+
							'<div class="mp_color_button" tooltip="' + ml('Choose text color') + '"><div id="mp1_[ID]_cb"><span class="drop_arrow">&nbsp;</span></div></div>' +
							'<div class="mp_font_selector" id="mp1_[ID]_font_button" style="margin-left:5px" tooltip="' + ml('Choose font') + '"><div class="mp_font_container"><img id="mp1_[ID]_font_text" src="[PATH_PREFIX]/images/trans.gif"/></div><span class="drop_arrow">&nbsp;</span></div>'+
						'</div>' +
						'<div class="cell">'+
							'<ol class="text_transform">'+
								'<li class="bold" tooltip="' + ml('Bold') + '"><span id="mp1_[ID]_b">B</span></li>' +
								'<li class="italic" tooltip="' + ml('Italics') + '"><span id="mp1_[ID]_i">I</span></li>' +
							'</ol>' +
							/* ----align */
							'<!--<label>'+ml("align")+'</label>-->'+
							'<ol id="mp1_[ID]_alc" class="mp_align">'+
								'<li class="mp_align_left" tooltip="' + ml('Left align text when it spans multiple lines') + '"><span id="mp1_[ID]_al" class="button_down">&nbsp;</span></li>'+
								'<li class="mp_align_center" tooltip="' + ml('Center align text when it spans multiple lines') + '"><span id="mp1_[ID]_ac" class="button_up">&nbsp;</span></li>'+
								'<li class="mp_align_right" tooltip="' + ml('Right align text when it spans multiple lines') + '"><span id="mp1_[ID]_ar" class="button_up" width="27">&nbsp;</span></li>'+
							'</ol>' +
							/* ---- */
							/* ----sizefield */
						'<span id="mp1_[ID]_sizefield_container" style="display:none;" class="mm" tooltip="' + ml('Specify the font size in millimeters') + '">' +
							'<input id="mp1_[ID]_sizefield" type="text" size="2" /><label>' + ml('mm') + '</label>' +
						'</span>' +
						'</div>' +
					'</div>' +
				'</div>' + /*close layer left */
			'</div>' + /*close layer container */
		'</div>' +
        
        /*-------*/
        '<div class="advanced_pane"  id="mp1_[ID]_advt" style="display: none; position: relative;">'+
			
			'<div id="mp1_[ID]_lock_container" class="lock_container" style="display: none;" tooltip="' + ml('This will stop users customizing this design from changing this text') + '">'+
				'<ol>'+
					'<li>'+
					'<input type="checkbox" name="mp1_[ID]_lock" id="mp1_[ID]_lock" />'+
					'<label for="mp1_[ID]_lock">' + ml('lock this layer') + '</label>'+
					'</li>'+
				'</ol>'+
			'</div>'+
			
		
			'<div class="et_container">' +
				'<ul id="advanced_pane_tabs_[ID]" class="et_tabs">' +
				  '<li id="et_effects_[ID]" class="et_selected_tab"><a href="#" id="etc_effects_[ID]">' + ml("Effects") + '</a></li>' +
				  '<li id="et_gradient_[ID]" class="et_unselected_tab"><a href="#" id="etc_gradient_[ID]">' + ml("Gradient") + '</a></li>' +
				  '<li id="et_shape_[ID]" class="et_unselected_tab"><a href="#" id="etc_shape_[ID]">' + ml("Warp") + '</a></li>' +
				  '<li id="et_stroke_[ID]" class="et_unselected_tab"><a href="#" id="etc_stroke_[ID]">' + ml("Outline") + '</a></li>' +
				'</ul>' +
				'<div class="et_content">'+
					
					/* start transplanted content */
					
					 '<div id="ep_effects_[ID]" class="effects_pane">' +
					  /* effect type */
					  '<ul><li><label>effect type</label><select class="mp_select" id="ep_effecttype_[ID]"><option value="0">' + ml("None") + '</option><option value="1">' + ml("Shadow") + '</option><option value="2">' + ml("Glow") + '</option></select></li></ul>' +
					  '<div class="effects_container">'+
						'<ul>'+
						  /* shadow options */
						  '<li id="shadow_effects_[ID]" style="display:none;">' +
							'<label>offset</label>'+
							'<div class="pane_offset">'+
							  '<div id="mp_[ID]_offset" style="background: url([PATH_PREFIX]/images/mp/rotate_circle.gif) 0 0 no-repeat; height: 36px; width: 36px; " align="absmiddle">' +
								'<img id="mp_[ID]_offset_handle" src="[PATH_PREFIX]/images/sizer.gif" style="cursor: pointer; left: 15px; top: 15px; position: relative; " />'+
							  '</div>'+
							'</div>'+
						  '</li>'+
						  /* glow options */
						  '<li id="glow_effects_[ID]" style="display: none;">' +
							'<label>' + ml("glow strength") + '</label>' +
							'<div id="ep_g_str_[ID]_t" style="width: 88px; " class="et_bar">' +
							  '<div id="ep_g_str_[ID]_s" class="et_grab"> </div>' +
							'</div>' +
						  '</li>' +
	
						  /* color */
						  '<li>'+
							'<label>' + ml("color") + '</label><div class="mp_color_button"><div id="ep_effectcolor_[ID]"><span class="drop_arrow">&nbsp;</span></div></div>' +
						  '</li>'+
	
						  /* blur */
						  '<li>'+
							'<label>' + ml("blur") + '</label>'+
							'<div id="ep_b_str_[ID]_t" style="width: 88px;" class="et_bar">' +
							  '<div id="ep_b_str_[ID]_s" class="et_grab"> </div>' +
							'</div>'+
						  '</li>'+
						'</ul>'+
					  '</div>'+
					'</div>' +
	
					/* gradient */
					'<div id="ep_gradient_[ID]" style="display:none;" class="effects_pane">' +
					  '<ul>'+
						'<li><label>' + ml("gradient") + '</label><select class="mp_select" style="margin-bottom:1px;margin-top:2px;" id="ep_gradienttype_[ID]"><option value="0">None</option><option value="1">Vertical</option><option value="2">Horizontal</option></select></li>' +
						'<li><label>' + ml("color") + '</label><div class="mp_color_button"><div id="ep_gradient_color_[ID]"><span class="drop_arrow">&nbsp;</span></div></div></li>' +
					  '</ul>'+
					'</div>' +
	
					/* warp */
					'<div id="ep_shape_[ID]" style="display:none;" class="effects_pane">' +
					  '<ul>'+
						'<li><label>' + ml("Select Shape") + '</label><span id="warp_shape_select_[ID]" class="warp_shape_select"><select class="mp_select" id="ep_shape_sel_down_[ID]"></select></span></li>' +
						'<li id="ep_s_x_[ID]" class="mp_text_pane"><label>' + ml("width") + '</label>' +
						  '<div id="ep_x_str_[ID]_t" style="width:88px;" class="et_bar">' +
							'<div id="ep_x_str_[ID]_s" class="et_grab"> </div>' +
						  '</div>' +
						'</li>' +
						'<li id="ep_s_y_[ID]" class="mp_text_pane"><label>' + ml("height") + '</label>' +
						  '<div id="ep_y_str_[ID]_t" style=" width: 88px;" class="et_bar">' +
							'<div id="ep_y_str_[ID]_s" class="et_grab"> </div>' +
						  '</div>' +
						'</li>' +
					  '</ul>'+
					'</div>' +
	
					/* stroke */
					'<div id="ep_stroke_[ID]" style="display:none;" class="effects_pane">' +
					  '<ul>'+
						'<li>'+
						  '<label>width</label>'+
						  '<div id="ep_stroke_width_[ID]_t" style="width: 88px;" class="et_bar">' +
							'<div id="ep_stroke_width_[ID]_s" class="et_grab"> </div>' +
						  '</div>' + 
						'</li>' +
						'<li class="mp_text_pane">' +
						  '<label>' + ml("color") + '</label><div class="mp_color_button"><div id="ep_stroke_color_[ID]"><span class="drop_arrow">&nbsp;</span></div></div>' +
						'</li>'+
					  '</ul>'+
					'</div>' +
					/* end transplanted content */
				
				'</div>'+
			'</div>'+
		  /*-------*/
		'</div>'+
	  '</div>' +
      '<div id="mp1_[ID]_edit_text_container" class="edit_text_container" style="display: none;">' +
        '<textarea id="mp1_[ID]_edit_text" rows="5" cols="55" class="mp_textarea"></textarea>' +
        /*'<img src="[PATH_PREFIX]/images/mp/edit_text.gif" id="mp1_[ID]_save_edit"/>'+*/
        '<input type="button" class="button" value="' + ml('change text') + '" id="mp1_[ID]_save_edit" />'+
        /*'<img src="[PATH_PREFIX]/images/mp/cancel_text.gif" id="mp1_[ID]_cancel_edit"/>' +*/
        '<a href="#" class="cancel_button" id="mp1_[ID]_cancel_edit">' + ml('cancel') + '</a>'+
      '</div>' +
    '</div>';
    
    
/* placeholder */
var type_3 = '<div id="mp1_[ID]" class="managepaneoff" stopdeselect="true">' +
      '<div class="managepane_header" id="mp1_[ID]_bar" style="cursor:pointer;">' +
        '<ul class="mp_title">' +
          '' +
          '<li id="mp1_[ID]_ico_c" class="icon"><span id="mp1_[ID]_ico_alert" class="alert_icon_none"></span><img id="mp1_[ID]_stimg" /></li>' +
          '<li class="title"><span class="managepane_title" id="mp1_[ID]_title"></span></li>' + 
          '<li id="mp1_[ID]_fc_dim" class="dimension">' +
            '<span id="mp1_[ID]_dim_value"></span>' +
          '</li>'+
        '</ul>'+
        '<ul id="mp1_[ID]_controls" class="mp_control">'+
          '<li class="lock_icon" id="mp1_[ID]_lock_icon" tooltip="' + ml('This placeholder is locked so it will not be able to be changed when users customise this design') + '">locked</li>'+
          '<li><a href="#" id="mp1_[ID]_edit" class="change" tooltip="' + ml('Edit text') + '">' + ml('edit text') + '</a></li>' +
          '<li class="up"><span id="mp1_[ID]_zup" tooltip="' + ml('Move this placeholder higher up so it prints on top of other design elements') + '">up</span></li>'+
          '<li class="down"><span id="mp1_[ID]_zdown" tooltip="' + ml('Move this placeholder lower down so it prints below other design elements') + '">down</span></li>' +   
          '<li><a href="#" id="mp1_[ID]_remove" class="delete" tooltip="' + ml('Remove this placeholder') + '">delete</a></li>' +
        '</ul>' + 
      '</div>' +
      '<div id="mp1_[ID]_body" style="display:none;" class="mp_body_below">' +
        /* ---- */
		       
      '<div class="managepane_body">' +
        '<div class="pane_alerts" id="mp1_[ID]_alerts" style="display:none;"><ol id="mp1_[ID]_a_error" class="error" style="display:none;"></ol><ol id="mp1_[ID]_a_warn" class="warn" style="display:none;"></ol><ol id="mp1_[ID]_a_notice" class="notice" style="display:none;"></ol></div>' +
        /*==========layer right stuff */
        '<div class="layer_container">' +
          '<div class="layer_right">' +
            '<ul class="layer_controls">' +
              
              /* ----move */
              '<li id="mp1_[ID]_fc_movement" class="movement">'+
                '<label>' + ml("position") + '</label>' +
                '<div>'+
                  '<!--<img src="[PATH_PREFIX]/images/mp/movement.jpg" id="mp1_[ID]_move" style="display: block;" />-->'+
                  '<ul>'+
                    /* ----row 1 */
                    '<li class="t_l"><a href="#" id="mp1_[ID]_move_0" tooltip="' + ml('Move text up and to the left') + '">&nbsp;</a></li>'+
                    '<li class="t_c"><a href="#" id="mp1_[ID]_move_1" tooltip="' + ml('Move text up') + '">&nbsp;</a></li>'+
                    '<li class="t_r"><a href="#" id="mp1_[ID]_move_2" tooltip="' + ml('Move text up and to the right') + '">&nbsp;</a></li>'+
                    /* ----row 2 */
                    '<li class="c_l"><a href="#" id="mp1_[ID]_move_3" tooltip="' + ml('Move text to the left') + '">&nbsp;</a></li>'+
                    '<li class="c_c"><a href="#" id="mp1_[ID]_move_4">&nbsp;</a></li>'+
                    '<li class="c_r"><a href="#" id="mp1_[ID]_move_5" tooltip="' + ml('Move text to the right') + '">&nbsp;</a></li>'+
                    /* ----row 3 */
                    '<li class="b_l"><a href="#" id="mp1_[ID]_move_6" tooltip="' + ml('Move text down and to the left') + '">&nbsp;</a></li>'+
                    '<li class="b_c"><a href="#" id="mp1_[ID]_move_7" tooltip="' + ml('Move text down') + '">&nbsp;</a></li>'+
                    '<li class="b_r"><a href="#" id="mp1_[ID]_move_8" tooltip="' + ml('Move text down and to the right') + '">&nbsp;</a></li>'+
                  '</ul>'+
                '</div>'+
              '</li>'+
              
              /* size */
              '<li id="mp1_[ID]_fc_size_arrows" class="arrows">' +
                '<label>' + ml("size") + '</label>' +
                '<ol>'+
                   '<li class="arrow_up"><a href="#" id="mp1_[ID]_sizeup" tooltip="' + ml('Increase placeholder size') + '">&nbsp;</a></li>' +
                   '<li class="arrow_down"><a href="#" id="mp1_[ID]_sizedown" tooltip="' + ml('Decrease placeholder size') + '">&nbsp;</a></li>' +
                '</ol>'+
              '</li>' +
              
              /* align */
              '<li class="center_both">'+
                '<label>' + ml("align") + '</label>' +
                '<ol>'+
                  '<li class="center_h"><a href="#" id="mp1_[ID]_center_h" tooltip="' + ml('Center the text horizontally') + '">&nbsp;</a></li>'+
                  '<li class="center_v"><a href="#" id="mp1_[ID]_center_v" tooltip="' + ml('Center the text vertically') + '">&nbsp;</a></li>'+
                  '<li class="center"><a href="#" id="mp1_[ID]_center" tooltip="' + ml('Center the text both horizontally and vertically') + '">&nbsp;</a></li>'+
                '</ol>'+
              '</li>'+
            '</ul>' +
          '</div>' +
          '<div class="layer_left">' +
            '<div class="font_options">' + 
              '<label>' + ml('placeholder options') + '</label>'+
              /* ----color */
              '<div class="cell">'+
              '<ul class="placeholder_options">'+
                '<li class="mp_change_to_image" id="mp1_[ID]_convert_to_image" tooltip="' + ml('Convert to image') + '"><span>' + ml('Convert to image') + '</span><span>&nbsp;</span></li>' +
                '<li class="mp_change_to_text" id="mp1_[ID]_convert_to_text" tooltip="' + ml('Convert to text') + '"><span>' + ml('Convert to text') + '</span><span>&nbsp;</span></li>' +
               '</ul>'+
              '</div>' +
            '</div>' +
            '<div id="mp1_[ID]_lock_container" class="lock_container" style="display: none;" tooltip="' + ml('This will stop users customizing this design from changing this text') + '">'+
              '<ol>'+
                '<li>'+
                '<input type="checkbox" name="mp1_[ID]_lock" id="mp1_[ID]_lock" />'+
                '<label for="mp1_[ID]_lock">' + ml('lock this layer') + '</label>'+
                '</li>'+
              '</ol>'+
            '</div>' +
          '</div>' + /*close layer left */
        '</div>' + /*close layer container */
      '</div>' +
        
	  '</div>' +
      '<div id="mp1_[ID]_edit_text_container" class="edit_text_container" style="display: none;">' +
        '<textarea id="mp1_[ID]_edit_text" rows="5" cols="55" class="mp_textarea"></textarea>' +
        /*'<img src="[PATH_PREFIX]/images/mp/edit_text.gif" id="mp1_[ID]_save_edit"/>'+*/
        '<input type="button" class="button" value="' + ml('change text') + '" id="mp1_[ID]_save_edit" />'+
        /*'<img src="[PATH_PREFIX]/images/mp/cancel_text.gif" id="mp1_[ID]_cancel_edit"/>' +*/
        '<a href="#" class="cancel_button" id="mp1_[ID]_cancel_edit">' + ml('cancel') + '</a>'+
      '</div>' +
    '</div>';

      
var type_min = '<div id="mp0_[ID]" style="display: none; margin-top: 2px; width: 90px; position: relative;" class="managepaneoff2" stopdeselect="true">' +
        '<table class="managepane_header" cellpadding="0" cellspacing="2" border="0" width="90" id="mp0_[ID]_bar" style="cursor:pointer;">' +
          '<tr>' +
            '<td id="mp0_[ID]_ico_c" style="white-space:nowrap;"><span id="mp0_[ID]_ico_alert" class="alert_icon_none"></span><img id="mp0_[ID]_stimg"/></td>' +
            '<td width="100%"><span class="managepane_title" id="mp0_[ID]_title"></span></td>' +
            '<td align="right" valign="top"><table cellpadding="0" cellspacing="0" border="0"><tr><img src="[PATH_PREFIX]/images/mp/move_up_on.gif" id="mp0_[ID]_zup"/></td></tr><tr><img src="[PATH_PREFIX]/images/mp/move_down_on.gif" id="mp0_[ID]_zdown"/></td></tr></table></td>' +
            '<td align="right" valign="top"><img src="[PATH_PREFIX]/images/mp/close.gif" id="mp0_[ID]_remove"/></td>' +
          '</tr>' + 
        '</table>' +
    '</div>';
      
var types = [
  type_0,
  type_1,
  type_1,
  type_3
];


var Cart = Class.create({
    CLASSDEF: {
        name: 'Cart'
    },
  
  initialize: function Cart_initialize(options) {
    this.products = [];
    this.productsById = new Hash();
    this.extras = [];
    this.lineItems = [];
    this.credits = [];
    //this.productsByUid = new Hash();
    this.derivedProductsByLinkId = {};
    this.id = -1;
    this.digitizedAssets = {};
    this.applyTax = true;
    this.usedClientIds = [];
    this.couponDiscount = 0;
    this.coupon = null;
    
    // options only passed in order management system
    if (options) {
      this.id = options.id;
      this.is_testing = options.is_testing;
      this.applyTax = options.apply_tax;
      this.setTaxInfo(options.taxes.rate, options.taxes.names);
      if (options.final_discount) {
        this.finalDiscountLocked = true;
        this.finalDiscount = parseFloat(options.final_discount);
      } else {
        this.finalDiscountLocked = false;
        this.finalDiscount = 0;
      }
      this.legacyFinalDiscount = options.final_discount;
      this.finalDiscountType = options.final_discount_type;
      
      if (options.overriding_final_total) {
        this.setFinalTotal(parseFloat(options.overriding_final_total), true);
      } else {
        this.finalTotalLocked = false;
      }
      
      this.shippingTotal = options.shipping.total;
      this.shippingMethod = options.shipping.method_name;
      this.shippingMethodId = options.shipping.id;
      this.shippingOverride = options.shipping.override;
      this.updateShippingInfo(options.shipping.info, false, true);
      
      if (options.coupon) {
        this.couponDiscount = options.coupon_discount;
        this.coupon = new Coupon(options.coupon);
      }
      
      if(options.gc_amount) {
        this.gcAmount = options.gc_amount;
        this.gcCodes = options.gc_codes;
      } else {
        this.gcAmount = 0.0;
        this.gcCodes = "";
      }
      
      var products = options.products;
      if (products) {
        for (var pid in products) {
          if (!d.productsById[pid]) {
            log('Adding product ' + pid);
            var p = new Product(pid, products[pid]);
            d.addProduct(p);
          }
        }
      }
      var configuredProducts = options.configured_products;
      if (configuredProducts) {
        for (var cpid in configuredProducts) {
          log('Adding configured product ' + cpid);
          var cp = new ConfiguredProduct(cpid, configuredProducts[cpid]);
          this.add(cp);
        }
        this.initDigitizationStatusForProducts();
      }
    } else {
      this.taxRate = null;
      this.taxNames = '';
      this.finalDiscount = 0;
      this.shippingTotal = 0;
      this.shippingMethod = '';
      this.shippingMethodId = null;
      this.shippingOverride = false;
      this.gcAmount = 0.0;
      this.gcCodes = "";
    }
  },
  
  setTaxInfo: function Cart_setTaxInfo(rate, names) {
    this.taxRate = rate;
    this.taxNames = names;
    d.tMod = this.taxRate;
  },
  
  updateShippingInfo: function Cart_updateShippingInfo(info, initMethod, keepLegacy) {
    this.shippingInfo = info;
    this.shippingDiscontinued = true;
    var self = this;
    if (this.shippingInfo && hashSize(this.shippingInfo) > 0) {
      $H(this.shippingInfo).each(function(pair) {
          var id = pair.key;
          var meth = pair.value;
          if (initMethod && self.shippingMethodId == null) {
            self.shippingMethodId = id;
            self.shippingMethod = meth.name;
            self.shippingTotal = meth.cost;
          }
          if (self.shippingMethodId == id) {
            self.shippingDiscontinued = false;
            if (keepLegacy && self.shippingTotal != meth.cost) {
              self.shippingOverride = true;
            } else if (!self.shippingOverride) self.shippingTotal = meth.cost;
            /*if (self.shippingTotal != meth.cost) {
              self.shippingOverride = true;
            }*/
          }
      });
    } else {
      self.shippingMethodId = null;
      self.shippingMethod = "No shipping method available";
      if (!self.shippingOverride) self.shippingTotal = 0;
      self.shippingDiscontinued = false;
    }
  },
  
  isShippingOK: function Cart_isShippingOK() {
    var allDigital = this.isAllDigital();
    if ((allDigital && this.shippingMethodId == null) || (!allDigital && this.shippingMethodId != null)) {
      return true;
    } else {
      return false;
    }
  },
  
  isAllDigital: function Cart_isAllDigital() {
    var allDigital = true;
    this.products.each(function(p) {
        if (p.product) allDigital = false;
    });
    return allDigital;
  },
  
  // called in order management system, carts are generated locally
  initDesignerCart: function Cart_initDesignerForOrder() {
    var cartBody = $("cart_body");
    
    cartBody.update("");
    this.products.each(function(product) {
        new Insertion.Top(cartBody, product.cartRowHtml());
    });
  },
  
  initDigitizationStatusForProducts: function Cart_initDigitizationStatusForProducts() {
    var self = this;
    $H(this.digitizedAssets).each(function(pair) {
        var status = pair.value.product.status;
        var cpIds = pair.value.product.for_cp_ids;
        cpIds.each(function(cpid) {
            var p = self.productsById[cpid];
            if (p) p.digStatus = status;
        });
    });
  },
  
  add: function Cart_add(product) {
    if(product.cp_type == CP_DIGITIZATION) {
      var assetDef = d.assets[product.asset_id];
      if(assetDef == null) {
        log("ERROR: unable to get asset " + product.asset_id + " when adding digitization cart item");
        return;
      }
      this.getDigitization(assetDef).product = product;
      this.derivedProductsByLinkId[product.asset_id] = product;
      this.extras.push(product);
    } else if (product.product || product.cp_type == CP_NEW) {
      this.lineItems.push(product);
    } else if (product.cp_type == CP_NEW_EXTRA || product.cp_type == CP_DIGITIZATION || product.cp_type == CP_EXTRA_CHARGE) {
      this.extras.push(product);
    } else if (product.cp_type == CP_CREDIT) {
      this.credits.push(product);
    }
    
    this.usedClientIds.push(product.clientId);
    
    this.products.push(product);
    if (product.id) this.productsById[product.id] = product;
  },
  
  hasLineItems: function Cart_hasLineItems() {
    for(var i=0; i<this.lineItems.size(); i++) {
      var p = this.lineItems[i];
      if (p.cp_type != CP_NEW && !p.deleted) return true;
    }
    return false;
  },
  
  recalcItemPosition: function Cart_recalcItemPosition() {
    var pos = 1;
    this.products.each(function(p) {
        if (p.product || p.cp_type == CP_NEW || p.cp_type == CP_GIFT_CERTIFICATE) {
          p.pos = pos;
          pos += 1;
        }
    });
  },
  
  getItemPosition: function Cart_getItemPosition(id) {
    var p = this.getProduct(id);
    if (!p.pos) this.recalcItemPosition();
    return (!p.pos) ? -1 : p.pos;
  },
  
  getProduct: function Cart_getProduct(id) {
    return this.productsById[id];
  },
  
  setValidProducts: function Cart_setValidProducts(ids) {
    var validProducts = [];
    var validProductsById = {};
    for(var i=0; i < ids.length; i++) {
      var pId = ids[i];
      validProducts.push(this.productsById[pId]);
      validProductsById[pId] = this.productsById[pId];
    }
    this.products = validProducts;
    this.productsById = validProductsById;
  },
  
  isEditable: function Cart_isEditable() {
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.product && p.editable == false) {
        return false;
      }
    }
    return true;
  },
  
  getFirstEditableProduct: function Cart_getFirstEditableProduct() {
    for(var i=0; i < this.products.length;i++) {
      if((this.products[i].addedToCart)&&(this.products[i].cp_type!=CP_DIGITIZATION)&&(this.products[i].cp_type!=CP_CREDIT)) {
        return this.products[i];
      }
    }
    return null;
  },
  
  getUnsavedProduct: function Cart_getUnsavedProduct() {
    for(var i=0; i < this.products.length;i++) {
      if((!this.products[i].addedToCart)&&(this.products[i].cp_type!=CP_DIGITIZATION)&&(this.products[i].cp_type!=CP_CREDIT)) {
        return this.products[i];
      }
    }
    return null;
  },
  
  copyProduct: function Cart_copyProduct(id) {
    var self = this;
    if((typeof(dontWarnBeforeLeaving) != 'undefined')&&(dontWarnBeforeLeaving == false)) { //current item is dirty...
      msgBox(ml("Save Changes?"), ml("The currently loaded item has unsaved changes. Do you want to save the item before copying?"), null, ml("Yes"), ml("No"), ml("Cancel"), function(result) {
          if(result == 0) { //yes
            d.currentCProduct.save(false, false, function() {
                self.copyProduct(id);
            });
          } else if(result == 1) { //no
            dontWarnBeforeLeaving = true;
            if(d.currentCProduct.addedToCart) {
              d.currentCProduct.rollBack();
            }
            self.copyProduct(id);
          } else {
            //cancel...
          }
      });
      return;
    }
    
    
    var product = this.productsById[id];
    var cp = this.getUnsavedProduct();
    if(cp != null) {
      cp.copy(product);
      cp.save(true, false);
    } else {
      
      var asyncKey = asyncStart("designer_container");
      var self = this;
      var t2 = new Ajax.Request(d.pathPrefix + "/designer/new_product?id=" + product.product.id, {asynchronous:true, evalScripts:true, onComplete: function D_onComplete(response) {
          asyncFinish(asyncKey);
          cp = self.getUnsavedProduct();
          if(cp != null) {
            self.copyProduct(id);
          } else {
            alert("ERROR: No new item was added");
          }
        }
       });
    }
  },
  
  removeProductAlt: function Cart_removeProductAlt(product, avoidConfirm) {
    if (typeof(product) == "number") product = this.productsById[product];
    if((avoidConfirm == true) || (confirm(ml("Are you sure you want to remove this product?")))) {
      if (product.id) this.productsById[product.id] = null;
      var idx = this.products.indexOf(product);
      if(idx != null) {
        this.products[idx] = null;
        this.products = this.products.compact();
      }
      if(product.cp_type == CP_CREDIT) {
        idx = this.credits.indexOf(product);
        if(idx != null) {
          this.credits[idx] = null;
          this.credits = this.credits.compact();
        }
      }
      if((product.cp_type == CP_DIGITIZATION)&&(product.asset_id != null)) {
        delete this.derivedProductsByLinkId[product.asset_id];
      }
      
     /* var cartRow = $("cart_" + id);
      var cartOptRow=$("cart_options_"+id);
      if(cartRow != null) {
        cartRow.remove();
        cartOptRow.remove();
      }
      */
      
      // remove associated items from extra charges
      if (product.cp_type == CP_DECORATED_PRODUCT) {
        this.products.each(function(p) {
            if (p.cp_type == CP_EXTRA_CHARGE) {
              var index = p.associatedItems.indexOf(product.id);
              if (index != -1) {
                p.associatedItems.splice(index, 1);
              }
            }
        });
      }
      
      product.remove();
      product.deleted = true;
    }
  },
  
  removeProduct: function Cart_removeProduct(id) {
    if(confirm(ml("Are you sure you want to remove this product?"))) {
      var product = this.productsById[id];
      this.productsById[id] = null;
      var idx = this.products.indexOf(product);
      if(idx != null) {
        this.products[idx] = null;
        this.products = this.products.compact();
      }
      var cartRow = $("cart_" + id);
      var cartOptRow=$("cart_options_"+id);
      if(cartRow != null) {
        cartRow.parentNode.removeChild(cartRow);
        cartOptRow.parentNode.removeChild(cartOptRow);
      }
      var aKey = asyncStart("m_cart_pane");
      var self = this;
      var t1 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/remove_product?from=designer&id=" + id), {asynchronous:true, evalScripts:true, 
      onComplete: function Cart_onComplete() { 
        d.notifyCartChanged();
        asyncFinish(aKey); 
        if(d.currentCProduct.id == id) { //we deleted the currently selected product....
          //why was below code existing? it would stop us deselecting the view/area/item....
          //d.currentCView = null;
          //d.currentCViewArea = null;
          var firstProduct = self.getFirstEditableProduct();
          if(firstProduct == null) {
            d.startNewCartItem(d.currentCProduct.product.id, true);
            $("m_cart").className="unselected_tab_hidden";
          } else {    
            d.selectConfiguredProduct(firstProduct.id, true, false);
          }
        /*
        why below? shouldnt i leave be if i didnt delete current product?
        } else if((self.products.length == 0) || (self.products.length==1 && self.getUnsavedProduct() != null)) {
          d.startNewCartItem(d.currentCProduct.product.id, true);
          $("m_cart").className="unselected_tab_hidden";*/
        }
        product.remove();
        self.updateCartPrice();
      
      }});
      
      
      //delete this.allProducts[product.id];
    }
  },
  
  updateCartPrice: function Cart_updateCartPrice(doItems) {
    log("updateCartPrice");
    var total = 0;
    var count = 0;
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.addedToCart && p.cp_type != CP_CREDIT) {
        total += p.getPrice();
        count++;
        if(doItems) {
          p.updateCartPrice(false);
        }
      }
    }
    if(this.tpEl==null) {
      this.tpEl = $("total_price");
    }
    if(this.tpCEL==null) {
      this.tpCEL = $("cart_total_container");
    }
    log("total price=" + total);
    if(this.tpEl!=null) {
      this.tpEl.innerHTML = d.formatPrice(total);
    }
    if(this.tpCEL!=null) {
      if(count <= 1) {
        this.tpCEL.style.display="none";
      } else {
        this.tpCEL.style.display="";
      }
    }
    
    this.total = total;
    log("updateCartPrice Done");
  },
  
  recalcPrices: function Cart_recalcPrices() {
    log("calcPrices");
    if (!this.taxRate) this.taxRate = d.tMod;
    var total = 0, totalWholesale = 0, totalCost = 0, totalCredit = 0;
    var tax = 0, couponDiscount = 0;
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.addedToCart) {
        p.getPrice();
        totalWholesale += p.pricing.wsCost * p.qty;
        totalCost = p.pricing.cost * p.qty;
        if (p.cp_type == CP_CREDIT) {
          totalCredit -= p.finalTotal;
        } else {
          total += p.finalTotal;
          if (!p.product || !p.product.taxExempt) {
            tax += p.finalTotal * (this.taxRate-1.0);
          }
        }
      }
    }
    if (orderManager.applyShippingTax) tax += this.shippingTotal * (this.taxRate-1.0);
    this.couponDiscount = (this.coupon) ? this.coupon.getDiscountAmount(this) : 0;
    this.total = total.round();
    this.totalWholesale = totalWholesale.round();
    this.commission = this.total - this.totalWholesale;
    this.totalCost = totalCost.round();
    this.totalCredit = totalCredit.round();
    if (this.commission < 0) this.commission = 0;
    this.taxPrice = tax.round();
    tax = (this.applyTax) ? this.taxPrice : 0;
    this.calcFinalTotal = (tax + this.total + this.shippingTotal - this.couponDiscount - this.gcAmount).round();
    this.finalTotal = (this.finalTotalLocked) ? this.overridingFinalTotal.round() : (tax + this.total - this.totalCredit + this.shippingTotal - this.couponDiscount - this.finalDiscount - this.gcAmount).round();
  },
  
  setFinalTotal: function Cart_setFinalTotal(price, lock) {
    this.overridingFinalTotal = price;
    this.finalTotalLocked = lock;
    this.setFinalDiscount(AMOUNT_TYPE_PERCENT, null, this.finalDiscountLocked);
  },
  
  setFinalDiscount: function Cart_setFinalDiscount(discountType, discount, locked) {
    var finalDiscountAlt;
    this.finalDiscountType = discountType;
    if (discount == null) {
      finalDiscountAlt = this.legacyFinalDiscount;
    } else {
      finalDiscountAlt = discount;
    }
    if (discountType == AMOUNT_TYPE_PERCENT) {
      finalDiscountAlt = d.round(finalDiscountAlt / 100);
    }
    this.finalDiscountLocked = locked;
    
    if (this.finalTotalLocked) {
      this.finalDiscount = this.calcFinalTotal - this.overridingFinalTotal;
      this.finalTotalLocked = false;
      this.finalDiscountLocked = true;
    } else if (this.finalDiscountLocked) {
      if (this.finalDiscountType == AMOUNT_TYPE_PERCENT) {
        this.finalDiscount = this.calcFinalTotal * finalDiscountAlt;
      } else {
        this.finalDiscount = finalDiscountAlt;
      }
    } else {
      this.finalDiscount = this.legacyFinalDiscount;
    }
  },
  
  getFinalDiscount: function Cart_getFinalDiscount(discountType) {
    if (this.finalTotalLocked) {
      this.finalDiscount = this.calcFinalTotal - this.overridingFinalTotal;
    }
    var disc;
    if (discountType === null || typeof(discountType) == 'undefined') discountType = this.finalDiscountType;
    if (discountType == AMOUNT_TYPE_PERCENT) {
      disc = d.round(this.finalDiscount / this.calcFinalTotal * 100.0);
    } else {
      disc = d.round(this.finalDiscount);
    }
    if (isNaN(disc)) disc = 0;
    return disc;
  },
  
  selectCartItem: function Cart_selectCartItem(cProduct) {
    if((d.mode != DESIGNER_MODE_CONFIGURE)&&(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      for(var i=0; i < this.products.length;i++) {
        var p = this.products[i];
        if(p.addedToCart && p.cp_type != CP_GIFT_CERTIFICATE && p.cp_type != CP_EXTRA_CHARGE && p.cp_type != CP_CREDIT) {
          if(p == cProduct) {
            $("cart_" + p.id).className="cp_details selected";
            if(d.mode != DESIGNER_MODE_AMEND) {
              $("cart_options_" + p.id).className="cp_options selected";
            }
          } else {
            var el = $("cart_" + p.id);
            if(el != null) el.className="cp_details";
            if(d.mode != DESIGNER_MODE_AMEND) {
              el = $("cart_options_" + p.id);
              if(el != null) el.className="cp_options";
            }
          }
        }
      }
    }
  },

  //the state has been restored of a configured product (cancel edit).. lets rebuild the digitization state from the configured products....
  rollbackProduct: function(configuredProduct, opts) {
    this.digitizedAssets = {};
    for(var i=0; i < this.products.length; i++) {
      var p = this.products[i];
      if(p == configuredProduct) {
        p.doRollBack(opts);
      } else if(p.addedToCart) {
        p.restoreSharedState();
      }
    }
  },

  registerDigitizedAsset: function(item) {
    var digDef = this.getDigitization(item.asset);
    digDef.addItem(item);
    return digDef;
  },
  
  getDigitization: function(asset) {
    var digDef = null;
    if(this.digitizedAssets[asset.id] == null) {
      digDef = new DigitizedAsset(asset);
      this.digitizedAssets[asset.id] = digDef;
    } else {
      digDef = this.digitizedAssets[asset.id];
    }
    if(digDef.product == null) {
      digDef.product = this.derivedProductsByLinkId[asset.id]; 
    }
    return digDef;
  },
  
  getNextClientId: function() {
    if (!this.nextClientId) this.nextClientId = -2;
    while (this.usedClientIds.indexOf(this.nextClientId) != -1) {
      this.nextClientId --;
    }
    return this.nextClientId;
  },
  
  //check if there are any digitizedAssets and recalc prices/create on the fly....
  updateDerivedProducts: function() {
    for(var k in this.digitizedAssets) {
      var da = this.digitizedAssets[k];
      var primary = da.primaryItem();
      if(primary != null) {
        if(da.product == null) { //bingo...
          var digProd = new ConfiguredProduct(this.getNextClientId(), null, CP_DIGITIZATION);
          digProd.asset_id = da.asset.id;
          var associatedItems = [];
          da.items.list.each(function(item) {
              associatedItems.push(item.item.cView.configuredProduct.id);
          });
          digProd.for_cp_ids = associatedItems;
          digProd.associatedItems = associatedItems;
          digProd.status = "Not Purchased";
          digProd.price = primary.digitizationFee;
          digProd.rp = primary.digitizationFee;
          digProd.overridingPrice = null;
          digProd.isAutoPrice = true;
          digProd.legacyPrice = null;
          digProd.autoDerived = true;
          digProd.addedToCart = true;
          this.add(digProd);
          da.product = digProd;
        } else {
          da.product.price = primary.digitizationFee; //keep the auto price up-to-date
          
          // update associated items
          var associatedItems = [];
          da.items.list.each(function(item) {
              var cpid = item.item.cView.configuredProduct.id;
              associatedItems.push(cpid);
              if (da.product.associatedItems.indexOf(cpid) == -1) {
                da.product.associatedItems.push(cpid);
              }
          });
          da.product.for_cp_ids = associatedItems;
        }
      }
    }
  }
});


var DigitizedAsset = Class.create({
  CLASSDEF: {
      name: 'DigitizedAsset'
  },
  
  initialize: function DigitizedAsset_initialize(asset) {
    this.asset = asset;
    this.items = new MapList(this);
    this.product = null;
  },
  
  addItem: function DigitizedAsset_addItem(item) {
    this.items.add(new DigitizedAssetItem(item));
    this.items.resort();
    //if(this.configuredProducts[item.cView.configuredProduct.id] == null) {
     // this.configuredProducts[item.cView.configuredProduct.id] = {};
    //}
    //this.configuredProducts[item.cView.configuredProduct.id][item.id] = item;
    if(this.items.list.length == 2) {
      this.changed(item, DIG_EVENT_PRIMARY); //let the other item know it has a sibling...
      //this.items.list[0].item.refreshDigitizedAsset(DIG_EVENT_PRIMARY); 
    }
  },
  
  removeItem: function DigitizedAsset_removeItem(toDel) {
    this.items.remove(toDel.id);
    if(this.items.list.length == 0) { //last item...
      var digA = d.cart.digitizedAssets[this.asset.id];
      delete d.cart.digitizedAssets[this.asset.id];
      //remove the cart item
      if(d.inOrderManager) {
        if((digA != null)&&(digA.product != null)) {
          d.cart.removeProductAlt(digA.product, true);
        }
      }
      //save product will do this....
      /*if(this.product != null) {
        var cartRow = $("cart_" + this.product.id);
        if(cartRow != null) {
          cartRow.parentNode.removeChild(cartRow);
        }
      }
      */
    } else {
      for(var i=0; i < this.items.list.length; i++) {
        var item = this.items.list[i];
        item.item.refreshDigitizedAsset(DIG_EVENT_PRIMARY);
      }
    }
  },
  
  //the first use is the primary item...
  primaryItem: function DigitizedAsset_primaryItem() {
    if(this.items.list.length == 0) {
      return null;
    } else {
      return this.items.list[0].item;
    }
  },
  
  changed: function DigitizedAsset_changed(src, event) {
    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      if(item.item != src) {
        item.item.refreshDigitizedAsset(event);
      }
    }
  }
  
});

var DigitizedAssetItem = Class.create({
  CLASSDEF: {
      name: 'DigitizedAssetItem'
  },
  
  initialize: function DigitizedAssetItem_initialize(item) {
    this.id = item.id;
    this.item = item;
    this.sort_key = [
      item.cView.configuredProduct.cartIndex,
      item.cView.productView.viewIndex,
      item.cViewArea.productArea.areaIndex,
      item.id
    ];
  },
  
  //sort the items by cart order, view order, area order, then order added
  compare: function(other) {
    for(var i=0; i < 4; i++) {
      if(this.sort_key[i] > other.sort_key[i]) {
        return 1;
      } else if(this.sort_key[i] < other.sort_key[i]) {
        return -1;
      }
    }
    return 0;
  }
});


var ConfiguredProduct = Class.create({
  CLASSDEF: {
      name: 'ConfiguredProduct'
  },
  
  initialize: function CP_initialize(id, options, type, useLegacy) {
    if (id) {
      this.id = id;
    } else {
      this.id = nextConfiguredProductId;
      nextConfiguredProductId --;
    }
    if (id < 0) this.clientId = id;
    
    
    this.isAutoPrice = true;
    this.discountType = AMOUNT_TYPE_PERCENT;
    this.totalLocked = false;
    this.discountLocked = false;
    this.editable = true;
    
    if (options) {
      this.okToSave = (options.ots != null) ? options.ots : true;
      
      if(options.tot != null) { //overriding total..
        this.totalLocked = true;
        this.overridingTotal = options.tot;
      }
      /*
      if(options.dt != null) { //discount type used... must be overriden.,.
        this.discountLocked = true;
        this.discountType = parseInt(options.dt, 10);
        this.typedOverridingDiscount = parseFloat(options.odis);
      }
      */
      if (options.iad && options.ldk) {
        this.discountLocked = false;
        this.legacyDiscount = parseFloat(options.odis);
        this.legacyDiscountKey = options.ldk;
      } else if (!options.iad) {
        this.discountLocked = true;
        this.discountType = parseInt(options.dt);
        this.typedOverridingDiscount = parseFloat(options.odis);
      }
      
      //this.uid = nextConfiguredProductId;
      //nextConfiguredProductId ++;
      
      if (!this.clientId) this.clientId = options.cid;
      if(this.cartIndex == null) this.cartIndex = d.cart.products.length;
      this.options = options;
      this.cp_type = options.cp_type;
      if(options.q) {
        this.qty = parseInt(options.q, 10);
      } else {
        this.qty = 1;
      }
      if(this.options.lv) {
        this.layout_version = parseInt(this.options.lv, 10);
      } else {
        this.layout_version = 0;
      }
      this.discount = parseFloat(options.dis);
      this.overridingDiscount = this.discount;
      
      if (options.assets) {
        var self = this;
        options.assets.each(function(as) {
            d.addAsset(as.evalJSON());
        });
      }
      
      this.imageURL = options.iu;
      this.largeImageURL = options.iu2;
      
      this.processedBy = options.proc_by;
      this.processedDate = options.proc_date;
      this.shippedBy = options.ship_by;
      this.shippedDate = options.ship_date;
      
      this.views = {};
      this.product = d.productsById[options.p];
      this.nextItemId = 1; //this id is unique per configured product.... allows us to cache stuff on the server....
      this.brandId = options.b;
      if (options.markup) {
        this.markup = options.markup;
        this.markupMode = options.markup.mode;
        this.markupAmount = options.markup.amount;
      } else {
        this.markupMode = d.brandMarkups[this.brandId].mode;
        this.markupAmount = d.brandMarkups[this.brandId].amount;
      }
      
      if ((!this.markupMode && this.markupMode != 0) || (!this.markupAmount && this.markupAmount != 0)){
        log("No markup details for CP: "+this.id+" with brand of "+this.brandId+": "+d.brandMarkups[this.brandId]);
      }else{
        log("set markup details for CP: "+this.id+" with brand of "+this.brandId+": "+this.markupMode+", "+this.markupAmount);
      }
      
      this.renderVersion = (options.lv == null) ? 0 : options.lv;
      this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
      this.reRenderChild = false; //track if children have made changes requiring rerendering
      this.price = null;
      this.addedToCart = options.added;
      
      this.overridingBasePrice = options.obp;
      this.isAutoBasePrice = (options.iabp == null) ? true : options.iabp;
      this.legacyBasePrice = (!this.isAutoBasePrice) ? null : options.obp;
      this.isAutoSizePrice = (options.iasp == null) ? true : options.iasp;
      
      if(this.cp_type == CP_DIGITIZATION) {
        this.asset_id = options.asset_id;
        this.price = options.price;
        this.for_cp_ids = (options.for_cp_ids) ? options.for_cp_ids : [];
        this.associatedItems = (options.associated_items) ? options.associated_items : [];
        var self = this;
        this.for_cp_ids.each(function(cpid) {
            if (self.associatedItems.indexOf(cpid) == -1) {
              self.associatedItems.push(cpid);
            }
        });
        this.status = options.status;
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
        //this.legacyPrice = options.op;
        this.rp = options.rp;
  //      this.primaryItemId = options.cpa;
      } else if (this.cp_type == CP_EXTRA_CHARGE) {
        this.name = options.n;
        this.rrp = options.rrp;
        this.price = options.price;
        this.associatedItems = (options.associated_items) ? options.associated_items : [];
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
        //this.legacyPrice = options.op;
        this.extraCharge = orderManager.extraChargesById[options.eid];
      } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
        this.price = options.price;
        this.name = options.n;
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
        this.legacyPrice = options.op;
      } else if (this.cp_type == CP_CREDIT) {
        this.rrp = options.rrp;
        this.price = options.price;
        this.isAutoPrice = true;
        this.discountLocked = true;
        this.discountType = AMOUNT_TYPE_PERCENT;
        this.typedOverridingDiscount = 0;
        this.dateModified = options.dm;
      } else {
        this.legacyUnitPrice = options.up;
        this.legacyTotal = options.price;
        this.usingTeamnames = (options.utn==true);
        if(this.usingTeamnames) {
          if(options.tn != null && options.tn.length > 0) {
            this.getTeamNames().load(options.tn);
            this.usingTeamnames = true;
          } else {
            this.usingTeamnames = false;
          }
        }
        
        
        
        this.initFields(); //init the field objects
    
        this.usesDecorationLibrary = null; // start as null, as we don't know yet, could be true or false    
    
        
        
        
        //load fields
        for(var i=0; i < options.f.length; i++) {
          var fieldData = options.f[i];
          var cField = this.cFields[fieldData.id];
          if(cField != null) {
            for(var j=0; j < fieldData.opts.length; j++) {
              var optData = fieldData.opts[j];
              var cOpt = cField.addSelectedOption(optData.id, optData.qty, null, optData.up, optData.iap, optData.lk);
              if(cOpt != null) {
                for(var k=0; k < optData.sub.length; k++) {
                  var subData = optData.sub[k];
                  cOpt.addSelectedOption(subData.id, subData.qty, subData.up, subData.iap, optData.lk);
                }
              }
            }
          } else {
            log("Dropping Configured Field " + fieldData.id);
          }
          
        }
        
        this.initFieldDefaults();
        
        var views = options.v;
        
        this.customProductId = options.cp;
        if(this.customProductId != null) {
          this.customProduct = d.customProducts[this.customProductId];
          if(views.length == 0 && this.customProduct) { //no views defined in configured product... use the views defined in the custom product....
            log("Loading views from custom product definition");
            views = this.customProduct.viewOptions;
          }
        }
        for(var i=0; i < views.length;i++) {
          var view = views[i];
          var productView = this.product.views.byId[view.id];
          if(productView==null) {
            log("Error loading custom product view: Product does not have view " + view.id);
          } else {
            log("init view:" + productView.id);
            this.views[productView.id] = new ConfiguredView(this, productView, view);
          }
        }
        if(options.def_proc == null && options.def_proc != -1) {
          if((this.customProduct != null)&&(this.customProduct.defaultProcessId != null)) {
            this.defaultProcessId = this.customProduct.defaultProcessId;
          } else if(d.defaultProcess == null) {
            this.defaultProcessId = this.product.getFirstProcessId(); 
          } else {
            this.defaultProcessId = d.defaultProcess.id;
          }
        } else {
          this.defaultProcessId = options.def_proc;
        }
        
        
        
        this.selectedColorId = options.color;
        if((this.selectedColorId == null) && (this.usingCustomProduct())) {
          this.selectedColorId = this.customProduct.defaultColor;
        }
        
        if ((useLegacy || d.useLegacyPrices) && this.legacyTotal != null && this.getPrice().round() != this.legacyTotal) {
          this.editable = false;
        }
      }
    } else {
      this.cp_type = (type) ? type : CP_NEW;
      
      if(this.cartIndex == null) this.cartIndex = orderManager.currentOrder.cart.products.length;
      this.options = null;
      this.okToSave = false;
      this.qty = 1;
      this.layout_version = 0;
      this.discount = 0;
      
      this.processedBy = null;
      this.processedDate = null;
      this.shippedBy = null;
      this.shippedDate = null;
      
      this.views = {};
      this.nextItemId = 1; //this id is unique per configured product.... allows us to cache stuff on the server....
      this.brandId = d.brandId;
      this.markupMode = d.brandMarkups[this.brandId].mode;
      this.markupAmount = d.brandMarkups[this.brandId].amount;
      
      this.renderVersion = 0;
      this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
      this.reRenderChild = false; //track if children have made changes requiring rerendering
      this.price = null;
      this.addedToCart = false;
      this.usingTeamnames = false;
      this.usesDecorationLibrary = null; // start as null, as we don't know yet, could be true or false    
      this.customProductId = null;
      this.selectedColorId = null;
      this.associatedItems = [];
      
      this.isAutoBasePrice = true;
      this.legacyBasePrice = null;
      
      this.isAutoSizePrice = true;
    }
    
    if (this.defaultProcessId == null) this.defaultProcessId = d.defaultProcessId;
    
    this.legacyPriceChanges = [];
  },
  
  isPreDecProduct: function CP_isPreDecProduct() {
    return (this.customProductId);
  },
  
  isBlankProduct: function CP_isBlankProduct() {
    return (this.cp_type == CP_DECORATED_PRODUCT && !this.customProductId);
  },
  
  isNonDecProduct: function CP_isNonDecProduct() {
    return (this.cp_type == CP_NON_DEC);
  },
  
  saveCurrentStatus: function CP_saveCurrentStatus() {
    this.options = this.serializeToOptions();
  },
  
  serializeToOptions: function CP_serializeToOptions() {
    var opts = {};
    if (this.totalLocked) {
      opts.tot = this.overridingTotal;
    }
    if (this.discountLocked) {
      opts.dt = this.discountType;
      opts.odis = this.typedOverridingDiscount;
    }
    opts.ots = this.okToSave;
    opts.cid = this.clientId;
    opts.cp_type = this.cp_type;
    opts.q = this.qty;
    opts.lv = this.layout_version;
    opts.dis = this.discount;
    opts.iu = this.imageURL;
    opts.iu2 = this.largeImageURL;
    opts.proc_by = this.processedBy;
    opts.proc_date = this.processedDate;
    opts.ship_by = this.shippedBy;
    opts.ship_date = this.shippedDate;
    opts.p = (this.product) ? this.product.id : null;
    opts.b = this.brandId;
    opts.added = this.addedToCart;
    opts.obp = this.overridingBasePrice;
    opts.iabp = this.isAutoBasePrice;
    opts.iasp = this.isAutoSizePrice;
    opts.markup = this.markup;
    
    if (this.cp_type == CP_NEW || this.cp_type == CP_NEW_EXTRA) {
      
    } else if (this.cp_type == CP_DIGITIZATION) {
      opts.asset_id = this.asset_id;
      opts.price = this.price;
      opts.for_cp_ids = this.for_cp_ids;
      opts.status = this.status;
      opts.op = this.overridingPrice;
      opts.iap = this.isAutoPrice;
      opts.rrp = this.rrp;    
      opts.rp = this.price;
    } else if (this.cp_type == CP_EXTRA_CHARGE) {
      opts.n = this.name;
      opts.rrp = this.rrp;
      opts.price = this.price;
      opts.op = this.overridingPrice;
      opts.iap = this.isAutoPrice;
    } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
      opts.price = this.price;
      opts.n = this.name;
      opts.op = this.overridingPrice;
      opts.iap = this.isAutoPrice;
    } else {
      opts.utn = this.usingTeamnames;
      if(this.usingTeamnames) {
        opts.tn = [];
        this.getTeamNames().names.list.each(function(name) {
            opts.tn.push({
                o : name.optionId,
                s : name.subOptionId,
                n : name.names.names
            });
        });
      }
      
      opts.color = this.selectedColorId;
      opts.f = [];
      
      $H(this.cFields).each(function(pair) {
          var cField = pair.value;
          var opt = {
              id : cField.id,
              val : cField.fieldValue,
              opts : []
          };
          $H(cField.options).each(function(pair) {
              var cFieldOption = pair.value;
              var sopt = {
                id : cFieldOption.optionId,
                qty : cFieldOption.qty,
                up : cFieldOption.unitPrice,
                iap : cFieldOption.isAutoPrice,
                lk : cFieldOption.legacyKey,
                sub : []
              };
              $H(cFieldOption.subOptions).each(function(pair) {
                  var cFieldSubOption = pair.value;
                  sopt.sub.push({
                      id : cFieldSubOption.subOptionId,
                      qty : cFieldSubOption.qty,
                      up : cFieldSubOption.unitPrice,
                      iap : cFieldSubOption.isAutoPrice,
                      lk : cFieldSubOption.legacyKey
                  });
              });
              opt.opts.push(sopt);
          });
          opts.f.push(opt);
      });
      
      opts.cp = this.customProductId;
      
      opts.v = [];
      if (this.customProductId == null) {
        $H(this.views).each(function(pair) {
            var cView = pair.value;
            var vOpt = {
              id : cView.id,
              u : cView.startUrl,
              rv : cView.renderVersion,
              lrv : cView.loadedRenderVersion,
              a : []
            };
            $H(cView.areas).each(function(pair) {
                var cArea = pair.value;
                var aOpt = {
                  id : cArea.id,
                  rv : cArea.renderVersion,
                  c : {
                    bg : cArea.bgColor,
                    rv : cArea.renderVersion
                  },
                  p : [],
                  i : []
                };
                $H(cArea.processes).each(function(pair) {
                    var cProcess = pair.value;
                    var pOpt = {
                      p : cProcess.id,
                      odp : cProcess.overridingDecorationPrice,
                      ocp : cProcess.overridingClipartPrice,
                      uod : cProcess.usingOverridingDec,
                      uoc : cProcess.usingOverridingClipart,
                      lk : cProcess.legacyKey
                    };
                    aOpt.p.push(pOpt);
                    cProcess.items.list.each(function(item) {
                        aOpt.i.push({
                            id : item.id,
                            p : cProcess.id,
                            rv : item.renderVersion,
                            c : item.serializeToOptions()
                        });
                    });
                });
                $H(cArea.items).each()
                vOpt.a.push(aOpt);
            });
            opts.v.push(vOpt);
        });
      }
      
      opts.def_proc = this.defaultProcessId;
    }
    return opts;
  },
  
  toString: function CP_toString() {
    return this.getName();
  },
  
  getName: function CP_getName() {
    if (this.cp_type == CP_DIGITIZATION) {
      return "Digitizing Fee";
    } else if (this.cp_type == CP_EXTRA_CHARGE) {
      if (this.extraCharge) {
        return this.extraCharge.name;
      } else {
        return this.name;
      }
    } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
      return this.name;
    } else if (this.product) {
      return this.product.name;
    } else {
      return this.name;
    }
  },
  
  getDescription: function CP_getDescription() {
    if (this.cp_type == CP_DIGITIZATION) {
      return this.status;
    } else if (this.cp_type == CP_EXTRA_CHARGE) {
      return this.extraCharge.description;
    } else {
      return '';
    }
  },
  
  initFields: function CP_initFields() {
    this.cFields = {};
    //load from configured product options first
    var fieldOpts = (this.options) ? this.options.f : null;
    if(fieldOpts==null) fieldOpts = []; //this shouldnt happen but DNC-2317 says it must....
    for(var i=0; i < fieldOpts.length; i++) {
      var fieldOpt = fieldOpts[i];
      var field = this.product.type.fields.byId[fieldOpt.id];
      if(field != null) {
        var pField = this.product.fields.byId[field.id];
        if((pField!=null)&&(pField.used)) { //product may not support field....
          var cField = new ConfiguredField(field.id, this, fieldOpt);
          this.cFields[cField.id] = cField;
        }
      }
    }
    //now laod all fields not in the options...
    for(var i=0; i < this.product.type.fields.list.length; i++) {
      var field = this.product.type.fields.list[i];
      if((this.cFields[field.id] == null)&&(!field.discontinued)) {
        var pField = this.product.fields.byId[field.id];
        if((pField!=null)&&(pField.used)) { //product may not support field....
          var cField = new ConfiguredField(field.id, this, {uDef: true});
          this.cFields[cField.id] = cField;
        }
      }
    }
  },
  
  initFieldDefaults: function CP_initFieldDefaults() {
    for(var k in this.cFields) {
      this.cFields[k].initToDefault();
    }
  },
  
  getTeamNames: function() {
    if(this.teamNames == null) {
      this.teamNames = new TeamNames(this);
    }
    return this.teamNames;
  },
  
  //build the html displaying the views 
  updateViewListHtml: function CP_updateViewListHtml() {
    var colorId =  this.getSelectedColorId();
    /*var colorId = 0;
    if(color!=null) {
      colorId = color.productChosenOptionId;
    }*/
    
    var customProduct = (this.usingCustomProduct() && !this.isCustomised()) ? this.customProduct : null;
    if(customProduct != null) {
      log("Using Custom Product to updateViewListHtml");
    } else {
      log("NOT Using Custom Product to updateViewListHtml: this.usingCustomProduct()=" + this.usingCustomProduct() + " this.isCustomised()=" + this.isCustomised());
    }
    var state = { needServer: false };
    var html = '';
    for(var i=0;i < this.product.views.list.size(); i++) {
      var pView = this.product.views.list[i];
      html += pView.buildSelectorHtml(colorId, this.getView(pView.id, false), customProduct, state);
    }
    $("product_views").innerHTML = html;
    if(state.needServer) {
      this.callServerToRenderThumbnails(); 
    }
  },
  
  getView: function CP_getView(viewId, createIfNotFound) {
    var view = this.views[viewId];
    if((view == null) && (createIfNotFound==true)) {
      var productView = this.product.views.byId[viewId];
      if(productView==null) {
        log("No defined view " + viewId);
      }
      view = new ConfiguredView(this,productView, {});
      this.views[viewId] = view;
    }
    return view;
  },
  
  //given a ProductViewAreaProcess, build/get the ConfiguredViewProcess for it...
  getCProcess: function C_getCProcess(productViewAreaProcess) {
    var cView = this.getView(productViewAreaProcess.viewArea.view.id, true);
    var cArea = cView.getArea(productViewAreaProcess.viewArea.id, true);
    return cArea.processes[productViewAreaProcess.id];
  },
  
  getFirstSelectedViewId: function CP_getFirstSelectedViewId() {
    var firstViewId = null;
    for(var i=0;i < this.product.views.list.size(); i++) {
      var pView = this.product.views.list[i];
      var cView = this.getView(pView.id, false);
      if((cView != null)&&(cView.isUsed())) {
        return pView.id;
      }
      if((firstViewId == null)&&(pView.allowDesign)) {
        firstViewId = pView.id;
      }
    }
    return firstViewId;
  },
  
  
  getNextItemId: function CP_getNextItemId() {
    return d.getNextItemId(); //proxy to designer to make ids cart wide...
  },
  
  registerServerItemId: function CP_registerServerItemId(id) {
    return d.registerServerItemId(id); //proxy to designer to make ids cart wide...
  },
  
  //get the configured size field
  getSelectedSize: function() {
    if(this.product.type.sizeField != null) {
      return this.cFields[this.product.type.sizeField.id];
    }
    return null;
  },
  
  getSelectedColor: function CP_getSelectedColor() {
    var f = this.product.type.colorField;
    if(f != null) {
      if (this.selectedColorId!=null) {
        var c = this.product.colors.byId[this.selectedColorId];
        if(c != null) return c;
        //no color, try discontinued colors
        c = this.product.discontinuedColors.byId[this.selectedColorId];
        if(c != null) return c;
      }
      //no color selected... lets get the default....
      this.selectedColorId = this.product.defaultColor.id;
      return this.product.defaultColor;
      
    } else {
      log("product has no color field");
    }
    return null;
  },
  
  getSelectedColorId: function CP_getSelectedColorId() {
    var sc = this.getSelectedColor();
    if(sc == null) {
      log("product has no color");
      return null;
    }
    return sc.id;
  },
  
  getAvailableColorList: function CP_getAvailableColorList(unavailableColors) {
    var colors = null;
    
    if(this.usingCustomProduct()) {
      colors = this.customProduct.getColors();
    } else {
      colors = this.product.colors;
    }
    var sizeField = this.getSelectedSize();
    var result = [];
    if((this.product.limitSizeColors)&&(sizeField != null)) {
      //check that the colors are available for the selected size(s)
      for(var i=0;i < colors.list.length; i++) {
        var pColor = colors.list[i];
        var allAvailable = true;
        var conflictSizes = [];
        for(var sizeId in sizeField.options) {
          var sizeChoice = sizeField.options[sizeId];  
          if(!sizeChoice.def.isMulti) {
            if(sizeChoice.pDef.subs == null && sizeChoice.qty > 0) {
              var sizeColors = this.product.sizeColorCombinations[sizeId];
              if(sizeColors != null) { 
                if(sizeColors[0][pColor.productChosenOptionId] != true) {
                  allAvailable = false;
                  conflictSizes.push(sizeChoice.def.value);
                  log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId); 
                  break;
                }
              } else {
                log("Error: size " + sizeId + " has no scc entries");
              }
            } else if(sizeChoice.subQty > 0) {
              for(var subSizeId in sizeChoice.subOptions) {
                var subSizeChoice = sizeChoice.subOptions[subSizeId];  
                if(subSizeChoice.qty > 0) {
                  var sizeColors = this.product.sizeColorCombinations[sizeId];
                  if (sizeColors != null) {
                    if (sizeColors[subSizeId] != null) {
                      if(sizeColors[subSizeId][pColor.productChosenOptionId] != true) {
                        allAvailable = false;
                        conflictSizes.push(sizeChoice.def.value + "-" + subSizeChoice.def.value);
                        log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId + "." + subSizeId); 
                        break;
                      }
                    } else if (sizeColors[0] != null){
                      if(sizeColors[0][pColor.productChosenOptionId] != true) {
                        allAvailable = false;
                        conflictSizes.push(sizeChoice.def.value + "-" + subSizeChoice.def.value);
                        log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId + "." + subSizeId); 
                        break;
                      }
                    } else {
                      log("Error: size " + sizeId + "." + subSizeId + " has no scc entries");
                    }
                  } else {
                    log("Error: size " + sizeId + "." + subSizeId + " has no scc entries");
                  }
                }
              }
            }
          }
        }
        if(allAvailable) {
          result.push(pColor);
        } else if (unavailableColors) {
          unavailableColors.push({"color":pColor, "conflict_sizes":conflictSizes});
        }
      }
    } else {
      result = colors.list;
    }
    //check if amending.. only allow same color type so we cannot affect price...
    if(d.mode == DESIGNER_MODE_AMEND) {
      var curColorType = this.getSelectedColor().color_type;
      var result2 = [];
      for(var i=0; i < result.length; i++) {
        if(result[i].color_type == curColorType) {
          result2.push(result[i]);
        }
      }
      return result2;
    }
    
    return result;
  },
  /*
  we can now use the supplier_chosen_color_id to get images
  getSelectedProductColorId: function CP_getSelectedProductColorId() {
    var sc = this.getSelectedColor();
    if(sc == null) {
      log("product has no color");
      return null;
    }
    return sc.productChosenOptionId;
  },*/
  
  //the user has changed the color from the interface
  selectColor: function CP_selectColor(color) {
    var f = this.product.type.colorField;
    log("setting color to " + color.id);
    this.selectedColorId = color.id;
    this.setReRender();
    this.updateThumbnails();
    d.currentCView.setDesignerBackground();
    
    var sizeField = this.getSelectedSize();
    if((this.product.limitSizeColors)&&(sizeField != null)) { //need to rerender the size field because of color change...
      this.product.type.fields.byId[sizeField.id].setField(this);
    }
    
    this.price = null;
    d.currentProductType.updatePrice();
  },
  
  //check if any design work will transfer across (if any exists) so we can warn user of loss of art...
  productChangeWillMatch: function CP_productChangeWillMatch(product) {
    //store old views in an ordered array
    var oldViews = [];
    var wasUsing = false;
    for(var i=0; i < this.product.views.list.size(); i++) {
      var view = this.product.views.list[i];
      oldViews[i] = this.views[view.id];
      if((oldViews[i] == null || !oldViews[i].isUsed()) && (this.stash != null) && (this.stash[i] != null)) {
        oldViews[i] = this.stash[i]; //we are not using the view.. there is one from the stash.. lets use it...
      }
    }
    for(var v in this.views) {
      if(this.views[v].isUsed()) {
        wasUsing = true;
      }
    }
    if(!wasUsing) {
      log("productChangeWillMatch: no designs were being used");
      return 0; //no designs used.. ok to change without warning...
    }
    var moveResult = -1;
    for(var i=0; i < product.views.list.size(); i++) {
      var view = product.views.list[i];
      if(oldViews[i] != null) {
        //we can use a view from the same position....
        var newCView = new ConfiguredView(this,view, {isTempView:true});
        var mR = newCView.moveToView(oldViews[i], true);//called with testOnly=true to test move
        if(moveResult < mR) {
          moveResult = mR;
        }
      }
    }
    return moveResult; 
  },
  
  //when the product changes for this configured product... type and match up product data to be user friendly....
  setProduct: function CP_setProduct(product) {
    var oldProduct = this.product;
    this.product = product;
    if (oldProduct != product) {
      this.customProductId = null;
      if (this.options && this.options.markup) {
        this.options.markup = null;
        this.markupMode = d.brandMarkups[this.brandId].mode;
        this.markupAmount = d.brandMarkups[this.brandId].amount;
      }
      this.overridingPrice = null;
      this.isAutoPrice = true;
      this.overridingTotal = null;
      this.totalLocked = false;
      
      this.discountLocked = false;
      this.overridingDiscount = null;
      
      this.overridingBasePrice = null;
      this.isAutoBasePrice = true;
      this.legacyBasePrice = null;
      this.isAutoSizePrice = true;
    }
    
    if(oldProduct != null) {
      
      //move the field choices across...
      this.selectedColorId = null;
      var oldFields = this.cFields;
      this.initFields();
      
     for(var i=0; i < this.product.type.fields.list.length; i++) {
        var fieldDef = this.product.type.fields.list[i];
        if(fieldDef.code != "C") {
          var newField = this.cFields[fieldDef.id];
          if(newField != null) {
            var oldFieldDef = oldProduct.type.fieldsByCode[fieldDef.code];
            if(oldFieldDef==null) { 
                oldFieldDef = oldProduct.type.fieldsByName[fieldDef.name];
                if(oldFieldDef != null) {
                  log("setProduct: field with name " + fieldDef.name + " is in both products");
                }
            } else{
              log("setProduct: field with code " + fieldDef.code + " is in both products");
            }
            if(oldFieldDef != null) {
              //field exists in both types..
              oldCField = oldFields[oldFieldDef.id];
              if(oldCField != null) {
                newField.initFromPreviousSelection(oldCField);
              } else {
                log("setProduct: field with code " + fieldDef.code + " is missing from oldProduct");
              }
            }
            newField.refreshHtml();
          }
        }
      }
      this.setQtyFromMulti();
      
      
      this.initFieldDefaults();
      
      //for each view that is used
      log("Setting Product" );
      log(product);
      log(oldProduct);
      
      
      //store old views in an ordered array
      var oldViews = [];
      for(var i=0; i < oldProduct.views.list.size(); i++) {
        var view = oldProduct.views.list[i];
        oldViews[i] = this.views[view.id];
        if((oldViews[i] == null || !oldViews[i].isUsed()) && (this.stash != null) && (this.stash[i] != null)) {
          oldViews[i] = this.stash[i]; //we are not using the view.. there is one from the stash.. lets use it...
          log("Using stashed view for " + view.name + " pos=" + i);
        }
      }
      
      this.views = {};
      //go through the views in order.. use order to match....
      for(var i=0; i < this.product.views.list.size(); i++) {
        var view = this.product.views.list[i];
        if(oldViews[i] != null) {
          //we can use a view from the same position....
          var newCView = this.getView(view.id, true);
          newCView.moveToView(oldViews[i]);
          //oldViews[i] = null;
        }
      }
      //do we want to try and fill views from stuff in oldViews?... for now we wont do the order of views wont change around...
      
      //keep the oldViews arounf
      //anything in oldViews needs to be cleaned up...
      for(var i=0; i < oldViews.length; i++) {
        if(oldViews[i] != null) {
          oldViews[i].remove(); 
        }
      }
      this.stash = null; //oldViews; DONT STASH VIEWS ANYMORE: confusing to customers...
      
      
      
      
    } else {
      
      this.initFields();
      this.initFieldDefaults();
      this.selectedColorId = null;
    }
    this.imageURL = product.imageURL;
    this.largeImageURL = product.largeImageURL;
  },
  
  //called after moving views between products and the view thumbnails are rendered to add the warning against....
  afterProductChanged: function CP_afterProductChanged() {
    this.callServerToRenderThumbnails();
    for(var k in this.views) {
      this.views[k].validate();
    }
  },
  

  getPercentMarkup: function CP_getPercentMarkup(forcePercent) {
    var percentMarkup = 1;
    if((forcePercent == true) || (!this.usingCustomProductWithFixedMarkup() && this.markupMode == 0)) {
      percentMarkup =  1 + this.markupAmount / 100;
      log("Percent Markup=" + percentMarkup);
    }
    return percentMarkup;
  },
  
  getFixedMarkup: function CP_getFixedMarkup() {
    var fixedMarkup = 0;
    if(this.usingCustomProductWithFixedMarkup()) {
      fixedMarkup = this.customProduct.markup;
      log("Fixed Custom Product Markup=" + fixedMarkup);
    } else if(this.markupMode == 1) {
      fixedMarkup = this.markupAmount;
      log("Fixed Markup=" + fixedMarkup);
    } else if(this.markupMode != 0) {
      fixedMarkup = this.customProduct.markupAmount;
      log("Fallback Fixed Custom Product Markup=" + fixedMarkup);
    }
    return fixedMarkup;
  },
  
  registerLegacyPrice: function(legacyPrice, livePrice, obj) {
    if(legacyPrice != livePrice) {
      legacyPrice = d.round(legacyPrice);
      livePrice = d.round(livePrice);
      if(legacyPrice != livePrice) {
        this.legacyPriceChanges.push({legacyPrice:legacyPrice, livePrice:livePrice, obj:obj});
        d.registerLegacyPrice(this);
      }
    }
  },
  
  getUnitPrice: function CP_getUnitPrice() {
    var percentMarkup = this.getPercentMarkup();
    var fixedMarkup = this.getFixedMarkup();
    log("percentMarkup= " + percentMarkup + " fixedMarkup=" + fixedMarkup);
    log("Branding is :"+this.brandId);
    
    this.legacyPriceChanges = [];
    if (this.cp_type == CP_CREDIT) {
      this.pricing = priceFromRRP(this.price, false, percentMarkup, 0.0);
      
      this.price = this.pricing.getOverride();
      this.cost = 0.0;
      log("final unit pricing=" + this.pricing.toString());
      return this.price;
    } else if (this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_GIFT_CERTIFICATE) {
      if(this.rrp == null) {
        this.rrp = this.price;
      }
      this.pricing = priceFromCost(this.rrp, d.commissionRate, percentMarkup, 0.0, this.overridingPrice, !this.isAutoPrice, d.legacyPrice(this.legacyPrice));
      
      this.pricing.round();
    
      this.pricing = d.roundPriceObject(this.pricing);
      
      this.price = this.pricing.getOverride();
      this.cost = this.pricing.wsCost;
      log("final unit pricing=" + this.pricing.toString());
      return this.pricing.getOverride();
    } else if (this.cp_type == CP_DIGITIZATION) {
      if(this.rrp == null) {
        this.rrp = rpToRRP(this.rp, d.commissionRate, percentMarkup, 0.0);
      } 
      this.pricing = priceFromCost(this.rrp, d.commissionRate, percentMarkup, 0.0, this.overridingPrice, !this.isAutoPrice, d.legacyPrice(this.legacyPrice));
      
      this.pricing.round();
    
      //we need to apply rounding to match internet prices
      this.pricing = d.roundPriceObject(this.pricing);
      
      this.price = this.pricing.getOverride();
      this.cost = this.pricing.wsCost;
      log("final unit pricing=" + this.pricing.toString());
      return this.pricing.getOverride();
    }
    
    var color = this.getSelectedColor();
    
    var colorType = 0;
    if(color != null) {
      colorType = color.color_type;
      log("Color not null, colorType = " + colorType);
    } else {
      log("Color NULL!, colorType = " + colorType);
    }
    
    var cost = 0;
    
    
    if(colorType == 0 || this.product.type.pricingType == PRICE_TYPE_FLAT) {
      cost = parseFloat(this.product.price[0]);
		} else {
			cost = parseFloat(this.product.price[colorType]);
		}
    
		log("cost=" + cost + ", d.commissionRate=" + d.commissionRate + ", percentMarkup=" + percentMarkup + ", this.overridingBasePrice=" + this.overridingBasePrice);
    var price = priceFromCost(cost, d.commissionRate, percentMarkup, fixedMarkup, this.overridingBasePrice, !this.isAutoBasePrice, d.legacyPrice(this.legacyBasePrice));
    this.registerLegacyPrice(this.legacyBasePrice, price.crp, this);
    
    this.basePrice = price;

    var basePriceWithDecoration = price;
    
    
    log("basePrice=" + price.toString());
    
    var pricingData = {
      percentMarkup: percentMarkup,
      basePrice: price,
      //baseColorDelta: baseColorDelta,
      basePriceWithDecoration: price,
      //baseCostWithDecoration: cost,
      //unUsedDecorationCost: 0.0,
      // baseCost: cost,
      views: new MapList(),
      fields: new MapList(),
      decorationPrice: priceZero(),
      //decorationCost: priceZero(),
      fieldPrice: priceZero(),
      fieldPriceWithoutSizes: priceZero()
      //fieldCost: priceZero()
    };
    
    
    var decorationPrice = priceZero();
    var fieldDecorationCost = 0.0;
    //init the base price to include the first area price that can use the selected process
    if((!this.hasDecorations()) && this.defaultProcessId != -1) { //as soon as we have started decorating we no longer use default prices...(d.userSelectedProcessId != -1 removed for BH)
      var foundDefaultPricing = false;
      var procs = [];
      if (this.defaultProcessId != null) procs.push(this.defaultProcessId);
      procs.push(this.product.getFirstProcessId());
      
      for(var a=0; a < 2; a++) {
        var dProcessId = procs[a];
        log("No Customising Done: Using default decoration prices from process " + dProcessId);
        for(var i =0; i < this.product.views.list.size(); i++) {
          var pView = this.product.views.list[i];
          var noUsageProcess = pView.getDefaultPricingProcess(dProcessId);
          if(noUsageProcess != null) {
          //we now need to store a configured process to represent this...
          var cProcess = this.getCProcess(noUsageProcess);
          var cView = cProcess.configuredViewArea.configuredView;
          var pricingState = {processes: {}};
          for(var i=0; i < processes.list.length; i++) {
            pricingState.processes[processes.list[i].id] = { totalUsed: 0, currentCount: 0} ;
          }
          this.defaultProcessId = dProcessId;
          pricingState.processes[this.defaultProcessId].totalUsed = 1;
          cProcess.defaultOn = true;
          decorationPrice = cView.calculatePrice(colorType, pricingData, percentMarkup, pricingState, noUsageProcess);
          pricingData.decorationPrice = decorationPrice;
          /*
          
            var cost = noUsageProcess.getDefaultDecorationPrice(colorType);
            log("Default Decoration Cost:" + cost);
          pricingData.basePriceWithDecoration += (cost * d.wsRate * percentMarkup);
          //pricingData.baseCostWithDecoration += cost;
            fieldDecorationCost = cost;
          */
            foundDefaultPricing = true;
            this.defaultProcessId = dProcessId;
            break;
          } else {
            log("noUsageProcess not found");
          }
        }
        if(!foundDefaultPricing) {
          log("No default pricing found for process " + dProcessId);
        } else {
           break;
        }
      }
    } else {
      //update the decoration cost
      var pricingState = {processes: {}};
      for(var i=0; i < processes.list.length; i++) {
        pricingState.processes[processes.list[i].id] = { totalUsed: 0, currentCount: 0} ;
      }
      //get usage counts...(needed by dtg price tables...)
      for(var i =0; i < this.product.views.list.size(); i++) {
        var pView = this.product.views.list[i];
        var cView = this.views[pView.id];
        if((cView != null) && (cView.isUsed())) {
          for(var j=0; j < pView.areas.list.size(); j++) {
            var pArea =  pView.areas.list[j];
            var cArea = cView.areas[pArea.id];
            if((cArea != null) && (cArea.isUsed())) {
              for(var x=0; x < processes.list.length; x++) {
                if(cArea.isUsed(processes.list[x].id)) {
                  pricingState.processes[processes.list[x].id].totalUsed++;
                }
              }
            }
          }
        } 
      }
      
      for(var i =0; i < this.product.views.list.size(); i++) {
        var pView = this.product.views.list[i];
        var cView = this.views[pView.id];
        if((cView != null) && (cView.isUsed())) {
          decorationPrice = decorationPrice.add(cView.calculatePrice(colorType, pricingData, percentMarkup, pricingState));
        } 
      }
      pricingData.decorationPrice = decorationPrice;
    }
    log("decorationPrice=" + decorationPrice.toString());
    
    var unitPrice = this.basePrice.add(decorationPrice); 
    
    //get the prices for field choices
    
    var totalFieldPrice = this.fieldPrice(colorType, decorationPrice, this.basePrice, percentMarkup, pricingData); 
    
    
    //unitCost += fieldCost;
    //var fieldPrice = fieldCost * d.wsRate * percentMarkup;
    //pricingData.fieldCost = fieldCost;
    //unitPrice += fieldPrice;
    pricingData.fieldPrice = totalFieldPrice;
    log("totalFieldPrice=" + totalFieldPrice.toString());
    //round to full cents...
    //unitPrice = parseFloat(Math.round(unitPrice * 100.0) / 100);
    //unitCost = parseFloat(Math.round(unitCost * 100.0) / 100);
    //log("final unit price=" + unitPrice + " cost=" + unitCost);
    
    this.pricing = unitPrice.add(totalFieldPrice);
    this.pricing.round();
    
    this.pricing = d.roundPriceObject(this.pricing);
    
    this.price = this.pricing.getOverride();
    this.cost = this.pricing.wsCost;
    log("final unit pricing=" + this.pricing.toString());
    
    
    this.pricingData = pricingData;
    return this.price;
  },
  
  fieldPrice: function(colorIdx, decorationPrice, basePrice, percentMarkup, pricingData) {
    log("fieldPrice for colorIdx=" + colorIdx);
    var fixedMarkups = {};
    var percentMarkups = {};
    var finalMarkups = {};
    //sum defaults for the fields...
   
    
    
    //determine the fixed/percent markups for each field
    for(var i=0; i < this.product.fields.list.size(); i++) {
      var pField = this.product.fields.list[i];
      if(pField.used) {
        if(pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
          var fieldDef = pField.fieldDef;
          var cField = this.cFields[fieldDef.id];
          if(cField != null) {//null if discontinued and not used
            var priceModifierType = fieldDef.priceModifierType;
            if (priceModifierType != PRICE_MODIFY_NONE) {
              var fieldPriceData = {
                id: fieldDef.id,
                type: 4,
                field: cField,
                selectedOptions: new MapList()
              };
              var legacyId = "";
              pricingData.fields.add(fieldPriceData);
              var fieldMarkup = 0.0;
              var fieldQty = 0;
              var fPrice = priceZero();
              var fieldColorIdx = (fieldDef.pricingType == PRICE_TYPE_FLAT) ? 0 : colorIdx;
              if(fieldDef.typeOptions.list) {
                var selectedOptions = cField.getSelectedOptions(true);
                legacyId = (fieldDef.pricingType == PRICE_TYPE_FLAT) ? "SP:" : "CT:c=" + colorIdx;
                var selectedOptionIds = [];
                
                for(var j=0; j < selectedOptions.length; j++) {
                  var cChoice = selectedOptions[j];
                  var priceModifierToUse = pField.usePriceDefaults ? cChoice.def.defaultPrices : cChoice.pDef.priceDelta;
                  var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
                  choicePrice = parseFloat(choicePrice);
                  
                  var overrideDelta = null;
                  var legacyDelta = null;
                  if(cChoice != null) {
                    overrideDelta = cChoice.getOverrideDelta(basePrice.getLegacy());
                    legacyDelta = cChoice.getLegacyDelta(basePrice.getLegacy(), legacyId);
                    log("overrideDelta=" + overrideDelta + ", legacyDelta=" + legacyDelta + ", cChoice.unitPrice=" + cChoice.unitPrice);
                  }
                  
                  var cPrice = null;
                  if(priceModifierType == PRICE_MODIFY_FIXED) {
                    cPrice = priceFromCost(choicePrice, d.commissionRate, percentMarkup, 0, overrideDelta, (overrideDelta != null), d.legacyPrice(legacyDelta));
                  } else {
                    cPrice = priceFromCost(choicePrice, 0, 1.0, 0, null,false, null); 
                  }
                  
                  if((legacyDelta != null)&&(priceModifierType == PRICE_MODIFY_FIXED)) {
                    this.registerLegacyPrice(legacyDelta + basePrice.crp, cPrice.crp + basePrice.crp, cChoice);
                  }
      
                  var optPriceData = {
                    id: cChoice.id,
                    type: 5,
                    option: cChoice,
                    qty: null,
                    total: cPrice
                  };
                  if(cField.multiSelect) {
                    selectedOptionIds.push("" + cChoice.id + "x" +  cChoice.qty);
                    if(priceModifierType == PRICE_MODIFY_FIXED) {
                      log("fieldPrice: multi select " + cChoice.def.name + ", qty=" + cChoice.qty + ":" + cPrice.toString());
                      cPrice = cPrice.multiply(parseFloat(cChoice.qty));  
                    }
                    fPrice = fPrice.add(cPrice);
                    fieldMarkup += choicePrice * cChoice.qty;
                    fieldQty += cChoice.qty;
                    optPriceData.qty = cChoice.qty;
                  } else {
                    fPrice = fPrice.add(cPrice);
                    fieldMarkup += choicePrice;
                    selectedOptionIds.push("" + cChoice.id);
                  }
                  log("fieldPrice: option " + cChoice.def.name + ":" + cPrice.toString());
                  fieldPriceData.selectedOptions.add(optPriceData);
                }
                selectedOptionIds = selectedOptionIds.sortBy( function(el) { return el;});
                legacyId += "&o=" + selectedOptionIds.join(","); 
                if(cField.multiSelect) {
                  if(fieldQty == 0) {
                    fPrice = priceZero();
                    fieldMarkup = 0;
                  } else {
                    fPrice = fPrice.divide(parseFloat(fieldQty));
                    fieldMarkup = fieldMarkup / fieldQty;
                  }
                  
                  log("fieldPrice: rounding multiSelect:" + fPrice.toString());
                }
              } else if(cField.isUsed()) {
                var priceModifierToUse = pField.usePriceDefaults ? fieldDef.priceModifier : pField.priceDelta;
                var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
                fieldMarkup += parseFloat(choicePrice);
                fPrice = priceFromCost(choicePrice, d.commissionRate,percentMarkup, 0); //TODO: get overrides/legacy...
                legacyId = "USED";
              }
              fieldPriceData.legacyID = legacyId;
              fieldPriceData.total = fPrice; 
              if(priceModifierType == PRICE_MODIFY_FIXED) {
                fixedMarkups[fieldDef.id] = fPrice; //priceFromCost(fieldMarkup,percentMarkup, 0);
                log(fieldDef.name + " has a fixed markup of " + fPrice.toString());
              } else {
                percentMarkups[fieldDef.id] = fieldMarkup;
                log(fieldDef.name + " has a percent markup of " + fieldMarkup + "% ("  + fPrice.toString() + ")");
              }
            } else if (pField.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
              var fieldPriceData = {
                id: fieldDef.id,
                type: 4,
                field: cField,
                selectedOptions: new MapList()
              };
              var legacyId = "";
              pricingData.fields.add(fieldPriceData);
              var fieldQty = 0;
              var fPrice = priceZero();
              
                var selectedOptions = cField.getSelectedOptions(true);
                legacyId = "SP:";
                var selectedOptionIds = [];
                
                for(var j=0; j < selectedOptions.length; j++) {
                  var cChoice = selectedOptions[j];
                  
                  var overrideDelta = null;
                  var legacyDelta = null;
                  if(cChoice != null) {
                    overrideDelta = cChoice.getOverrideDelta(basePrice.getLegacy());
                    legacyDelta = cChoice.getLegacyDelta(basePrice.getLegacy(), legacyId);
                    log("overrideDelta=" + overrideDelta + ", legacyDelta=" + legacyDelta + ", cChoice.unitPrice=" + cChoice.unitPrice);
                  }
                  
                  var cPrice = priceFromCost(0, d.commissionRate, percentMarkup, 0, overrideDelta, (overrideDelta != null), d.legacyPrice(legacyDelta));
                  
                  if(legacyDelta != null) {
                    this.registerLegacyPrice(legacyDelta + basePrice.crp, cPrice.crp + basePrice.crp, cChoice);
                  }
                  
                  var optPriceData = {
                    id: cChoice.id,
                    type: 5,
                    option: cChoice,
                    qty: null,
                    total: cPrice
                  };
                  if(cField.multiSelect) {
                    selectedOptionIds.push("" + cChoice.id + "x" +  cChoice.qty);
                    
                      log("fieldPrice: multi select " + cChoice.def.name + ", qty=" + cChoice.qty + ":" + cPrice.toString());
                      cPrice = cPrice.multiply(parseFloat(cChoice.qty));  
                    
                    fPrice = fPrice.add(cPrice);
                    fieldQty += cChoice.qty;
                    optPriceData.qty = cChoice.qty;
                  } else {
                    fPrice = fPrice.add(cPrice);
                    selectedOptionIds.push("" + cChoice.id);
                  }
                  log("fieldPrice: option " + cChoice.def.name + ":" + cPrice.toString());
                  fieldPriceData.selectedOptions.add(optPriceData);
                }
                selectedOptionIds = selectedOptionIds.sortBy( function(el) { return el;});
                legacyId += "&o=" + selectedOptionIds.join(","); 
                if(cField.multiSelect) {
                  if(fieldQty == 0) {
                    fPrice = priceZero();
                  } else {
                    fPrice = fPrice.divide(parseFloat(fieldQty));
                  }
                  
                  log("fieldPrice: rounding multiSelect:" + fPrice.toString());
                }
              
              fieldPriceData.legacyID = legacyId;
              fieldPriceData.total = fPrice;
              
              fixedMarkups[fieldDef.id] = fPrice;
            }
          }
        }
      }
    }
    fixedMarkups[-1] = basePrice;   
    fixedMarkups[-2] = decorationPrice;  

    var totalFieldMarkup = priceZero();
    var totalFieldMarkupWithoutSizes = priceZero();
    
    for(var i=0; i < this.product.fields.list.size(); i++) {
      var pField = this.product.fields.list[i];
      if(pField.used) {
        if(pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
          var fieldDef = pField.fieldDef;
          var cField = this.cFields[fieldDef.id];
          if(cField != null) { //null if discontinued and not used
            var priceModifierType = fieldDef.priceModifierType;
            if (priceModifierType != PRICE_MODIFY_NONE) {
              var fieldMarkup = priceZero();
              var fieldPriceData = pricingData.fields.byId[fieldDef.id];
              if(priceModifierType == PRICE_MODIFY_FIXED) {
                 fieldMarkup = fixedMarkups[fieldDef.id];
                 fieldMarkup.round();
              } else {
                var totalFieldQty = 0;
                if(fieldDef.typeOptions.list) {
                  for(var k=0; k < fieldPriceData.selectedOptions.list.length; k++) {
                    var optPriceData = fieldPriceData.selectedOptions.list[k];
                    var cPrice = priceZero();
                    var thisPercent = parseFloat(optPriceData.total.cost) / 100.0 ;
                    //we need to apply a percentage to the selected fixed fields
                    for(var j=0; j < fieldDef.percentOf.length; j++) {
                      var percentOfId = fieldDef.percentOf[j];
                      var fixedMarkupData = null;
                      if(percentOfId == -1) {
                        fixedMarkupData = { total: basePrice};
                      } else if(percentOfId == -2) {
                        fixedMarkupData = { total: decorationPrice};
                      } else {
                        fixedMarkupData = pricingData.fields.byId[percentOfId];
                      }
                      if(fixedMarkupData == null) {
                        log("Unable to get fixed markup for field " + percentOfId);
                      } else {
                        legacyId += "&po=" + fixedMarkupData.total.crp;
                        log("Adding " + (thisPercent * 100) + "% of " + fixedMarkupData.total.toString() + " for field " + percentOfId);
                        cPrice = cPrice.add(fixedMarkupData.total.multiply(thisPercent));
                      }
                    }
                    // cPrice now has the rrp in override.. update the cost/wsCost, rrp and crp of optPriceData.total
                    cPrice.override = optPriceData.total.override;
                    cPrice.isOverriden = optPriceData.total.isOverriden;
                    cPrice.legacy = optPriceData.total.legacy;
                    
                    if(cPrice.legacy != null) {
                      this.registerLegacyPrice(cPrice.legacy + basePrice.crp, cPrice.crp + basePrice.crp, cChoice);
                    }
                    
                    optPriceData.total = cPrice;
                    cPrice.round();
                    if(cField.multiSelect) {
                      cPrice = cPrice.markup(optPriceData.qty * 100);  
                      totalFieldQty += optPriceData.qty;
                    }
                    
                    fieldMarkup = fieldMarkup.add(optPriceData.total);
                  }
                  if(cField.multiSelect) {
                    fieldMarkup = fieldMarkup.divide(parseFloat(totalFieldQty));
                  }
                  fieldMarkup.round();
                  fieldPriceData.total = fieldMarkup;
                } else if(cField.isUsed()) {
                  var priceModifierToUse = pField.usePriceDefaults ? fieldDef.priceModifier : pField.priceDelta;
                  var thisPercent = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
                  legacyId = "USED";
                  for(var j=0; j < fieldDef.percentOf.length; j++) {
                    var percentOfId = fieldDef.percentOf[j];
                    var fixedMarkupData = null;
                    if(percentOfId == -1) {
                      fixedMarkupData = { total: basePrice};
                    } else if(percentOfId == -2) {
                      fixedMarkupData = { total: decorationPrice};
                    } else {
                      fixedMarkupData = pricingData.fields.byId[percentOfId];
                    }
                    if(fixedMarkupData == null) {
                      log("Unable to get fixed markup for field " + percentOfId);
                    } else {
                      legacyId += "&po=" + fixedMarkupData.total.crp;
                      log("Adding " + (thisPercent * 100) + "% of " + fixedMarkupData.total.toString() + " for field " + percentOfId);
                      fieldMarkup = fieldMarkup.add(fixedMarkupData.total.multiply(thisPercent/100.0));
                    }
                  }
                }
              }
 
              log("Calcing Field Markup, " + fieldDef.name + " adds $" + fieldMarkup.toString());
              totalFieldMarkup =  totalFieldMarkup.add(fieldMarkup);
              if (pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_SIZE) {
                totalFieldMarkupWithoutSizes = totalFieldMarkupWithoutSizes.add(fieldMarkup);
              }
            } else if (pField.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
              var fieldMarkup = fixedMarkups[fieldDef.id];
              fieldMarkup.round();
              
              log("Calcing Field Markup, " + fieldDef.name + " adds $" + fieldMarkup.toString());
              totalFieldMarkup =  totalFieldMarkup.add(fieldMarkup);
            }
          }
        }
      }
    }
    
    //this.buildSizeOptions();
    
    pricingData.fieldPriceWithoutSizes = totalFieldMarkupWithoutSizes;
    return totalFieldMarkup;
  },
  
  hasSizeField: function CP_hasSize() {
    return (this.product.getSizeField() != null);
  },
  
  supportMultiSizes: function CP_supportMultiSizes() {
    var pSizeField = this.product.getSizeField();
    if(pSizeField == null || pSizeField.multiOption == null) return false;
    return true;
  },
  
  useMultiSizes: function CP_useMultiSizes() {
    if (this.supportMultiSizes()) {
      var pSizeField = this.product.getSizeField();
      cSizeField = this.cFields[pSizeField.id];
      if (!cSizeField.multiSelect) {
        cSizeField.setValue(pSizeField.multiOption.id + '', null, true);
      }
      return true;
    } else {
      return false;
    }
  },
  
  //assumes this.pricingData is up-to-date 
  buildSizeOptions: function CP_buildSizeOptions() {
    var pSizeField = this.product.getSizeField();
    if(pSizeField == null) return null;
    
    var percentMarkup = this.getPercentMarkup();
    
    var priceModifierType = pSizeField.fieldDef.priceModifierType;
    
    var color = this.getSelectedColor();
    var colorType = 0;
    if(color != null) {
      colorType = color.color_type;
    }
    
    var cField = this.cFields[pSizeField.id];
    
    var fieldColorIdx = 0;
    var legacyKey = "SP:";
    if(pSizeField.fieldDef.pricingType != PRICE_TYPE_FLAT) {
      fieldColorIdx = colorType;
      legacyKey = "CT:c=" + colorType;
    }
    
    var options = new MapList();
    for(var i=0; i < pSizeField.options.list.length; i++) {
      var pOpt = pSizeField.options.list[i];
      if(!pOpt.def.isMulti) {
        var optData = {
          id: pOpt.id, 
          pOpt: pOpt,
          qty: 0,
          subs: new MapList()
        };
        
        options.add(optData);
        
        cOpt = cField.options[pOpt.id];
        this.addOptionPrice(optData, pSizeField, pOpt, cOpt, fieldColorIdx, percentMarkup, priceModifierType, legacyKey);
        if(pOpt.def.subs != null) {
          for(var j=0; j < pOpt.def.subs.list.length; j++) {
            if (pOpt.subs != null) {
              var pSubOpt = pOpt.subs[pOpt.def.subs.list[j].id];
              if(pSubOpt != null) {
                var subOptData = {
                  id: pSubOpt.id, 
                  pOpt: pSubOpt,
                  qty: 0
                };
                cSubOpt = (cOpt == null) ? null : cOpt.subOptions[pSubOpt.id];
                this.addOptionPrice(subOptData, pSizeField, pSubOpt, cSubOpt, fieldColorIdx, percentMarkup, priceModifierType, legacyKey);
                optData.subs.add(subOptData);
              }
            }
          }
        }
      }
    }
    log("=============buildSizeOptions================");
    log(options);
    this.sizeOptions = options;
    return options;
  },
  
  addOptionPrice: function(optData, pSizeField, pOpt, cOpt, fieldColorIdx, percentMarkup, priceModifierType, legacyKey) {
    //get the unit price of the option.. (base price + size delta)
      
    var priceModifierToUse = pSizeField.usePriceDefaults ? pOpt.def.defaultPrices : pOpt.priceDelta;
    var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
    choicePrice = parseFloat(choicePrice);
    
    var overrideDelta = null;
    var legacyDelta = null;
    if(cOpt != null) {
      overrideDelta = cOpt.getOverrideDelta(this.basePrice.getLegacy());
      legacyDelta = cOpt.getLegacyDelta(this.basePrice.getLegacy(), legacyKey);
      optData.qty = cOpt.qty;
    }
    
    if(priceModifierType == PRICE_MODIFY_FIXED) {
      fieldMarkup = priceFromCost(choicePrice, d.commissionRate, percentMarkup, 0, overrideDelta, (overrideDelta != null), d.legacyPrice(legacyDelta)); 
    } else {
      var thisPercent = choicePrice;
      var fieldDef = pSizeField.fieldDef;
      fieldMarkup = priceZero();
      //we need to apply a percentage to the selected fixed fields
      for(var j=0; j < fieldDef.percentOf.length; j++) {
        var percentOfId = fieldDef.percentOf[j];
        var fixedMarkupData = this.pricingData.fields[percentOfId];
        if(fixedMarkupData == null) {
          log("Unable to get fixed markup for field " + percentOfId);
        } else {
          log("Adding " + thisPercent + "% of " + fixedMarkupData.total.toString() + " for field " + percentOfId);
          fieldMarkup = fieldMarkup.add(fixedMarkupData.total.markup(thisPercent));
          //fieldMarkup += (fixedMarkup * thisPercent / 100.0);
        }
      }
      fieldMarkup.legacy = legacyDelta;
      fieldMarkup.override = overrideDelta;
      fieldMarkup.isOverriden = (overrideDelta != null);
    }
    log("buildSizeOptions " + pOpt.def.name + ":" + fieldMarkup.toString());
    //we now have the rrp/crp for the field choice with override/legacy set as a delta...
    optData.price = this.basePrice.add(fieldMarkup); //add the base price to get a unit price....
    optData.price.round();
    return optData;
  },
  
  calcLegacyDiscountKey: function CP_calcLegacyDiscountKey() {
    if(this.cp_type == CP_DIGITIZATION || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_CREDIT) {
      return 'Q:' + this.qty + '&C:' + cCustomerId + '&UP' + this.price.toPrice();
    } else {
      return "P:" + ((this.product) ? this.product.id : '') + '&Q:' + this.qty + '&C:' + cCustomerId + '&UP' + this.price.toPrice();
    }
  },
  
  getDiscount: function CP_getDiscount(discountType) {
    var legacyDiscount = null;
    var calcRate = this.getCalculatedDiscountRate() / 100.0;
    var legacyKey = this.calcLegacyDiscountKey();
    if (legacyKey == this.legacyDiscountKey) {
      legacyDiscount = this.legacyDiscount;
      this.registerLegacyPrice(legacyDiscount, this.calculatedDiscountAmount, this);
    }
    if (this.totalLocked) {
      this.discount = this.overridingDiscount = this.price * this.qty - this.overridingTotal;
      this.discountType = AMOUNT_TYPE_PERCENT;
      this.typedOverridingDiscount = this.overridingDiscount / (this.price * this.qty);
      this.totalLocked = false;
      this.discountLocked = true;
    } else if (this.discountLocked) {
      log("this.price=" + this.price);
      log("this.typedOverridingDiscount=" + this.typedOverridingDiscount);
      if (this.discountType == AMOUNT_TYPE_PERCENT) {
        this.discount = this.overridingDiscount = this.pricing.getOverride() * this.qty * this.typedOverridingDiscount;
      } else {
        this.discount = this.overridingDiscount = this.typedOverridingDiscount;
      }
    } else {
      this.calcLegacyDiscount = legacyDiscount;
      this.discount = d.legacyPrice(this.calcLegacyDiscount, this.calculatedDiscountAmount);
    }
    
    var disc;
    if (discountType === null || typeof(discountType) == 'undefined') discountType = this.discountType;
    if (discountType == AMOUNT_TYPE_PERCENT) {
      disc = d.round(this.discount / (this.pricing.getOverride() * this.qty)  * 100.0); //convert back to %
    } else {
      disc = d.round(this.discount);
    }
    if (isNaN(disc)) disc = 0;
    return disc;
  },
  
  getDiscountId: function CP_getDiscountId() {
    var discountId;
    if(d.appLicMode == LIC_SOLO) {
      discountId = d.defaultDiscountId;
      this.discountToBase = false;
    } else if(this.product.discountId == -1) {
      discountId = this.product.type.discountId;
      this.discountToBase = this.product.type.discountToBase;
    } else {
      discountId = this.product.discountId;
      this.discountToBase = this.product.discountToBase;
    }
    return discountId;
  },
  
  getCalculatedDiscountRate:function CP_getCalculatedDiscountRate() {
    this.calculatedDiscountAmount = 0.0;
    this.bulkDiscount = 0;
    if(this.cp_type == CP_DIGITIZATION || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_CREDIT) {
      this.customerDiscount = (this.pricing.getOverride() * this.qty * cDisc / 100.0).round();
      this.calculatedDiscountAmount = this.customerDiscount; 
    } else {
      var discountId = this.getDiscountId();
      var dPrice = this.pricing.getOverride();
      var dCost = this.pricing.wsCost;
      
      if(discountId == null) {
        log("Product has no discount");
      } else {
        if(this.discountToBase) {
          dPrice = this.basePrice.getOverride();
          dCost = this.basePrice.wsCost;
          log("Discount applies to base (price=" + dPrice + " and cost=" + dCost + ")");
        } else {
          log("Discount applies to whole price (price=" + dPrice + " and cost=" + dCost + ")");
        }
        this.bulkDiscount = d.getDiscount(discountId, this.qty, dPrice, dCost);
        log("before rounding this.bulkDiscount = " + this.bulkDiscount);
      }
      
      this.customerDiscount = (this.pricing.getOverride() * this.qty * cDisc / 100.0).round();
      this.calculatedDiscountAmount = this.bulkDiscount + this.customerDiscount;
    }
    //we now have the fixed amount discount... convert to a % of override()
    this.calculatedDiscountRate = d.round(this.calculatedDiscountAmount / (this.pricing.getOverride() * this.qty)  * 100.0);
    log("this.calculatedDiscountRate  = " + this.calculatedDiscountRate );
    
    if (isNaN(this.calculatedDiscountRate)) this.calculatedDiscountRate = 0;
    return this.calculatedDiscountRate;
  },
  
  getPrice: function CP_getPrice() {
    this.getUnitPrice();
    this.getDiscount();
    log("this.calculatedDiscountAmount=" +this.calculatedDiscountAmount);
    var tmpDisc = priceFromCost(this.calculatedDiscountAmount, 0, 1, 0, this.overridingDiscount, this.totalLocked || this.discountLocked, d.legacyPrice(this.calcLegacyDiscount));
    log("discount=" + tmpDisc.toString());
    this.total = this.pricing.multiply(this.qty).minus(tmpDisc);
    this.calculatedFinalTotal = (this.pricing.getOverride() * this.qty - this.calculatedDiscountAmount).round();
    log("total=" + this.total.toString());
    if (this.editable === false) {
      this.finalTotal = this.legacyTotal;
    } else if (this.totalLocked) {
      this.finalTotal = this.overridingTotal;
    } else if (this.discountLocked) {
      this.finalTotal = this.pricing.getOverride() * this.qty - this.overridingDiscount;
    } else {
      this.finalTotal = this.total.getOverride();
    }

    return this.finalTotal;
  },
  
  getRRP: function CP_getRRP(applyDiscount) {
    if(this.cp_type == CP_DIGITIZATION || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_CREDIT) {
      return this.price;
    } else if (this.cp_type == CP_EXTRA_CHARGE) {
      return (this.overridingPrice) ? this.overridingPrice : this.price;
    } else {
      if(this.price == null) {
        this.getUnitPrice();
      }
      
      var discountId;
      this.discountToBase = false;
      var dPrice = this.pricing.rrp;
      var dCost = this.pricing.wsCost;
      if(d.appLicMode == LIC_SOLO) {
        discountId = d.defaultDiscountId;
        this.discountToBase = false;
      } else if(this.product.discountId == -1) {
        discountId = this.product.type.discountId;
        this.discountToBase = this.product.type.discountToBase;
      } else {
        discountId = this.product.discountId;
        this.discountToBase = this.product.discountToBase;
      }
      var discount = 0;
      if(applyDiscount) {
        if(discountId == null) {
          log("Product has no discount");
        } else {
          if(this.discountToBase) {
            dPrice = this.basePrice.rrp;
            dCost = this.basePrice.wsCost;
            log("Discount applies to base (price=" + dPrice + " and cost=" + dCost + ")");
          } else {
            log("Discount applies to while price (price=" + dPrice + " and cost=" + dCost + ")");
          }
          discount = d.getDiscount(discountId, this.qty, dPrice, dCost);
        }
        
        log("discount=" + discount);
      }
      return (this.pricing.rrp * this.qty) - discount;
    }
  },
  
  getCurPrice: function CP_getCurPrice() {
    this.getPrice() * d.cMod;
  },
  
  getCurDiscount: function CP_getCurDiscount() {
    this.discount * d.cMod;
  },
  
  multiOptionField: function CP_multiOptionField() {
    for(var k in this.cFields) {
      if(this.cFields[k].multiSelect) {
        return this.cFields[k];
      }
    }
    return null;
  },
  
  serialize: function CP_serialize(queryComponents, prefix, viewId, forSave) {
    if(prefix==null) {
      prefix="c[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    log(this);
    this.serializingForSave = forSave;
    queryComponents.push(encodeURIComponent(prefix + "[cid]") + "=" + encodeURIComponent(d.cart.id));
    queryComponents.push(encodeURIComponent(prefix + "[p]") + "=" + encodeURIComponent(this.product.id));
    queryComponents.push(encodeURIComponent(prefix + "[q]") + "=" + encodeURIComponent(this.qty));
    queryComponents.push(encodeURIComponent(prefix + "[lv]") + "=" + encodeURIComponent(this.renderVersion));
    queryComponents.push(encodeURIComponent(prefix + "[c]") + "=" + encodeURIComponent(this.getSelectedColorId()));
    queryComponents.push(encodeURIComponent(prefix + "[def_proc]") + "=" + encodeURIComponent(this.defaultProcessId));

    if(this.usingCustomProduct() || (typeof(orderManager) != "undefined" && this.customProductId != null)) {
      queryComponents.push(encodeURIComponent(prefix + "[cp]") + "=" + encodeURIComponent(this.customProductId));
    }
    
    var self = this;
    for(var i=0; i < this.product.fields.list.size(); i++) {
      var pField = this.product.fields.list[i];
      if((pField.used)&&(pField.fieldDef.code != "C")) { //ignore color field
        var cField = this.cFields[pField.id];
        if(cField != null) {
          cField.serialize(queryComponents, prefix + "[f][" + pField.id + "]");
        }
      }
    }
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      for(var k in this.views) {
        view = this.views[k];
        if((view.isUsed())&&((viewId==null)||(viewId == view.id))) {
          var p = prefix + "[v][" + view.id + "]";
          view.serialize(queryComponents, p);
        }
      }
    }
    
    if(forSave == true && this.teamNames != null && this.usingTeamnames) {
      queryComponents.push(encodeURIComponent(prefix + "[utn]") + "=1");
      this.teamNames.serialize(queryComponents, prefix + "[tn]");
    }
    this.serializingForSave = false;
    return queryComponents.join('&');
  },
  
  save: function CP_save(force, showCheckoutOption, callback) {
    if(this.qty <= 0) {
      alert(ml("You must specify a valid quantity to add this product to the cart"));
      return;
    }
    if(!this.validateFields()) {
      return;  
    }
    
    
    var self = this;
    
    var extraParams = '';
    if(showCheckoutOption == false) {
      extraParams = "&no_cart_notice=1";
    }
    
    this.checkForAlerts(false, function(result) {
      if(result) {
        var self = this;
        var aKey = null;
        var cont = null;
        
        
        if(this.addedToCart) {
          d.track("update-edited-product");
          cont = "cart_" + this.id;
          
          ajaxQueueManager.queueRequest("save_product/" + this.id, 1, {
              mode: 0,
              url: d.pathPrefix + "/designer/save_product?cpid=" + this.id + extraParams,
              target: "cart_" + this.id,
              parameters: function CP_parameters() { return self.serialize([],null, null, true);},
              options: {asynchronous:true, evalScripts:true, 
                onFailure: function CP_onFailure() {
                  alert(ml("An error occured on the server. The development team has been notified. Sorry for any inconvenience."));
                },
                onComplete: function CP_onComplete() { 
                  log("save product complete after update edit product");
                  asyncFinish(aKey);
                  d.selectTab('m','cart');
                  d.notifyCartChanged();
                  d.cart.selectCartItem(d.currentCProduct); 
                  processToolTips($(cont));
                  self.product.type.itemSaved();
                  if (typeof(dontWarnBeforeLeaving) != 'undefined') dontWarnBeforeLeaving = true;
                  if(callback!= null) callback();
                }
              }
          });
          
        } else {
          d.track("save-new-product");
          cont = "tab_pane_designer";
          
          ajaxQueueManager.queueRequest("save_product/" + this.id, 1, {
              mode: 0,
              url: d.pathPrefix + "/designer/save_product?is_new=1" + extraParams,
              target: "cart_body",
              parameters: function CP_parameters() { return self.serialize([],null, null, true);},
              options: {asynchronous:true, evalScripts:true, insertion: Insertion.Bottom,
                /*
                Moved to saveCallback called by server response
                onSuccess: function CP_onSuccess() {
                  self.addedToCart = true; 
                  d.notifyCartChanged();
                  if(d.currentCProduct == self) {
                    self.product.type.showEditing(true);
                  }
                },*/
                onFailure: function CP_onFailure() {
                  alert(ml("An error occured on the server. The development team has been notified. Sorry for any inconvenience."));
                },
                onComplete: function CP_onComplete() { 
                  log("save product complete after save new product");
                  asyncFinish(aKey);
                  d.selectTab('m','cart');
                  d.cart.selectCartItem(d.currentCProduct);
                  processToolTips($("cart_body"));
                  self.product.type.itemSaved();
                  if (typeof(dontWarnBeforeLeaving) != 'undefined') dontWarnBeforeLeaving = true;
                  if(callback!= null) callback();
                }
              }
          });
        }
        var cartTab = $("m_cart");
        if(cartTab.className == "unselected_tab_hidden") {
          cartTab.className = "unselected_tab";
        }
        aKey = asyncStart($(cont));
      }
    }.bind(this));
  },
  
  
  validateFields:  function CP_validateFields() {
    for(var i=0; i < this.product.fields.list.size(); i++) {
      var pField = this.product.fields.list[i];
      if((pField.used)&&(pField.valid)) {
        if(pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
          var fieldDef = pField.fieldDef;
          var cField = this.cFields[fieldDef.id];
          if((cField != null)&&(fieldDef.isRequired)&&(!cField.isUsed())) {
            alert(ml("You must enter a value for '%s'", fieldDef.name));
            var el = $('pt_fc_' + fieldDef.id);
            if(el != null) {
              new Effect.Highlight(el);
            }
            
            return false;
          }
        }
      }
    }
    return true;
  },
  
  saveCallback: function CP_saveCallback(errorsOccured) {
    if(errorsOccured) {
      this.checkForAlerts(false, function() {}); //show the error...
    } else {
        this.addedToCart = true; 
        d.notifyCartChanged();
        try {
          if(updateCart) updateCart();
        } catch(e) {
          //doesnt matter (this would update cart at top of page.. must be there...)
        }
        
        if(d.currentCProduct == this) {
          this.product.type.showEditing(true);
        }
    }
  },
  
  //will check for alerts, if ignoreWarnings only stops on errors
  //calls callback with true if all ok, false if the save should stop...
  checkForAlerts: function CP_checkForAlerts(ignoreWarnings, callback) {
    this.checkCallback = callback;
    var types = [0];
    if(!ignoreWarnings) {
      types.push(1);
    }
    
    log("Checking for alerts");
    var allAlertTypes = {"quality_warning":true, "crop_error":true, "overlap_error":true, "crop_warning":true};
    
    var alerts = this.getAlerts(types);
    var alertIndex = -1;
    if(hashSize(alerts[0]) > 0) { //errors.. dont bother with warnings...
      $("alert_warning_has_errors").style.display="";
      $("alert_warning_no_errors").style.display="none";
      $("alert_warning_error_icon").style.display="";
      $("alert_warning_warning_icon").style.display="none";
      alertIndex = 0;
    } else if(hashSize(alerts[1]) > 0) { //no errors.. show warnings...
      $("alert_warning_has_errors").style.display="none";
      $("alert_warning_no_errors").style.display="";
      $("alert_warning_error_icon").style.display="none";
      $("alert_warning_warning_icon").style.display="";
      alertIndex = 1;
    }
    if(alertIndex == -1) {
      this.checkCallback(true);
    } else {
      for(var k in alerts[alertIndex]) {
        var error = alerts[alertIndex][k];
        $(k + "_div").style.display="";
        delete allAlertTypes[k]; //track its been used..
        var images = $(k + "_images_list"); //remove existing images...
        while(images.childNodes.length > 0) {
          images.removeChild(images.childNodes[0]);
        }
        
        for(var i=0; i < error.items.length; i++) {
          var item = error.items[i];
          var thumb = document.createElement("Img");
          thumb.src = item.errorThumb;
          thumb.width = item.width;
          thumb.height = item.height;
          images.appendChild(thumb);
        }
        //clear unused types
        for(var k in allAlertTypes) {
          $(k + "_div").style.display="none";
        }
      }
      //this.checkCallback(false);
      popup('alert_warning');
    }
  },
  
  alertCheckFinished: function CP_alertCheckFinished(result) {
    closePopup('alert_warning') ;
    this.checkCallback(result);
  },
  
  //go through the items get any item with alerts of {types}
  getAlerts: function CP_getAlerts(types) {
    var alerts = [{},{},{}];
    for(k in this.views) {
      view = this.views[k];
      if(view.isUsed()) {
        for(var ak in view.areas) {
          var area = view.areas[ak];
          if(area.isUsed()) {
            log("Check for alerts in area " + area.productArea.getName());
            for(var ik in area.allItems) {
              var item = area.allItems[ik];
              
              item.getAlerts(types, alerts);
            }
          }
        }
      }
    }
    return alerts;
  },
  
  //set an icon against the add/update button and views if there is a warning/error
  setAlertIcons: function CP_setAlertIcons() {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) return; //viewing custom product
    var minAlert = 4;
    for(var i=0; i < this.product.views.list.size(); i++) {
      var pView = this.product.views.list[i];
      
      var view = this.views[pView.id] ;
      var minViewAlert = 4;
      if(view != null && view.isUsed()) {
        for(var ak in view.areas) {
          var area = view.areas[ak];
          if(area.isUsed()) {
            for(var ik in area.allItems) {
              var item = area.allItems[ik];
              if(item.alertLevel != null && item.alertLevel < minAlert) {
                minAlert = item.alertLevel;
              }
              if(item.alertLevel != null && item.alertLevel < minViewAlert) {
                minViewAlert = item.alertLevel;
              }
            }
          }
        }
      }
      if((pView.allowView)&&(pView.allowDesign)) {
        log(pView);
        var viewEl = $("d_l_s_" + pView.id);
        if(viewEl !=null) {
          if(minViewAlert == 1) { //warning
            $("d_l_s_" + pView.id).className = "d_layout_warning";
          } else if(minViewAlert == 0) {//error  
            $("d_l_s_" + pView.id).className = "d_layout_error";
          } else {                               
            $("d_l_s_" + pView.id).className = "d_layout_noalert";
          }
        }
      }
    }
    if(minAlert == 1) { //warning
      $("add_cart_container").className = "d_g_button_warning";
      $("update_cart_button_container").className = "d_g_button_warning";
    } else if(minAlert == 0) {//error
      $("add_cart_container").className = "d_g_button_error";
      $("update_cart_button_container").className = "d_g_button_error";
    } else {
      $("add_cart_container").className = "d_g_button_noalert";
      $("update_cart_button_container").className = "d_g_button_noalert";
    }
  },
  
  
  usingCustomProduct: function CP_usingCustomProduct() {
    if((this.customProduct)&&(this.customProduct.productId == this.product.id)) {
      return true;
    }
    return false;
  },
  
  usingCustomProductWithFixedMarkup: function CP_usingCustomProductWithFixedMarkup() {
    if((this.customProduct)&&(this.customProduct.productId == this.product.id)&&(!this.customProduct.useDefaultPricing)) {
      return true;
    }
    return false;
  },
  
  hasDecorations: function CP_hasDecorations() {
    //we still need to check the views because they may have deleted everything so we need to show the "unused price"
    for(var k in this.views) {
      view = this.views[k];
      if(view.isUsed()) {
        return true;
      }
    }
    return false;
  },
  
  isCustomised: function CP_isCustomised() {
    if(this.layout_version == 0) {//when its a custom product there will be used views yet the product has not been customised...
      return false;
    }
    //we still need to check the views because they may have deleted everything so we need to show the "unused price"
    for(var k in this.views) {
      view = this.views[k];
      if(view.isUsed()) {
        return true;
      }
    }
    return false;
  },
  
  
  saveFromView: function CP_saveFromView() {
    var self = this;
    var aKey = null;
    var cont = null;
    if(!this.validateFields()) {
      return;  
    }
    this.product.type.itemSaved();
    cont = "pt_info_" + this.product.type.id;
    if(this.addedToCart) {
      var t2 = new Ajax.Updater("cart_notice", d.ajaxUrl(d.pathPrefix + "/designer/save_product?for_view=1"), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null), 
        onComplete: function CP_onComplete() { 
          asyncFinish(aKey);
          d.notifyCartChanged();
          popup("cart_notice");
          try {
            if(updateCart) updateCart();
          } catch(e) {
            //doesnt matter (this would update cart at top of page.. must be there...)
          }
        }
      });
    } else {
      var t2 = new Ajax.Updater("cart_notice",d.ajaxUrl(d.pathPrefix + "/designer/save_product?for_view=1&is_new=1"), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null), 
        onComplete: function CP_onComplete() { 
          asyncFinish(aKey);
          d.notifyCartChanged();
          popup("cart_notice");
          try {
            if(updateCart) updateCart();
          } catch(e) {
            //doesnt matter (this would update cart at top of page.. must be there...)
          }
        }
      });
    }
    aKey = asyncStart($(cont));
  },
  
  saveWorkingVersion: function CP_saveWorkingVersion() {
    $("dynamic_popup").innerHTML = "<br/><br/>Saving..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({ success: 'dynamic_popup' }, d.ajaxUrl(d.pathPrefix + "/designer/save_working_version"), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null), 
      onComplete: function CP_onComplete() { 
        asyncFinish(aKey);
        repositionPopup("dynamic_popup");
      },
      onFailure: function CP_onFailure() {
        alert(ml("Server Error Occured"));
        closePopup("dynamic_popup");
      }
    });
    $("dynamic_popup").style.width="400px";
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  
  email: function CP_email() {
    $("dynamic_popup").innerHTML = "<br/><br/>Saving..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({ success: 'dynamic_popup' }, d.ajaxUrl(d.pathPrefix + "/designer/email"), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null), 
      onComplete: function CP_onComplete() { 
        asyncFinish(aKey);
        repositionPopup("dynamic_popup");
      },
      onFailure: function CP_onFailure() {
        alert(ml("Server Error Occured"));
        closePopup("dynamic_popup");
      }
    });
    $("dynamic_popup").style.width="650px";
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  
  showPreview: function CP_showPreview() {
    $("dynamic_popup").innerHTML = "<br/><br/>Generating..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({success:"dynamic_popup"}, d.ajaxUrl(d.pathPrefix + "/designer/save_product?big_preview=1"), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null), 
      onComplete: function CP_onComplete() { 
        asyncFinish(aKey);
      },
      onSuccess: function CP_onSuccess() {
        repositionPopup("dynamic_popup");
      },
      onFailure: function CP_onFailure() {
        closePopup("dynamic_popup");
        alert(ml("An error occured processing preview"));
      }
    });
    $("dynamic_popup").style.width="";
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  
  bindCart: function CP_bindCart() {
    log("Binding Cart Item " + this.id);
    this.cartQtyBox = $("cart_qty_" + this.id);
    this.cartPrice = $("cart_price_" + this.id);
    if(this.cartQtyBox != null) {
      var self = this;
      this.cartQtyBox.onkeyup = function() {
        d.itemChanged();
        self.updateQty(self.cartQtyBox.value, true);
      } ;
    } else {
      log("bindCart: No cart qty el for " + this.id);
    }
    if(this.cartPrice==null) {
      log("No Cart Price Element cart_price_" + this.id);
    } else {
      log(this.cartPrice);
    }
  },
  
  //make sure qty follows min qty/bundle rules...
  checkQty: function CP_checkQty(qty, updateQtyEl, updateCartEl) {
    var rQty = qty;
    if(this.product.usesMinQty()) {
      if(qty < this.product.minQty) {
        rQty = this.product.minQty;  
      } else {
        var extraQty = qty - this.product.minQty;
        var bundles = parseInt(extraQty / this.product.bundleSize);
        extraQty = bundles * this.product.bundleSize;
        rQty = this.product.minQty + extraQty;
      }
      if(rQty != qty) {
        if(updateQtyEl) {
          if(d.currentCProduct == this) {
            $("qty").value = rQty;
          }
        }
        if(updateCartEl) {
          if(this.cartQtyBox!=null) {
            this.cartQtyBox.value = rQty;
          }
        }
      }
    }
    return rQty;
  },
  
  updateQty: function CP_updateQty(value, fromCart, fromOMS) {
    if(this.multiOptionField()==null) {
      this.qty = parseInt(value, 10);
      if(isNaN(this.qty)) {
        this.qty = 0;
      }
      this.qty = this.checkQty(this.qty, true, true);
      this.updateCartPrice();
      if(fromOMS) {
        if(d.currentCProduct == this) {
          $("qty").value = this.qty;
        }
        if(this.cartQtyBox!=null) this.cartQtyBox.value = this.qty;
      } else if(fromCart) {
        if(d.currentCProduct == this) {
          $("qty").value = this.qty;
        }
      } else if(this.cartQtyBox!=null) {
        this.cartQtyBox.value = this.qty;
      }
      if(d.currentCProduct == this) {
        $("qty").disabled = d.defaultQtyDisabled;
        this.product.type.updatePrice();
      }
      if(fromCart) {
        //update the backend
        var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/update_qty?cpid=" + this.id + "&qty=" + this.qty), {asynchronous:true, evalScripts:true});
      }
    }
  },
  
  
  updateMultiQty: function CP_updateMultiQty(value, fieldId, optionId, subOptionId, unitPrice, isAutoPrice) { 
    log("updateMultiQty:" + value + " fieldId=" + fieldId + " optionId=" + optionId + " subOptionId=" + subOptionId);
    var qty = parseInt(value, 10);
    if(isNaN(qty)) {
      qty = 0;
    }
    var f = this.cFields[fieldId];
    f.addSelectedOption(optionId, qty, subOptionId, unitPrice, isAutoPrice);
    f.cleanEmptyOptions();  //options contain overriding prices info, cannot be deleted.
    this.setQtyFromMulti();
    if(d.currentCProduct == this) {
      this.product.type.updatePrice();
      $("qty").disabled = true;
      $("qty").value = this.qty;
    }
    if(this.product.type.sizeField != null && this.product.type.sizeField.id == fieldId && this.product.limitSizeColors) {
      //we need to rerender the color list as the size selection could have changed available colors..
      this.product.type.buildColorPanel(this);
    }
  },
  
  //calculate the qty from multi selector
  setQtyFromMulti: function CP_setQtyFromMulti() {
    var cField = this.multiOptionField();
    if(cField != null) {
      if(cField.multiSelect) {
        log("Setting QTY For multiple selection data");
        var qty = 0;
        for(var k in cField.options) {
          var opt = cField.options[k];
          if(!opt.def.isMulti) {
            if(opt.pDef.subs != null) {
              qty += opt.subQty;
            } else {
              qty += opt.qty;
            }
          }
        }
        this.qty = qty;
      }
    }
  },
  
  updateCartPrice: function CP_updateCartPrice(skipTotal) {
    if(this.cartPrice!=null) {
      var unitPrice = this.getUnitPrice();
      this.cartPrice.innerHTML = d.formatPrice(this.getPrice());
    } else {
      log("No Cart Price Element for " + this.id);
    }
    if(skipTotal || skipTotal == null) {
      d.cart.updateCartPrice();
    }
  },
  
  remove: function CP_remove() {
    var oldDontWarnBeforeLeaving;
    if (typeof(dontWarnBeforeLeaving) != 'undefined') oldDontWarnBeforeLeaving = dontWarnBeforeLeaving;
    for(var k in this.views) {
      var view = this.views[k];
      view.remove();
    }
    if (typeof(dontWarnBeforeLeaving) != 'undefined') dontWarnBeforeLeaving = oldDontWarnBeforeLeaving;
  },
  
  //restore state from options
  rollBack: function CP_rollBack(opts) {
    //call the cart to rollback in the same order the cart is listed in to build shared state correctly
    d.cart.rollbackProduct(this);
  },
  
  //called from cart.rollbackProduct in corrent order
  doRollBack: function CP_doRollBack(opts) {
    this.remove();
    this.initialize(this.id, (opts) ? opts : this.options);
    var select = false;
    if(d.selected_tabs["m"] == "customize") {
      select = true;
    }
    d.selectConfiguredProduct(this.id, true, select);
    this.updateQty(this.qty, false);
    this.product.type.itemSaved();
    this.setAlertIcons();
  },
  
  //called by cart to restore any shared state (digitization tracking)
  restoreSharedState: function() {
    for(var vk in this.views) {
      var view = this.views[vk];
      for(var va in view.areas) {
        var area = view.areas[va];
        for(var vi in area.allItems) {
          var item = area.allItems[vi];
          if(item.digitize) {
            item.digitizedAsset = d.cart.registerDigitizedAsset(item);
          }
        }
      }
    }
  },
  
  copy: function CP_copy(src) {
    this.remove();
    this.initialize(this.id, src.options);
    this.addedToCart = false;
    d.selectConfiguredProduct(this.id, true, true);
    this.updateQty(this.qty, false);
    this.product.type.itemSaved();
    this.setAlertIcons();
  },
  
  //called to update the configured product that will be used by the reseller
  saveProduct: function CP_saveProduct() {
    this.checkForAlerts(false, function(result) {
      if(result) {
        var self = this;
        var aKey = null;
        var cont = null;
        
        cont = "designer_container";
        var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/save_product?for_product=1"), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null), 
          onSuccess: function() { 
            //asyncFinish(aKey); 
            if (typeof(dontWarnBeforeLeaving) != 'undefined') dontWarnBeforeLeaving = true;
            var ma = ""
            if (window.location.search.indexOf("ma=4") >= 0) ma = "ma=4&"
            window.location = d.pathPrefix + "/cproducts/configure_product?"+ma+"cpid=" + self.id + "&" + d.extraCallbackParams;
          },
          
          onFailure: function() { 
            asyncFinish(aKey); 
            alert(ml("An error occured on the server. The development team has been notified. Sorry for any inconvenience."));
          }
        });
        aKey = asyncStart($(cont));
      }
    });
  },
  
  onLayoutChanged: function CP_onLayoutChanged() {
    if(d.mode == DESIGNER_MODE_CONFIGURE) {
      this.layout_version ++;
    } else {
      this.layout_version += 1000;
    }
  },
  
  //server callback when a view thumbnail is updated
  updateView: function CP_updateView(viewId, src, renderVersion, results) {
    var view = this.views[viewId];
    if(view!=null) {
      view.updateView(src, renderVersion, results);
    } else {
      //could be a derived view....
      var productView = this.product.views.byId[viewId];
      if((productView != null)&&(!productView.allowDesign)) { //it is a derived view...
        view = this.getView(viewId, true); //create one...
        view.updateView(src, renderVersion, results);
      } else {
        log("Update View called on missing view " + viewId);
      }
    }
  },
  
  //choices (sizes) can change the scale and x/y offset. So can the product itself.
  getLayoutModifiers: function CP_getLayoutModifiers() {
    
    var mods = new Hash({s:1, y:0, x:0});
    //depricated
    return mods;
  },
  
  setChildReRender: function CP_setChildReRender() {
    this.reRenderChild = true;
    this.renderVersion ++;
    d.itemChanged();
  },
  
  setReRender: function CP_setReRender() {
    this.reRender = true;
    this.renderVersion ++;
    //flag all the views that are used for rerendering
    for(var k in this.views) {
      var view = this.views[k];
      if(view.isUsed()) {
        view.setReRender(true);
      }
    }
  },
  
  clearReRender: function CP_clearReRender() {
    this.reRenderChild = false;
    this.reRender = false;
    for(var k in this.views) {
      this.views[k].clearReRender();
    }
  },
  
  //update the view thumbnails... called when all views need updating... not just a single view....
  updateThumbnails: function CP_updateThumbnails() {
  /*  logic handled below...
  if(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) { //this can only be a color change...
      //TODO....
      return;
    }
    */
    if((d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)&& (this.reRender || this.reRenderChild)) { //the data has changed requiring a rerender... need to regen the thumbnails on the server...
      //call the server to regen the thumbnails...
      this.callServerToRenderThumbnails();
    }
    var colorId = this.getSelectedColorId();
    //set the thumb of all views that dont need a reRender
    for(var i=0; i < this.product.views.list.size(); i++) {
      var productView = this.product.views.list[i];
      if(productView.allowView) {
        var cView = this.getView(productView.id, false);
        var url = null;
        if((cView == null)||(!cView.isUsed())||(d.mode ==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) { //the view has no mods...
          if(this.usingCustomProduct()) { //we are using a custom product.. check if we can use the customised view...
            url = this.customProduct.getViewURL(productView.id, 11, colorId, 1);
            log("updateThumbnails: usingCustomProduct= true, url=" + url);
          }
          if(url == null) {
            url = productView.getViewURL(11, colorId, 1); //get the blank version
          }
        } else if(cView.needsReRendering()) {
          //skip this view... waiting for it to be rerendered on the server...
        } else { 
          url = cView.getViewURL(11, true, true); //get the view url based on internal logic ocntained in cView (handle custom product prerendered views)
        }
        if(url != null) {
          var container = $("d_l_" + productView.id);
          var img = $("d_l_i_" + productView.id);
          if (img) setImageUrl(container, img, url, null);
        }
      }
    }
  },
  //save this to the server in preview mode to rerender the thumbnails
  callServerToRenderThumbnails: function CP_callServerToRenderThumbnails() {
    var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/save_product?preview=1&lcpid=" + this.id ), {asynchronous:true, evalScripts:true, parameters:this.serialize([],null, null)});
  },
  
  creditHtml: function CP_creditHtml(alt) {
    var altHtml = alt ? 'alt' : '';
    var html =
    '<tr>' +
      '<td><span class="value">' + this.dateModified + '</span></td>' +
      '<td class="text_right"><span class="value">' + d.curGlyph + (-this.finalTotal) + '</span></td>' +
      '<td><span class="value"><a href="#" onclick="orderManager.currentOrder.removeCredit(' + this.id + '); return false;">remove</a></span></td>' +
    '</tr>';
    
    return html;
  },
  
  rowHtml: function CP_rowHtml(alt, selected, hideTr, mode, incTick, ticked, tickReadOnly) {
    var thumb, code, description, qtySize, color, decorations, options, unitPrice, lineTotal, discountAsType, total;
    var qtySizeHtml, qtyTotalHtml, optionsHtml, unitPriceHtml, lineTotalHtml, discountHtml, decorationsHtml, thumbHtml, totalHtml;
    var altHtml = alt ? 'alt' : '';
    var selectedHtml = selected ? ' current' : '';
    var allowPopup = (mode != MODE_VIEW);
    var dropdownHtml = (mode != MODE_VIEW) ? 'dropdown' : 'value';
    
    var posHtml = '<td class="text_right"><span class="value">' + this.pos + '</span></td>';
    if (this.product) {
      var warningFlag = (this.okToSave) ? '' : '<span class="icon alert_16">warning</span>';
      this.getPrice();
      if (this.imageURL) {
        largeThumbHtml = (this.largeImageURL) ? 'onmouseover="orderManager.popupManager.showRollover(this, d.cart.getProduct(' + this.id + ').thumbNailPopupHtml());" onmouseout="orderManager.popupManager.hideRollover(this);"' : '';
        thumbHtml = warningFlag + '<span id="it_cp_thumb_ct_' + this.id + '" ' + largeThumbHtml + '><img id="it_cp_thumb_' + this.id + '" class="table_thumb" src="' + this.imageURL + '" alt="[ ]" /></span>';
      } else {
        thumbHtml = '<span>N/A</span>';
      }
      code = this.product.code;
      name = this.product.name;
      qtySize = this.qtySizeHtml();
      if (qtySize == 'N/A') qtySize = this.qty;
      var tQtySize = qtySize;//(allowPopup) ? qtySize.toString().truncate(16) : qtySize;
      var qtySizeRollover = (tQtySize != qtySize) ? ' onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + qtySize + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"' : '';
      qtySizeHtml = '<span id="sizing_field_' + this.id + '" class="' + dropdownHtml + '"' + qtySizeRollover + ((allowPopup) ? ' onclick="orderManager.popupManager.togglePopup(\'item_size\', this, {cpid:' + this.id + '}); return false;"' : '') + '><span>' + tQtySize + '</span><b class="arrow">&nbsp;</b></span>';
      color = this.colorCellHtml(allowPopup);
      qtyTotalHtml = '<span class="' + dropdownHtml + '" id="qty_total_' + this.id + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_size\', $(\'sizing_field_' + this.id + '\'), {cpid:' + this.id + '}); return false;"' : '') + '><span>' + this.qty + '</span><b class="arrow">&nbsp;</b></span>';
      options = this.getOptions();
      var tOptions = (allowPopup) ? options.toString().truncate(16) : options;
      var optionsRollover = (tOptions != options) ? ' onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + options + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"' : '';
      optionsHtml = (options == '') ? '<span class="value">N/A</span>' : '<span id="options_field_' + this.id + '" class="' + dropdownHtml + '"' + optionsRollover + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_options\', this, {cpid:' + this.id + '}); return false;"' : '') + '><span>' + tOptions + '</span><b class="arrow">&nbsp;</b></span>';
      unitPrice = (this.editable == false) ? this.legacyUnitPrice : this.pricing.getOverride().toPrice();
      unitPriceHtml = '<span id="unit_price_field_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_unit_price\', this, {cpid:' + this.id + '}); return false;"' : '') + '><span>' + d.curGlyph + unitPrice + '</span><b class="arrow">&nbsp;</b></span>';
      lineTotal = (unitPrice * this.qty).toPrice();
      lineTotalHtml = '<span id="line_total_field_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_line_total\', this, {cpid:' + this.id + '}); return false;"' : '') + '><span>' + d.curGlyph + lineTotal + '</span><b class="arrow">&nbsp;</b></span>';
      discountAsType = this.getDiscount().toPrice();
      var clazz = getPriceOverridingClazz(this.calculatedDiscountAmount, this.discount, this.totalLocked || this.discountLocked);
      var cg = (this.discountType == AMOUNT_TYPE_FIXED) ? d.curGlyph : '';
      var pc = (this.discountType == AMOUNT_TYPE_PERCENT) ? '%' : '';
      discountHtml = '<span id="discount_field_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_discount\', this, {cpid:' + this.id + '}); return false;"' : '') + '><i class="' + clazz + '">&nbsp;</i><span>' + cg + discountAsType + pc + '</span><b class="arrow">&nbsp;</b></span>';
      
      total = (this.editable == false) ? this.legacyTotal : this.finalTotal.toPrice();
      clazz = (this.discountLocked || this.totalLocked) ? getPriceOverridingClazz(this.total.crp, total, this.total.isOverriden) : '';
      totalHtml = '<span id="total_field_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_total\', this, {cpid:' + this.id + '}); return false;"' : '') + '><i class="' + clazz + '">&nbsp;</i><span>' + d.curGlyph + total + '</span><b class="arrow">&nbsp;</b></span>';
      if (this.isNonDecProduct()) {
        decorationsHtml = '<td colspan="2"><span class="value">N/A</span></td>';
      } else {
        decorations = d.curGlyph + this.pricingData.decorationPrice.getOverride().toPrice();
        if (allowPopup) {
          decorationsHtml = '<td class="text_right not_selectable"><span class="dropdown" onclick="orderManager.currentOrder.loadProductInDesigner(' + this.id + ');" onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + ml('load in designer') + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"><span class="icon designer"></span></span></td>';
        } else {
          decorationsHtml = '';
        }
        decorationsHtml += '<td class="text_right"' + ((allowPopup) ? '' : ' colspan="2"') + '><span id="decorations_field_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'item_decorations\', this, {cpid:' + this.id + '}); return false;" onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + ml('decoration prices') + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"' : '') + '><span>' + decorations + '</span><b class="arrow">&nbsp;</b></span></td>';
      }
    } else if (this.cp_type == CP_NEW) {
      thumbHtml = '<span>N/A</span>';
      code = "";
      name = "";
      qtySizeHtml = '<span class="value">N/A</span>';
      color = '<span class="colorbox"></span>';
      qtyTotalHtml = '<span class="value" id="qty_total_' + this.id + '">0</span>';
      decorationsHtml = '<td colspan="2"><span class="value">N/A</span></td>';
      optionsHtml = '<span class="value">N/A</span>';
      unitPriceHtml = '<span class="value">N/A</span>';
      lineTotalHtml = '<span class="value">N/A</span>';
      discountHtml = '<span class="value">N/A</span>';
      total = 0.0;
      totalHtml = '<span class="value">' + d.curGlyph + total + '</span>';
    } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
      thumbHtml = '<span id="it_cp_thumb_ct_' + this.id + '"><img id="it_cp_thumb_' + this.id + '" style="width:25px; height:25px;" src="' + this.imageURL + '" alt="[ ]" /></span>';
      name = this.getName();
      qtySizeHtml = '<span class="value">N/A</span>';
      color = '<span class="colorbox"></span>';
      qtyTotalHtml = '<span class="value" id="qty_total_' + this.id + '">' + this.qty + '</span>';
      decorationsHtml = '<td colspan="2"><span class="value">N/A</span></td>';
      optionsHtml = '<span class="value">N/A</span>';
      unitPriceHtml = '<span class="value">' + d.curGlyph + this.price + '</span>';
      lineTotalHtml = '<span class="value">' + d.curGlyph + this.price * this.qty + '</span>';
      discountHtml = '<span class="value">N/A</span>';
      total = this.price * this.qty;
      totalHtml = '<span class="value">' + d.curGlyph + total + '</span>';
    } else return '';
    
    var html = '';
    if (!hideTr) html += '<tr id="cp_' + this.id + '" class="line_item ' + altHtml + selectedHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.currentOrder.setCurrentConfiguredProduct(' + this.id + ');"' : '') + '>';
    if (incTick) {
      html += '<td><input type="checkbox" name="line_item_tick_' + this.id + '" value="' + this.id + '"' + ((ticked) ? ' checked="checked"' : '') + ((tickReadOnly) ? ' disabled="disabled"' : '') + ' />';
      if (tickReadOnly) html += '<input type="hidden" name="line_item_tick_' + this.id + '" value="' + this.id + '" />';
      html += '</td>';
    }
    html += posHtml +
        '<td class="thumbnail">' +
            thumbHtml +
        '</td>' +
        '<!--name-->' +
        '<td class="wrapless">' +
          '';
          
    if (allowPopup && !this.isNonDecProduct()) {
      html +=
      '<span id="name_field_' + this.id + '" class="dropdown input" onclick="orderManager.popupManager.togglePopup(\'item_name\', this, {cpid:' + this.id + '}); return false;" ' + ((name.length > 16) ? ' onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + name + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"' : '') + '>' +
              '<input id="name_field_input_' + this.id + '" class="w_110" type="text" value="' + name + '" /><b class="arrow">&nbsp;</b>' +
            '</span>';
    } else {
      html += '<span class="value">' + name + '</span>';
    }
            
    html +=
          '' +
        '</td>' +
        '<!-- color-->' +
        '<td>' +
          '<div id="cp_colorbox_' + this.id + '" >' +
            color +
          '</div>' +
        '</td>' +
        '<!--sizing qty-->' +
        '<td class="wrapless">' +
          '' +
            qtySizeHtml +
          '' +
        '</td>' +
        '<!--decorations-->' +
            decorationsHtml +
        '<!--options-->' +
        '<td ' + ((allowPopup) ? '' : 'class="wrapless"') + '>' +
          '' +
            optionsHtml +
          '' +
        '</td>' +
        '<!--unit price-->' +
        '<td class="text_right">' +
          '' +
            unitPriceHtml +
          '' +
        '</td>' +
        '<!--total-->' +
        '<td class="text_right">' +
          qtyTotalHtml +
        '</td>' +
        '<!--line total-->' +
        '<td class="text_right">' +
          '' +
            lineTotalHtml +
          '' +
        '</td>' +
        '<!--discount-->' +
        '<td class="text_right">' +
          '' +
              discountHtml +
          '' +
        '</td>';
        
    if (allowPopup) {
      html +=
        '<td class="text_right">' +
          '' + totalHtml +''+
        '</td><td>' +
           '<a href="#" id="cp_controls_' + this.id + '" class="controls popup_button" onclick="orderManager.popupManager.togglePopup(\'item_controls\', this, {cpid:' + this.id + '}); return false;"><span>controls</span></a>' +
        '</td>';
    } else {
      html +=
        '<td class="text_right">' +
          totalHtml +
        '</td>';
    }
    
    if (!hideTr) html += '</tr>';
      
    return html;
  },
  
  extraRowHtml: function CP_extraRowHtml(alt, selected, mode) {
    var name, descriptionHtml, qtyHtml, unitPriceHtml, discountHtml, total, associatesHtml = '';
    var altHtml = alt ? 'alt' : '';
    var selectedHtml = selected ? ' current' : '';
    var html = '';
    var dropdownHtml = (mode != MODE_VIEW) ? 'dropdown' : '';
    var allowPopup = (mode != MODE_VIEW);
    
    if (this.cp_type == CP_DIGITIZATION || this.cp_type == CP_EXTRA_CHARGE) {
      name = this.getName();
      if (this.associatedItems.size() > 0) {
        var itemNos = [];
        this.associatedItems.each(function(item) {
            var pos = d.cart.getItemPosition(item);
            if (pos != -1) itemNos.push(pos);
        });
        itemNos.sort();
        
        if (allowPopup) {
          associatesHtml = '<span class="associates"><span class="value">' + itemNos.join(',') + '</span></span>';
        } else {
          name += ' (line item ' + itemNos.join(',') + ')';
        }
      }
      
      var up = this.getUnitPrice();
      var clazz = getPriceOverridingClazz(this.pricing.rrp, this.pricing.getOverride(), this.pricing.isOverriden);
      unitPriceHtml = '<span id="extra_unit_price_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'extra_unit_price\', this, {cpid:' + this.id + '}); return false;"' : '') + '><i class="' + clazz + '">&nbsp;</i><span>' + d.curGlyph + up.toPrice() + '</span><b class="arrow">&nbsp;</b></span>';
      
      if (allowPopup) {
        qtyHtml = '<span class="input"><input class="w_15 text_right" type="text" value="' + this.qty + '" onkeyup="orderManager.currentOrder.onExtraQtyChange(this);" onblur="orderManager.currentOrder.resetItems();" /></span>';
      } else {
        qtyHtml = '<span class="value">' + this.qty + '</span>';
      }
      var discountAsType = this.getDiscount().toPrice();
      clazz = getPriceOverridingClazz(this.calculatedDiscountAmount, this.discount, this.discountLocked);
      var cg = (this.discountType == AMOUNT_TYPE_FIXED) ? d.curGlyph : '';
      var pc = (this.discountType == AMOUNT_TYPE_PERCENT) ? '%' : '';
      discountHtml = '<span id="extra_discount_field_' + this.id + '" class="' + dropdownHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'extra_discount\', this, {cpid:' + this.id + '}); return false;"' : '') + '><i class="' + clazz + '">&nbsp;</i><span>' + cg + discountAsType + pc + '</span><b class="arrow">&nbsp;</b></span>';
      total = this.getPrice().toPrice();
    } else if (this.cp_type == CP_NEW_EXTRA) {
      name = ml("select extra charge");
      qtyHtml = '<span class="value">N/A</span>';
      unitPriceHtml = '<span class="value">N/A</span>';
      discountHtml = '<span class="value">N/A</span>';
      total = 0.0;
    } else return '';
    
    html +=
    '<tr id="cp_' + this.id + '" class="extra_item ' + altHtml + selectedHtml + '" ' + ((allowPopup) ? 'onclick="orderManager.currentOrder.setCurrentExtra(' + this.id + ');"' : '') + '>' +
      '<td colspan="5" class="empty not_selectable">&nbsp;</td><td colspan="2" class="not_selectable empty text_right">'+associatesHtml +'</td><td colspan="2" class="text_left">';
        
    if (allowPopup) {
      html +=
      '<div class="has_associates">' + 
      '<span class="dropdown input" id="extra_name_field_' + this.id + '" ' + ((allowPopup) ? 'onclick="orderManager.popupManager.togglePopup(\'extra_name\', this, {cpid:' + this.id + '}); return false;"' + ((name.length > 16) ? ' onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + name + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"' : '') : '') + '>' +
          '<input id="extra_name_field_input_' + this.id + '" class="w_110" type="text" value="' + name + '" /></span>' +
        '</span>' +
      '</div>';
    } else {
      html += '<span class="value">' + name + '</span>';
    }
    
    html +=
      '</td>' +
      '<td>' + qtyHtml + '</td>' +
      '<td class="text_right">' + unitPriceHtml + '</td>' +
      '<td class="text_right">' + discountHtml + '</td>' +
      '<td class="text_right not_selectable">';
		
		if (allowPopup) {
      html +=
          '<div class="controls"><span id="ec_total_' + this.id + '" class="value">' + d.curGlyph + total + '</span></div>' +
          '</td><td>'+
           '<a href="#" id="cp_controls_' + this.id + '" class="controls popup_button" onclick="orderManager.popupManager.togglePopup(\'extra_controls\', this, {cpid:' + this.id + '}); return false;"><span>controls</span></a></div>';
    } else {
      html +=
          '<span class="value">' + d.curGlyph + total + '</span>';
    }
    
    html +=
      '</td>' +
    '</tr>';
    
    return html;
  },
  
  thumbNailPopupHtml: function() {
    var html = "<ul class='pop_thumb'>";
    for(var i=0;i < this.product.views.list.size(); i++) {
      var pView = this.product.views.list[i];
      var cView = this.getView(pView.id, false);
      if((cView != null)&&(cView.isUsed())) {
        var url = cView.getViewURL(3, true, false);
        html += '<li><span><img src="' + url + '"/></span><ul>';
        for(var ak in cView.areas) {
          var cArea = cView.areas[ak]; 
          if(cArea.isUsed()) {
            for(var ik in cArea.allItems) {
              var item = cArea.allItems[ik];
              var src = item.getSrc();
              var dims = item.fitToSize(50);
              html += '<li><img src="' + src + '" width="' + dims.w + '" height="' + dims.h + '" /></li>';
            }
          }
        }
        html += '</ul></li>';
      }
    }
    html += '</ul>';
    return html;
  },
  
  productionRowHtml: function CP_productionRowHtml(alt, cart) {
    var html = '';
    if (this.product) {
      var altHtml = alt ? 'alt' : '';
      var id = (this.id > 0) ? this.id : 'N/A';
      var options = this.getOptions();
      var optionsHtml = (options == '') ? '<td><span class="value">N/A</span></td>' : '<td class="text_left"><span class="value">' + options + '</span></td>';
      var qtySize = this.qtySizeHtml();
      var digStatus = (this.digStatus) ? '<td class="text_left"><span class="value">' + this.digStatus + '</span></td>' : '<td><span class="value">N/A</span></td>';
      var production = (this.processedDate) ? 'complete, ' + this.processedDate + ', by ' + this.processedBy : 'not complete';
      var shipping = (this.shippedDate) ? 'complete, ' + this.shippedDate + ', by ' + this.shippedBy : 'not complete';
      html +=
        '<tr class="' + altHtml + '">' +
          '<td>' + id + '</td>' +
          optionsHtml +
          '<td class="text_left"><span class="value">' + qtySize + '</span></td>' +
          digStatus +
          '<td><span class="value">' + production + '</span></td>' +
          '<td><span class="value">' + shipping + '</span></td>' +
        '</tr>';
    }
    return html;
  },
  
  qtySizeHtml: function CP_qtySizeHtml() {
    if (this.product) {
      var sizeField = this.product.type.sizeField;
      if (this.product.getSizeField()) {
        return sizeField.renderMultiQtyTextForOMS(this);
      }
    }
    return "N/A";
  },
  
  colorCellHtml: function CP_colorCellHtml(allowPopup) {
    var html = '';
    var color = this.getSelectedColor();
    if (color) {
      html = color.cellHtml(allowPopup, false, this, 25);
    } else {
      html = '<span class="colorbox"></span>';
    }
    return html;
  },
  
  cartRowHtml: function CP_cartRowHtml() {
    var html = '';
    if (this.product) {
      html +=
      '<tr id="cart_' + this.id + '" class="cp_details">' +
        '<td width="50"><img src="' + this.imageURL + '"></td>' +
        '<td class="options">' +
          '<a href="#" onclick="d.selectConfiguredProduct(' + this.id + ', true, false); d.autoCheckout=false; return false;" class="title">' + this.product.name + '</a>' +
          '<br />' +
          this.getOptions(true) +
        '</td>' +
        '<td>' + this.qty + '</td>' +
        '<td class="designer_price">' +
          '<strong>' + this.getPrice().toPrice() + '</strong>' +
        '</td>' +
      '</tr>' + 
      '<tr id="cart_options_' + this.id + '" class="cp_options">' +
      '</tr>';
    } else if (this.cp_type == CP_DIGITIZATION) {
      html +=
      '<tr id="cart_' + this.id + '" class="cp_details">' +
        '<td width="50"><img src="' + this.imageURL + '"></td>' +
        '<td class="options">' +
          this.getName() +
          '<br />' +
        '</td>' +
        '<td>' + this.qty + '</td>' +
        '<td class="designer_price">' +
          '<strong>' + this.getPrice().toPrice() + '</strong>' +
        '</td>' +
      '</tr>' + 
      '<tr id="cart_options_' + this.id + '" class="cp_options">' +
      '</tr>';
    }
    return html;
  },
  
  getOptions: function CP_getOptions(isHtml) {
    var html = '';
    var htmlSeparator = '';
    var text = '';
    var textSeparator = '';
    
    if (this.product) {
      var type = this.product.type;
      for(var i=0;i< type.customFields.list.length;i++) {
        var field = type.customFields.list[i];
        
        var productField = this.product.fields.byId[field.id];
        if((productField == null)||(!productField.valid)||(!productField.used)) { //this product does not use the field...
          log("Hiding field " + this.name + ": productField follows:");
          log(productField);
        } else {
          var cField = this.cFields[field.id];
          if(cField != null) {
            text += textSeparator + field.name + ': ' + cField.getValue('');
            html += htmlSeparator + '<label>' + field.name + ':</label><span>' + cField.getValue('') + '</span>';
            if (textSeparator == '') textSeparator = ', ';
            if (htmlSeparator == '') htmlSeparator = '<br />';
          }
        }
      }
    }
    
    if (isHtml) return html;
    else return text;
  },
  
  setExtraCharge: function CP_setExtraCharge(extra, name, description, unitPrice) {
    if (extra) {
      this.extraCharge = extra;
      this.cp_type = CP_EXTRA_CHARGE;
      this.name = extra.name;
      this.description = extra.description;
      this.price = extra.unit_price;
      this.overridingPrice = extra.unit_price;
      this.isAutoPrice = true;
    } else {
      this.extraCharge = null;
      this.cp_type = CP_EXTRA_CHARGE;
      this.name = name;
      this.description = description;
      this.price = unitPrice;
      this.overridingPrice = unitPrice;
      this.isAutoPrice = false;
    }
  },
  
  setTotalPrice: function CP_setTotalPrice(price, lock) {
    this.overridingTotal = price;
    this.totalLocked = lock;
  },
  
  setDiscount: function CP_setDiscount(discountType, discount, lock) {
    this.discountType = discountType;
    this.typedOverridingDiscount = discount.round();
    if (discountType == AMOUNT_TYPE_PERCENT) this.typedOverridingDiscount = this.typedOverridingDiscount / 100.0;
    this.discountLocked = lock;
  }
  
});

var nextConfiguredProductId = -2;

//a place to store selction info about a field.. handles multiselect
var ConfiguredField = Class.create({
  CLASSDEF: {
      name: 'ConfiguredField'
  },
  
  initialize: function CPF_initialize(id, configuredProduct, options) {
    this.id = id;
    this.configuredProduct = configuredProduct;
    this.options = {}; //hash of ConfiguredFieldOption objects
    
    this.usePriceDefaults = options.uDef;
    
    this.multiSelect = false;
    this.multiOption = null;
    this.productField = this.configuredProduct.product.fields.byId[this.id];
    this.fieldDef = this.productField.fieldDef;
    if(this.fieldDef==null) {
      return;
    }
    this.fieldValue = null;
    if(options != null) {
      if(options.val != null) {
        if(this.fieldDef.typeOptions.date) {
          try {
            this.fieldValue = new Date(options.val);
          } catch(e) {
            log("Error parsing date '" + options.val + "' when loading " + this.fieldDef.name);
          }
        } else {
          this.fieldValue = options.val;
        }
      }
    }
   
      
  },
  
  
  addSelectedOption: function CPF_addSelectedOption(optionId, qty, subOptionId, unitPrice, isAutoPrice, legacyKey) {
    var opt = this.options[optionId];
    var subQty = 0;
    if(subOptionId != null) {
      subQty = qty;
      qty = 1;
    }
    if(opt == null) {
      opt = new ConfiguredFieldOption(this, optionId, qty, unitPrice, isAutoPrice, legacyKey);
      if((opt.def == null)||(opt.pDef == null)) {
        log("Dropping option " + optionId + " as it has no def");
        return null;
      }
      this.options[optionId] = opt;
    } else {
      opt.qty = qty;
      if (unitPrice != null) {
        opt.unitPrice = unitPrice;
        opt.isAutoPrice = isAutoPrice;
      }
    }
    
    if(opt.def.isMulti) {
      this.multiSelect = true;
      this.multiOption = opt;
    } else if(subOptionId != null) {
      opt.addSelectedOption(subOptionId, subQty, unitPrice, isAutoPrice, legacyKey);
    } else {
      //should we select the default sub option?
      if(opt.pDef.subs != null) {
        if(this.fieldDef.typeOptions.singleSelect) {
          opt.initToDefault(true);
        }
      }
      
    }
    return opt;
  },
  
  //used when qty get set to 0 on sub option: remove the option...
  cleanEmptyOptions: function CPF_cleanEmptyOptions() {
    for(var k in this.options) {
      var opt = this.options[k];
      if((opt.qty == 0)||( (opt.pDef.subs != null) && (opt.subQty == 0)) ) {
        delete this.options[k];
      }
    }
  },
  
  initToDefault: function CPF_initToDefault() {
    if(this.fieldDef.typeOptions.list) {
      if(hashFirstElement(this.options) == null) { //no selected option...
        log("Initializing " + this.fieldDef.name + " to default");
        for(var i=0; i < this.productField.options.list.length; i++) {
          var pOpt = this.productField.options.list[i];
          if(pOpt.selected) {
            log("Adding default option " + pOpt.def.name);
            var opt = this.addSelectedOption(pOpt.id, 1);
            opt.initToDefault();
          }
        }
      }
      //check if this field is only is multi select mode...
      if(this.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE && this.fieldDef.productType.ms && !this.multiSelect && !this.configuredProduct.product.usesMinQty()) {
        log("Adding multiselect option because field is in multi only mode");
        this.addSelectedOption(this.fieldDef.multiOption.id, 1, null); //add the multi option...
      }
    }
  },
  
  selectedOptionQty: function CPF_selectedOptionQty(optionId, subOptionId) {
    var qo = this.options[optionId];
    if(qo == null) return 0;
    if(subOptionId != null) {
      var so = qo.subOptions[subOptionId];
      if(so==null) return 0;
      return so.qty;
    } else {
      return qo.qty;
    }
  },
  
  getMainOption: function CPF_getMainOption(useDefault, allowMultiSelect) {
    if(useDefault == true && hashFirstElement(this.options) == null) {
      if(this.productField.defaultOption == null) {
        return null;
      }
      this.addSelectedOption(this.productField.defaultOption.id, 1);
    }
    if((this.multiSelect)&&(allowMultiSelect != false)) {
      return this.multiOption;
    } else {
      for(var k in this.options) {
        return this.options[k]; //just return the first option
      }
      //no option selected... lets add the default
      if (useDefault == true) {
        return this.addSelectedOption(this.productField.defaultOption.id, 1);
        //return this.productField.defaultOption;
      }
      return null;
    }
  },
  
  
  //called when user changes field value from interface
  setValue: function CPF_setValue(value, forOMS) {
    if(this.fieldDef.typeOptions.list) { //the value is an option....
      if(value == null && this.fieldDef.fieldType == FIELD_TYPE_LIST_MULTISELECT) {
        //multi values from the form field
        this.options = {}; //reset as we will reload all from el
        this.multiSelect = false;
        this.multiOption = null;
    
        var el = $(this.iId('value'));
        if(el != null) {
          for(var i=0; i < el.length; i++) {
            if(el.options[i].selected) {
              var ids = this.extractOptIds(el.options[i].value);
              this.addSelectedOption(ids[0], 1, ids[1]);
            }
          }
        }
      } else {
        this.setListValue(value, forOMS);
        if(value.indexOf("-") == -1) { //not a sub option...
          var sizeColorCombinations = null;
          if(this.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE && this.configuredProduct.product.limitSizeColors) {
            sizeColorCombinations = this.configuredProduct.product.sizeColorCombinations;
          }
          $(this.iId('sub_value_container', false)).update(this.fieldDef.renderSubSelect(this.configuredProduct, this.productField, this, sizeColorCombinations, forOMS));
        }
      }
      if(this.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE && this.configuredProduct.product.limitSizeColors) {
        //we need to rerender the color list as the size selection could have changed available colors..
        this.configuredProduct.product.type.buildColorPanel(this.configuredProduct);
      }
    } else if(this.fieldDef.fieldType == FIELD_TYPE_TEXT_BOX || this.fieldDef.fieldType == FIELD_TYPE_TEXT_AREA)  {
      this.fieldValue = value;
    } else if(this.fieldDef.fieldType == FIELD_TYPE_FILE || this.fieldDef.fieldType == FIELD_TYPE_IMAGE)  {
      //check that that file is valid
      var el = $(this.iId('value'));
      if(!this.validFile(el.value)) {
        alert(ml("You can only upload files of type: %s", this.fieldDef.customOption("extensions", '')));
        el.value = "";
        return;
      }
      //we need to submit the form field after popping up the upload status bar...
      startAsyncAction(); 
      startTrackingUpload("CF"); 
      
      $("field_upload_form" + this.fieldDef.id).submit();
      popup("field_upload_popup");
    } else if((this.fieldDef.fieldType == FIELD_TYPE_DATE)||(this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME)) {
      this.fieldValue = this.loadDateFromInputs(true, (this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME));
    } else if(this.fieldDef.fieldType == FIELD_TYPE_TIME) {
      this.fieldValue = this.loadDateFromInputs(false, true);
    }
    
    if (!forOMS) {
      d.track("change-" + this.fieldDef.name);
      this.configuredProduct.product.type.updatePrice();
      d.itemChanged();
    }
  },
  
  loadDateFromInputs: function CPF_loadDateFromInputs(do_date, do_time) {
    var date = new Date();
    var selVal = null;
    try {
      if(do_date) {
        //month
        selVal = $FV(this.iId("date_1"));
        if(selVal == null || selVal == "") return null;
        date.setMonth(selVal);
        //day
        selVal = $FV(this.iId("date_0"));
        if(selVal == null || selVal == "" || isNaN(selVal)) return null;
        var iVal = parseInt(selVal, 10);
        if(iVal < 1 || iVal > 31) return null;
        date.setDate(iVal);
        //year
        selVal = $FV(this.iId("date_2"));
        if(selVal == null || selVal == "") return null;
        date.setFullYear(selVal);
      }
      if(do_time) {
        var timeFormat = parseInt(this.customOption("time_format", '0'), 10);
        //hour
        var timeMod = 0;
        if(timeFormat == 0) {
          timeMod = ($FV(this.iId("date_5")) == "AM")? 0 : 12;
          selVal = $FV(this.iId("date_3"));
        } else {
          selVal = $FV(this.iId("date_6"));
        }
        if(selVal == null || selVal == "" || isNaN(selVal)) return null;
        var iVal = parseInt(selVal, 10);
        if(iVal < 0 || iVal > 23) return null;
        date.setHours(iVal + timeMod);
        
        //minute
        selVal = $FV(this.iId("date_4"));
        if(selVal == null || selVal == "" || isNaN(selVal)) return null;
        var iVal = parseInt(selVal, 10);
        if(iVal < 0 || iVal > 59) return null;
        date.setMinutes(iVal);
      }
      
    } catch(e) {
      log("Exception when loading date:" + e.getMessage());
      return null;
    }
    return date;
  },
  
  validFile: function CPF_validFile(filename) {
    var ext = this.fieldDef.customOption("extensions", null);
    if((ext == null)||(ext.length==0)) return true;
    var exts = ext.split(' ');
    if(exts.length == 0) return true;
    for(var i=0; i < exts.length; i++) {
      var ext = exts[i];
      var extLength = ext.length;
      if((filename.length > extLength + 1)&&(filename[filename.length - extLength - 1] == '.')) {
        var fileTest = filename.substr(filename.length - extLength, extLength);
        if(fileTest.toLowerCase() == ext.toLowerCase()) {
          return true;
        }
      }
    }
    return false;
  },
  
  uploadFinished: function CPF_uploadFinished(fileData) {
    closePopup("field_upload_popup");
    if(fileData.error != null) {
      alert(fileData.error);
      return;
    }
    this.fieldValue = fileData;
    this.fieldValue.isWorkingVersion = true;
    this.fieldDef.setField(this.configuredProduct); //rerender the field so it now shows the uploaded file...

    this.configuredProduct.product.type.updatePrice();

  },
  
  removeFile: function CPF_removeFile() {
    if(confirm(ml("Are you sure you want to remove this file?"))) {
      this.fieldValue = null;
      this.fieldDef.setField(this.configuredProduct); //rerender the field 
      this.configuredProduct.product.type.updatePrice();
    }
  },
  
  extractOptIds: function CPF_extractOptIds(value) {
    var optionId = value;
    var subOptionId = null;
    if(value.indexOf("-") != -1) { //its an option-suboption pair
      var vals = value.split('-');
      optionId = parseInt(vals[0], 10);
      subOptionId = parseInt(vals[1], 10);
      log("Identified sub option selected: " + optionId + " : "  + subOptionId);
    }
    return [optionId,subOptionId];
  },
  
  //called internally from setValue
  setListValue: function CPF_setListValue(value, forOMS) {
    
    var ids = this.extractOptIds(value);
    var optionId = ids[0];
    var subOptionId = ids[1];
    
    var oldValue = this.getMainOption(true);
    
    this.setSelectedOption(optionId, 1, subOptionId, forOMS);
    
    if(this.fieldDef.hasMulti) {
      if(this.multiSelect) { //we are now choosing a multiple select field... get the old option as make it one of the selected values
        if(oldValue != null) {
          log("Moving To MultiOption from " + oldValue.def.name + " with qty " + this.configuredProduct.qty);
          if(oldValue.subQty > 0) { //sub option was selected...
            var sub = hashFirstElement(oldValue.subOptions);
            log("Using sub option " + sub.def.name);
            this.addSelectedOption(oldValue.optionId, this.configuredProduct.qty, sub.subOptionId);
          } else {
            this.addSelectedOption(oldValue.optionId, this.configuredProduct.qty);
          }
        } else {
          log("Selected Multi Option");
        }
        this.updateMultiQty();
        this.fieldDef.showMultiQtyOptions(this);
      } else {
        log("Hiding Multi Option From CField.setValue");
        this.fieldDef.hideMultiQtyOptions(this);
      }
    } else {
      log("Selected option of field without multi option");
    }
  },
  
  updateMultiQty: function CPF_updateMultiQty() {
    for(var i=0; i < this.productField.options.list.length; i++) {
      var pOpt = this.productField.options.list[i];
      if(!pOpt.def.isMulti) {
        if(pOpt.subs != null) {
          for(var k in pOpt.subs) {
            var pSub = pOpt.subs[k];
            var el = $('mqs_' + pSub.id);
            if(el != null) {
              el.value = this.selectedOptionQty(pOpt.id, pSub.id);
            } else {
              log("Missing multi qty el for sub option " + pSub.id);
            }
          }
        } else {
          var el = $('mq_' + pOpt.id);
          if(el != null) {
            el.value = this.selectedOptionQty(pOpt.id);
          } else {
            log("Missing multi qty el for option " + pOpt.id);
          }
        }
      }
    }
  },
  
  selectDate: function PTF_selectDate(allowTime) {
    var self = this;
    var c = new CalendarDateSelect( 'field_date_value_' + this.id, {
        popup_by: 'field_date_' + this.id,
        time: allowTime,
        after_close: function() {
          self.updateDate();
          
        }
    });
  },
  
  updateDate: function(date) {
    this.fieldValue = new Date($('field_date_value_' + this.fieldDef.id).value);
    this.fieldDef.updateDate(this);
  },
  
  getValue: function CPF_getValue(defaultValue) {
    if(this.fieldDef.typeOptions.list) {
      opts = this.getSelectedOptions();
      var result = '';
      opts.each(function(opt) {
          result += opt.def.name + ' ';
      });
      return result;
    } else {
      if(this.fieldValue == null) {
        return defaultValue;
      }
      return this.fieldValue;
    }
  },
  
  //called when the field choice changes...
  //called when saving teamnames to init into mult mode...
  setSelectedOption: function CPF_setSelectedOption(optionId, qty, subOptionId, skipRecalculations) {
    //first reset the state
    this.options = {}; //hash of ProductFieldChoice objects
    this.multiSelect = false;
    this.multiOption = null;
    //then add option
    var retVal = this.addSelectedOption(optionId, qty, subOptionId);
    if(skipRecalculations != true) {
      d.track("change-" + this.fieldDef.name);
      this.configuredProduct.product.type.updatePrice();
      d.itemChanged();
    }
    return retVal;
  },
  
  //called when a checkbox/radio button is selected...
  selectValue: function CPF_selectValue(on, optionId, subOptionId) {
    log("selectValue: on=" + on + " optionId=" + optionId + " subOptionId=" + subOptionId);
    if(this.fieldDef.fieldType == FIELD_TYPE_LIST_RADIO) {//single select...
      //hide any sub option containers if needed...
      var needsShowing = true;
      for(var k in this.options) {
        var opt = this.options[k];
        if((opt.optionId != optionId)&&(opt.pDef.subs != null)) {
          var el = $("subs_" + opt.optionId);
          if(el != null) {
            el.hide();
          } else {
            log("Error: unable to get sub container for option " + opt.optionId);
          }
        } else if(opt.optionId == optionId) {
          //already showing..
          needsShowing = false;
        }
      }
      var opt = this.setSelectedOption(optionId, 1, subOptionId);
      opt.initToDefault(true, true);
      if(needsShowing) {
        var el = $("subs_" + optionId);
        if(el != null) el.show();
      }
      
    } else { //checkbox...
      if(on) {
        var opt = this.addSelectedOption(optionId, 1, subOptionId); 
        if(opt.pDef.subs != null) {
          opt.initToDefault(false, true);
          var el = $("subs_" + optionId);
          if(el != null) el.show();
        }
      } else {
        var opt = this.options[optionId];
        if(opt != null) {
          if(subOptionId != null) {
            var sOpt = opt.subOptions[subOptionId];
            delete opt.subOptions[subOptionId];
          } else {
            if(opt.pDef.subs != null) {
              var el = $("subs_" + optionId);
              if(el != null) {
                el.hide();
              } else {
                log("Error: unable to get sub container for option " + optionId);
              }
            }
            delete this.options[optionId];
          }
        } else {
          log("Error: option was not already selected " + optionId);
        }
        
      }
      
    }
    d.track("change-" + this.fieldDef.name);
    this.configuredProduct.product.type.updatePrice();
    d.itemChanged();
  },
  
  //when changing products try and tranfer accross the old selection...
  initFromPreviousSelection: function CPF_initFromPreviousSelection(old) {
    if(this.fieldDef.typeOptions.text) {
      this.fieldValue = old.fieldValue;
    } else if(this.fieldDef.typeOptions.file) {
      if(old.id == this.id) {
        this.fieldValue = old.fieldValue;
        log("Copying file field (ids are same)");
      } else {
        log("Not copying file field (" + this.id + "!=" + old.id + ")"); 
      }
    } else if(this.fieldDef.typeOptions.date) {
      this.fieldValue = old.fieldValue;
    } else if(this.fieldDef.typeOptions.list) {
      if((this.fieldDef.hasMulti)&&(old.multiSelect)) {
        log("initFromPreviousSelection: multi select chosen");
        if(this.productField.multiOption == null) {
          log("ERROR: this.fieldDef.hasMulti==true yet this.productField.multiOption is null");
        } else {
          this.setSelectedOption(this.productField.multiOption.id, 1, null, true);
          //go thru all the options this product supports
          var usedOpts = {};
          var usedSOpts = {};
          var foundMatched = false;
          usedOpts[this.productField.multiOption.id]=true;
          for(var k in old.options) {
            var cOpt = old.options[k];
            var opt = this.productField.findOption(cOpt.pDef);
            if(opt != null && usedOpts[opt.id] == null) {
              usedOpts[opt.id]=true; //we dont want to add some option twice (matched on different name/id/value)
              log("Found common option " + opt.def.name);
              foundMatched = true;
              var foundSubOptions = false;
              for(var j in cOpt.subOptions) {
                var cSOpt = cOpt.subOptions[j];
                var sOpt = opt.findOption(cSOpt.pDef);
                if((sOpt != null)&&(usedSOpts[sOpt.id] == null)) {
                  usedSOpts[sOpt.id] = true;
                  this.addSelectedOption(opt.id, cSOpt.qty, sOpt.id);
                  log("Found common sub option " + sOpt.def.name);
                  foundSubOptions=true;
                }
              }
              if(!foundSubOptions) {
                log("No Common Sub Options, using qty:" + cOpt.qty);
                this.addSelectedOption(opt.id, cOpt.qty);
              }
            }
          }
          if(!foundMatched) {
            log("No Common Options, using default option with qty:" + this.configuredProduct.qty);
            for(var i=0; i < this.productField.options.list.length; i++) {
              var pOpt = this.productField.options.list[i];
              if((pOpt.selected)&&(!pOpt.def.isMulti)) {
                log("Adding default option " + pOpt.def.name);
                var opt = this.addSelectedOption(pOpt.id, this.configuredProduct.qty);
                opt.initToDefault();
                break;
              }
            }
          }
        }
      } else {
        var cOpt = old.getMainOption(true, false); //we dont want the multi option...
        if(cOpt != null) {
          var opt = this.productField.findOption(cOpt.pDef);
          if(opt != null) { //this product supports this option..
            var usingSubOpt = false;
            var cSOpt = cOpt.mainSubOption();
            if(cSOpt != null) {
              log("initFromPreviousSelection: Sub Option Previously Selected");
              var sOpt = opt.findOption(cSOpt.pDef);
              if(sOpt != null) {
                usingSubOpt = true;
                this.addSelectedOption(opt.id, 1, sOpt.id);
                log("initFromPreviousSelection: Found common sub option " + sOpt.def.name);
              }
            }
            if(!usingSubOpt) {
              this.setSelectedOption(opt.id, 1, null, true);
              log("initFromPreviousSelection: set common option " + opt.def.name);
            }
          }
        }
      }
    }
  },
  
  serialize: function CPF_serialize(queryComponents, prefix) {
    
    if(this.fieldDef.typeOptions.list) { //the value is an option....
      for(var k in this.options) {
        var cFieldOption = this.options[k];
        cFieldOption.serialize(queryComponents, prefix + "[o][" + cFieldOption.def.id + "]");
      }
    } else if(this.isUsed()) {
      if(this.fieldDef.fieldType == FIELD_TYPE_TEXT_BOX || this.fieldDef.fieldType == FIELD_TYPE_TEXT_AREA)  {
        queryComponents.push(encodeURIComponent(prefix + "[txt]") + "=" + encodeURIComponent(this.fieldValue));
      } else if(this.fieldDef.fieldType == FIELD_TYPE_FILE || this.fieldDef.fieldType == FIELD_TYPE_IMAGE)  {
        var fileSaveType = this.fieldValue.isWorkingVersion ? "2" : "1"; //track if the server should use the working version of the saved version...
        queryComponents.push(encodeURIComponent(prefix + "[file]") + "=" + encodeURIComponent(fileSaveType)); 
      } else if((this.fieldDef.fieldType == FIELD_TYPE_DATE)||(this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME)||(this.fieldDef.fieldType == FIELD_TYPE_TIME)) {
        if((this.fieldDef.fieldType == FIELD_TYPE_DATE)||(this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME)) {
          queryComponents.push(encodeURIComponent(prefix + "[y]") + "=" + encodeURIComponent(this.fieldValue.getFullYear()));
          queryComponents.push(encodeURIComponent(prefix + "[m]") + "=" + encodeURIComponent(this.fieldValue.getMonth()));
          queryComponents.push(encodeURIComponent(prefix + "[d]") + "=" + encodeURIComponent(this.fieldValue.getDate()));
        }
        if((this.fieldDef.fieldType == FIELD_TYPE_TIME)||(this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME)) {
          queryComponents.push(encodeURIComponent(prefix + "[h]") + "=" + encodeURIComponent(this.fieldValue.getHours()));
          queryComponents.push(encodeURIComponent(prefix + "[M]") + "=" + encodeURIComponent(this.fieldValue.getMinutes()));
        }
      }
    }
    
    //log(this);
    
   //if(this.multiOption != null) {
   //  queryComponents.push(encodeURIComponent(prefix + "[o][" + this.multiOption.pcoid + "]") + "=" + encodeURIComponent(1));
   //}
  },
  
  iId: function CPF_iId(inputField, includeAttr) {
    if(includeAttr) {
      return ' id="' + this.iId(inputField, false) + '"';
    } else {
      return 'f' + inputField + '_' + this.id;
    }
  },
  
  objectRef: function CPF_objectRef(forOMS) {
    if (forOMS) return 'orderManager.currentOrder.currentConfiguredProduct.cFields[' + this.id + ']';
    else return 'd.currentCProduct.cFields[' + this.id + ']';
  },
  
  getSelectedOptions: function CPF_getSelectedOptions(includeSubs) {
    var opts = [];
    for(var k in this.options) {
      var cOpt = this.options[k];
      if(!cOpt.def.isMulti) {
        cOpt.appendSelectedOptions(opts, includeSubs);
      }
    }
    return opts;
  },
  
  isUsed: function CPF_isUsed() {
    if(this.fieldDef.typeOptions.list) {
      return this.getSelectedOptions().length > 0;
    } else {
      if(this.fieldValue == null || this.fieldValue == "") {
        return false;
      }
    }
    return true;
  },
  
  refreshHtml: function() {
    this.fieldDef.setField(this.configuredProduct);
  },
  
  getName: function() {
    return this.configuredProduct.getName() + ":" + this.fieldDef.name;
  }
});


var ConfiguredFieldOption = Class.create({
  CLASSDEF: {
      name: 'ConfiguredFieldOption'
  },
  
  initialize: function CPFO_initialize(field, optionId, qty, unitPrice, isAutoPrice, legacyKey) {
    this.field = field;
    this.optionId = optionId;
    this.qty = qty;
    this.unitPrice = unitPrice;
    this.isAutoPrice = (typeof(isAutoPrice) === 'undefined' || isAutoPrice === null) ? true : isAutoPrice;
    this.legacyKey = legacyKey;
    this.pDef = field.productField.options.byId[optionId];
    this.def = field.fieldDef.options.byId[optionId];
    this.subOptions = {};
    this.subQty = 0;
  },
  
  initToDefault: function CPFO_initToDefault(mustSelect, selectElements) {
    if(hashFirstElement(this.subOptions) == null) { //no selected option...
      if(this.pDef.subs != null) {
        var anyDefault = null;
        var foundDefault = false;
        for(var k in this.pDef.subs) {
          var sub = this.pDef.subs[k];
          if(sub.selected) {
            var opt = this.addSelectedOption(sub.id, 1);
            foundDefault = true;
            if(selectElements) {
              if(opt != null) {
                var el = $('so_' + sub.id);
                if(el != null) { 
                  el.checked = true;
                } else {
                  log("Error: unable to get element to check on for sub option " + sub.id);
                }
              }
            }
          } else if(anyDefault == null){
            var cProduct = this.field.configuredProduct;
            var selectedColor = cProduct.getSelectedColor();
            if(cProduct.product.limitSizeColors) {
              var type = cProduct.product.type;
              var ptField = type.fields.byId[this.field.id];
              var sizeColorCombinations = cProduct.product.sizeColorCombinations;
              var ptOpt = ptField.options.byId[this.pDef.id];
              var ptSubOpt = ptOpt.subs.byId[sub.id];
              if (ptField.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, ptSubOpt.id)) {
                anyDefault = sub;
                if(selectElements) {
                  var el = $('so_' + sub.id);
                  if(el != null) { 
                    el.checked = false;
                  } else {
                    log("Error: unable to get element to check on for sub option " + sub.id);
                  }
     
                }
              }
            }
            
          }
        }
        if(!foundDefault && mustSelect && anyDefault!=null) {
          log("Forcing default value");
          var opt = this.addSelectedOption(anyDefault.id, 1);
          if(selectElements) {
            if(opt != null) {
              var el = $('so_' + anyDefault.id);
              if(el != null) { 
                el.checked = true;
              } else {
                log("Error: unable to get element to check on for sub option " + anyDefault.id);
              }
            }
          }
        }
        
      }
    }
  },
  
  addSelectedOption: function CPFO_addSelectedOption(subOptionId, qty, unitPrice, isAutoPrice, legacyKey) {
    var opt = this.subOptions[subOptionId];
    if(opt == null) {
      opt = new ConfiguredFieldSubOption(this, subOptionId, qty, unitPrice, isAutoPrice, legacyKey);
      if((opt.def == null)||(opt.pDef == null)) {
        log("Dropping sub option " + subOptionId + " as it has no def");
        return null;
      }
      this.subOptions[subOptionId] = opt;
      this.subQty += qty;
    } else {
      this.subQty += (qty - opt.qty);
      opt.qty = qty;
      if (unitPrice) {
        opt.unitPrice = unitPrice;
        opt.isAutoPrice = isAutoPrice;
      }
    }
    return opt;
  },
  
  serialize: function CPFO_serialize(queryComponents, prefix) {
    queryComponents.push(encodeURIComponent(prefix + "[q]") + "=" + encodeURIComponent(this.qty));
    queryComponents.push(encodeURIComponent(prefix + "[up]") + "=" + encodeURIComponent(this.unitPrice));
    queryComponents.push(encodeURIComponent(prefix + "[iap]") + "=" + encodeURIComponent(this.isAutoPrice));
    //if(this.subQty > 0) {
      for(var k in this.subOptions) {
        var cSubOption =  this.subOptions[k];
        cSubOption.serialize(queryComponents, prefix + "[o][" + k + "]");
      }
    //}
  },
  
  appendSelectedOptions: function CPFO_appendSelectedOptions(opts, includeSubs) {
    if((this.field.fieldDef.fieldType == FIELD_TYPE_LIST_CHECKBOX)||(this.subQty==0)||(includeSubs != true)) {
      opts.push(this);
    }
    if((includeSubs==true)&&(this.subQty > 0)) {
      for(var k in this.subOptions) {
        var sOpt = this.subOptions[k];
        if(sOpt.qty > 0) {
          opts.push(sOpt);
        }
      }
    }
  },
  
  getSelectedSubOption: function CPFO_getSelectedSubOption() {
    for(var k in this.subOptions) {
      var sOpt = this.subOptions[k];
      if(sOpt.qty > 0) {
        return sOpt;
      }
    }
    return null;
  },
  
  //used to get the id of selected option when rendering combo box
  getId: function CPFO_getId() {
    if(this.subQty > 0) {
      return this.optionId + "-" + this.getSelectedSubOption().subOptionId;
    } else {
      return this.optionId;
    }
  },
  
  mainSubOption: function() {
    return hashFirstElement(this.subOptions);
  },
  
  getLegacyDelta: function(baseCrp, legacyKey) {
    if(this.unitPrice == null) return null;
    if(this.isAutoPrice) {
      if(legacyKey == this.legacyKey) {
        return d.round(this.unitPrice - baseCrp);
      }
    }
    return null;
  },
  
  getOverrideDelta: function(baseCrp) {
    if(this.unitPrice == null) return null;
    if(!this.isAutoPrice) {
      return d.round(this.unitPrice - baseCrp);
    } else {
      return null;
    }
  },
  
  getName: function() {
    return this.field.getName() + ":" + this.def.name;
  }
    
});

var ConfiguredFieldSubOption = Class.create({
  CLASSDEF: {
      name: 'ConfiguredFieldSubOption'
  },
  
  initialize: function CPFSO_initialize(option, subOptionId, qty, unitPrice, isAutoPrice, legacyKey) {
    this.option = option;
    this.field = option.field;
    this.subOptionId = subOptionId;
    this.qty = qty;
    this.unitPrice = unitPrice;
    this.isAutoPrice = (typeof(isAutoPrice) === 'undefined' || isAutoPrice === null) ? true : isAutoPrice;
    this.legacyKey = legacyKey;
    this.def = option.def.getSubOption(subOptionId);
    this.pDef = option.pDef.getSubOption(subOptionId);
  },
  
  serialize: function CPFO_serialize(queryComponents, prefix) {
    queryComponents.push(encodeURIComponent(prefix + "[q]") + "=" + encodeURIComponent(this.qty));
    queryComponents.push(encodeURIComponent(prefix + "[up]") + "=" + encodeURIComponent(this.unitPrice));
    queryComponents.push(encodeURIComponent(prefix + "[iap]") + "=" + encodeURIComponent(this.isAutoPrice));
  },
  
  getLegacyDelta: function(baseCrp, legacyKey) {
    if(this.unitPrice == null) return null;
    if(this.isAutoPrice) {
      if(legacyKey == this.legacyKey) {
        return this.unitPrice - baseCrp;
      }
    }
    return null;
  },
  
  getOverrideDelta: function(baseCrp) {
    if(this.unitPrice == null) return null;
    if(!this.isAutoPrice) {
      return this.unitPrice - baseCrp;
    } else {
      return null;
    }
  },
  
  getName: function() {
    return this.option.getName() + "/" + this.def.name;
  }
});


//represents the used view... contains a list of LayoutManagers for each area...
var ConfiguredView = Class.create({
  CLASSDEF: {
      name: 'ConfiguredView'
  },
  
  initialize: function CPV_initialize(configuredProduct, productView, options) {
    this.configuredProduct = configuredProduct;
    this.productView = productView;
    this.id = (options.id == null) ? productView.id : options.id;
    this.startUrl = options.u;
    this.isTempView = (options.isTempView == true);
    
    
    this.dirty = false;
    this.renderVersion = (options.rv == null) ? 0 : options.rv;
    this.loadedRenderVersion = (options.lrv == null) ? this.renderVersion : options.lrv;
    this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
    this.reRenderChild = false; //track if children have made changes requiring rerendering
    
    this.selectedArea = null;
    
    this.areas = {};
    
    this.nextZIndex =2;
    
    this.viewBackgroundClickedEvent = this.viewBackgroundClicked.bindAsEventListener(this);
    this.desPanel = null; //the designers panel view where areas are placed inside of...... 
    this.desBgImage = null; //the designers background image for this view... 
    this.initBgPanel();
    
    
    var areas = options.a;
    if(areas!=null) {
      for(var i=0; i < areas.length; i++) {
        var area = areas[i];
        var lm = this.getArea(area.id, true);
        if(lm != null) {
          lm.loadItems(area.i);
          if (area.p) lm.updateProcesses(area.p); // update overriding prices
          area.c.rv = area.rv; //put the render version into the configuration..
          lm.loadConfiguration(area.c);
        }
      }
    }
  },
  
  toString: function() {
    return this.configuredProduct.toString() + ":" + this.productView.name;
  },
  
  initBgPanel: function CPV_initBgImage() {
    if(this.isTempView) return;
    this.desPanel = $("prod_edit_" + this.configuredProduct.id + "_" + this.productView.id);
    if(this.desPanel == null) {
      this.desPanel = $(document.createElement("DIV"));
      this.desPanel.id = "prod_edit_" + this.configuredProduct.id + "_" + this.productView.id;
      this.desPanel.style.position="absolute";
      this.desPanel.style.top = "0px"
      this.desPanel.style.left = "0px"
      if(d.currentCView != this) {
        this.desPanel.hide();
      }
      d.designPane.appendChild(this.desPanel);
    }
    
    this.desBgImage = $("prod_bg_" + this.configuredProduct.id + "_" + this.productView.id);
    if(this.desBgImage == null) {
      this.desBgImage =  $(document.createElement("IMG"));
      this.desBgImage.id = "prod_bg_" + this.configuredProduct.id + "_" + this.productView.id;
      //this.desBgImage.src="/ppr/images/trans.gif"; //this is causing an onload event to screw up the async load...
      this.desBgImage.style.width="400px"; 
      this.desBgImage.style.height="400px";
      this.checkBgImage();
      
     // this.setBgPosition();
      this.desPanel.appendChild(this.desBgImage);
     
    } else {
       //this is the load image.. its correct (in therory)..
      this.currentBGImageUrl = this.desBgImage.getAttribute("bg-image-url");
      this.checkBgImage();
      //this.currentBGImageUrl = this.getDesignerBackgroundURL();
    }
    log("initBgPanel:" + this.toString());
    Event.observe(this.desBgImage, "mousedown", this.viewBackgroundClickedEvent);
    
  },
  
  getArea: function CPV_getArea(areaId, createIfNotFound) {
    var area = this.areas[areaId];
    if((area == null) && (createIfNotFound == true)) {
      var productArea = this.productView.areas.byId[areaId];
      if(productArea != null) {
        area = new ConfiguredViewArea(this, productArea);
        this.areas[areaId] = area;
        //area.updateOverlay(); MAP: DNC-910: removed because getArea call would update overlay with incorrect color.. why is this needed?
      }
    }
    return area;
  },
  
  //allowStartUrl: a view can have a start url when loading existing configured items/custom products
  //allowScale: should we lookup and layout modifiers to see if the scale changes...
  getViewURL: function CPV_getViewURL(size, allowStartUrl, allowScale, state) {
    
    //get the selected color id
    var colorId =  this.configuredProduct.getSelectedColorId();
    //var colorId = (color==null) ? 0 : color.productChosenOptionId;
    var scale = "1";
    //get the size layout modifier...
    if(allowScale) {
      var mods = this.configuredProduct.getLayoutModifiers();
      scale = mods.s;
      scale = new String(scale).replace(".", "_");
    }
    if((d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)||((allowStartUrl==true)&&(!this.isModified()))) {
      if(this.configuredProduct.usingCustomProduct()) {
        var url = this.configuredProduct.customProduct.getViewURL(this.id, size, colorId, scale);
        if(url != null) {
          return url;
        }
      }
    }
    if((allowStartUrl==true)&&(!this.isModified())&&(this.startUrl!=null)&&(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      var url = this.startUrl.replace(d.layoutViewUrlCID, colorId);
      url = url.replace(d.layoutViewUrlS, size );
      return url.replace(d.layoutViewUrlSc, scale );
    } else if(allowStartUrl && this.isModified()) {
      if(this.needsReRendering() || this.viewSrc == null) {
        if(state != null) {
          state.needServer = true;  
        }
      } else {
        log("getViewURL: Using viewSrc: " + this.viewSrc);
        return this.viewSrc;
      }
    }
    return this.productView.getViewURL(size, colorId, scale);
    
  },
  
  getDesignerBackgroundURL: function CPV_getDesignerBackgroundURL() {
    var allowStartUrl = (d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT);
    return this.getViewURL(1, allowStartUrl, true);
  },
  
  //make sure the background image is correct for the current color/product
  //will also create the image element if not set...
  checkBgImage: function CPV_checkBgImage() {
    if(this.isTempView) return;
    var url = this.getDesignerBackgroundURL();
    if(this.currentBGImageUrl != url) {
      setImageUrl(d.designPane, this.desBgImage, url);
      this.desBgImage.setAttribute("bg-image-url", url);
      this.currentBGImageUrl = url;
    }
  },
  
  //used to set the postion of the background image without transition effect
  setBgPosition: function() {
    if(this.isTempView) return;
    if((d.currentCanvasType==0)||(!this.selectedArea.canZoom())) { //LAYOUT
      this.desPanel.style.top="0px";
      this.desPanel.style.left="0px";
      this.desBgImage.style.width = "400px";
      this.desBgImage.style.height = "400px";
      this.bgLeft = 0;
      this.bgTop = 0;
      this.bgWidth = 400;
      this.bgHeight = 400;
      this.bgScale = 1;
    } else {
      this.bgScale = this.selectedArea.productArea.reScale;
      this.bgLeft = 0 - (this.bgScale * this.selectedArea.productArea.l) + this.selectedArea.productArea.dL;
      this.bgTop = 0 - (this.bgScale * this.selectedArea.productArea.t) + this.selectedArea.productArea.dT;
      this.bgWidth = this.bgScale * 400;
      this.bgHeight = this.bgScale * 400;
      this.desPanel.style.top = this.bgTop + "px";
      this.desPanel.style.left = this.bgLeft + "px";
      this.desBgImage.style.width = this.bgWidth + "px";
      this.desBgImage.style.height =  this.bgHeight + "px";

    }
    d.setCurrentZoom(this.bgScale);
  },
  
  
  transitionToArea: function(area) {
    if(!this.selectedArea.canZoom()) {
      log("Not scaling to area as it will not zoom");
      this.transitionTo(0,0,1.0);
      return;
    }
    var left = 0 - (this.selectedArea.productArea.reScale * this.selectedArea.productArea.l) + this.selectedArea.productArea.dL;
    var top = 0 - (this.selectedArea.productArea.reScale * this.selectedArea.productArea.t) + this.selectedArea.productArea.dT;
    var width = this.selectedArea.productArea.reScale * 400;
    var height = this.selectedArea.productArea.reScale * 400;
    
    //make sure we are not going to drag the background and expose under it.....
    if(left + width < 400) {
      log("transitionToArea: need to shift left (" + left + ") as it will expose the designer background");
      left = 400 - width;
    }
    if(left > 0) left = 0;
    if(top + height < 400) {
      log("transitionToArea: need to shift top (" + top + ") as it will expose the designer background");
      top = 400 - height;
    }
    if(top > 0) top = 0;
    
    area.updateOverlay();
    this.transitionTo(left, top, this.selectedArea.productArea.reScale);
  },
  
  transitionTo: function(left, top, rescale) {
    var transTime = 0.5; //seconds...
    
    var betweenFrames = transTime * 50.0; //try 20 frames....
    
    //log("betweenFrames=" + betweenFrames);
    transTime = transTime * 1000.0; //convert to ms
    
    var startTime = new Date().getTime();
    if(this.transitionStep(startTime, transTime, betweenFrames, left, top, rescale)) {
      this.queueNextStep(startTime, transTime, betweenFrames, left, top, rescale);
    }
  },
  
  queueNextStep: function(startTime, transTime, betweenFrames, left, top, rescale) {
    var self = this;
    window.setTimeout( function() { 
      if(self.transitionStep(startTime,  transTime, betweenFrames, left, top, rescale)) {
        self.queueNextStep(startTime, transTime, betweenFrames, left, top, rescale);
      }
    }, betweenFrames);
  },
  
  //render a step in the transition
  transitionStep: function(startTime, transTime, betweenFrames, left, top, rescale) {
    var nowTime = new Date().getTime() + betweenFrames; // we render the 'next' step..
    var finished = false;
    //log(nowTime);
    if(nowTime > startTime + transTime) {
      nowTime = startTime + transTime;
      finished = true;
    } 
    //how far through are we?
    var percent = parseFloat(nowTime - startTime) / transTime;
    var curRescale = this.getTransitionValue(percent, this.bgScale, rescale);
    //log("curRescale=" + curRescale +  ", from=" + this.bgScale + ", to=" + rescale);
    left = this.getTransitionValue(percent, this.bgLeft, left);
    top = this.getTransitionValue(percent, this.bgTop, top);
    var width = 400 * curRescale;
    var height = width;
    
    //log("%=" + percent + ",l=" + left + ", t=" + top + ", w=" + width + " h=" + height);
    
    this.desPanel.style.top = top + "px";
    this.desPanel.style.left = left + "px";
    this.desBgImage.style.width = width + "px";
    this.desBgImage.style.height =  height + "px";
    if(finished) {
      this.bgScale = rescale;
      this.bgLeft = left;
      this.bgTop = top;
      this.bgWidth = width;
      this.bgHeight = height; 
    }
    
    d.setCurrentZoom(curRescale);
    this.prepareAreas(true);

    return !finished;
  }, 
  
  getTransitionValue: function(percent, from, to) {
    var scaleDiff = to - from;
    return from + (scaleDiff * percent);
  },
  
  //set the background image of the designer...
  setDesignerBackground: function CPV_setDesignerBackground() {
    this.checkBgImage();
    for(var k in this.areas) {
      this.areas[k].updateOverlay();
    }
  },
  //find the first used area or the first area if none found
  getFirstAreaId: function CPV_getFirstAreaId() {
    var fallbackArea = null;
    for(var i=0; i < this.productView.areas.list.size(); i++) {
      var pArea = this.productView.areas.list[i];
      if(fallbackArea == null) {
        fallbackArea = pArea;
      }
      var cArea = this.areas[pArea.id];
      if(cArea != null) { //we are using it!
        log("getFirstAreaId: found used area"); 
        return pArea.id;
      }
    }
    if(this.selectedArea != null) {
      log("getFirstAreaId: found selected area");
      return this.selectedArea.id;
    }
    if(fallbackArea != null) {
      log("getFirstAreaId: using fallback area " + fallbackArea.id);
    } else {
      log("NO AREAS!");
      log(this);
      alert(ml("This product is not configured correctly (it has no areas to decorate).")) ;
    }
    return fallbackArea.id;//no areas used.. get the first one...
  },
  
  isUsed: function CPV_isUsed(processId) {
    for(var k in this.areas) {
      var area = this.areas[k];
      if(area.isUsed(processId)) {
        return true;
      }
    }
    return false;
  },
  
  //select the view from the interface
  show: function CPV_show() {
    if(this.desPanel != null) {
      this.desPanel.show();
    }
    if(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) return;
    this.productView.updateAreaSelectorHtml();
    this.prepareAreas();
    this.setDesignerBackground();
  },
  
  //when a view is no longer selected...
  hide: function CPV_hide() {
    if(this.desPanel != null) {
      this.desPanel.hide();
    }
    if(this.areaHighlightEl != null) {
      this.areaHighlightEl.hide();
    }
    if(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) return;
    for(var k in this.areas) {
      var area = this.areas[k];
      area.hide();
    }
    if(this.selectedArea != null) {
      this.selectedArea.hideCurrentProcessPanels();
    }
    
  },
  
  //when a view is deleted or no longer used because product is changed...
  remove: function CPV_remove() {
    log("remove:" + this.toString());
    if(this.desBgImage != null) {
      Event.stopObserving(this.desBgImage, "mousedown", this.viewBackgroundClickedEvent);
    }
    for(var k in this.areas) {
      var area = this.areas[k];
      area.remove();
    }
  },
  
  //called from ConfiguredArea.show()
  selectArea: function CPV_selectArea(cArea, allowTransition) {
    if(this.selectedArea != null) {
      this.selectedArea.deSelect();
    }
    this.selectedArea = cArea;
    this.selectedArea.select();
    if((allowTransition)&&(d.currentCanvasType==1)) {
      this.transitionToArea(cArea);
    } else {
      if(!allowTransition) {
        //we have changed views... make sure the areas are all correctly positioned
        this.setBgPosition();
        this.prepareAreas(true);
      }
      //this.selectedArea.positionCanvas();
      this.selectedArea.setCanvasStyle();
    }
    this.positionAreaHighlight();
  },
  
  positionAreaHighlight: function CPV_positionAreaHighlight() {
    if(this.selectedArea == null) return;
    if(this.areaHighlightEl == null) {
      this.areaHighlightEl = $('d_l_ah_' + this.id);
    }
    if(this.areaHighlightEl==null) return; //derived views?
    var thumbEl = $("d_l_i_" + this.id);
    var offSet = Position.positionedOffset(thumbEl); //[1,1];//
    var reScale = 400.0 / 75.0;
    var t = parseInt(parseFloat(this.selectedArea.productArea.t) / reScale, 10);
    var l = parseInt(parseFloat(this.selectedArea.productArea.l) / reScale, 10);
    var w = parseInt(parseFloat(this.selectedArea.productArea.w) / reScale, 10) - 2; //1px borders...
    var h = parseInt(parseFloat(this.selectedArea.productArea.h) / reScale, 10) - 2;
    
    this.areaHighlightEl.style.left = (l + offSet[0]) + "px";
    this.areaHighlightEl.style.top = (t + offSet[1]) + "px";
    this.areaHighlightEl.style.width = w + "px";
    this.areaHighlightEl.style.height = h + "px";
    this.areaHighlightEl.style.margin="0px";
    this.areaHighlightEl.show();
  },
  
  //build the area selector (if needed) and put the canvas areas on the designer in unselected mode.
  //doItems: position the items (used when in layout mode)
  prepareAreas: function CPV_prepareAreas(doItems) {
    for(var i=0; i < this.productView.areas.list.size(); i++) {
      var pArea = this.productView.areas.list[i];
      var cArea = this.getArea(pArea.id, true);
      cArea.prepareCanvas(doItems);
    }
  },
  
  //called when the design/layout tab is clicked...
  toggleDesignMode: function CPV_toggleDesignMode() {
    
    //this.setBgPosition();
    
    if(d.currentCanvasType==0) { //LAYOUT.. show all
      //this.prepareAreas(true); //as all the items are visble now position them correctly
      //this.setDesignerBackground();
      this.transitionTo(0,0,1.0);
    } else {
      //this.prepareAreas(false); //as only the 
      //this.selectedArea.prepareCanvas(true); //we want to scale the selected areas items correctly...
      this.transitionToArea(this.selectedArea);
    }
    
  },
  
  getNextZIndex: function CPV_getNextZIndex() {
    return this.nextZIndex+=5;
  },
  
  //stop hammering the server by queuing updates..
  queueViewUpdate: function CPV_queueViewUpdate() {
    if(this.vuto != null) {
      window.clearTimeout(this.vuto);
    }
    if(!this.isUsed()) {
      
      if(this.configuredProduct.product.hasDerivedViews) {
        //need to blank all the views..
        log("queueViewUpdate:!this.isUsed():hasDerivedViews");
        this.configuredProduct.updateThumbnails();
      } else {
        var url = this.getViewURL(11, false, false); //get the blank version
        var container = $("d_l_" + this.productView.id);
        var img = $("d_l_i_" + this.productView.id);
        setImageUrl(container, img, url, null);
      }
      return;
    }
    var self = this;
    var cpid = d.currentCProduct.id;
    this.vuto = window.setTimeout( function() { 
      //if(self.configuredProduct.product.hasDerivedViews) {
      //  log("queueViewUpdate:hasDerivedViews");
      //  self.configuredProduct.updateThumbnails();
      //} else {
        //queue an ajax request that will remove any existing view requests already queued
        ajaxQueueManager.queueRequest("save_product/" + self.configuredProduct.id, 3, {
            subKey: "view_" + self.id,
            mode: 0,
            url: d.pathPrefix + "/designer/save_product?preview=1&lid=" + self.id + "&lcpid=" + cpid,
            parameters: function CPV_parameters() { return self.configuredProduct.serialize([],null,self.id);},
            options: {asynchronous:true, evalScripts:true}
        });
        //var t2 = new Ajax.Request(d.pathPrefix + "/designer/save_product?preview=1&lid=" + self.id + "&lcpid=" + cpid , {asynchronous:true, evalScripts:true, parameters:d.currentCProduct.serialize([],null, self.id)});
      //}
    }, 1000);
  },
  
  //callback from server after product thumbs have been generated
  updateView: function CPV_updateView(src, renderVersion, results) {
    if(renderVersion >= this.renderVersion) {
      if(this.productView.allowView) {
        var container = $("d_l_" + this.id);
        var img = $("d_l_i_" + this.id);
        if(src == null) {
          this.viewSrc = null; //revert back to default view...
          setImageUrl(container, img, this.getViewURL(11, true, false) , null);
        } else {
          this.viewSrc = d.pathPrefix + src + "?" + new Date().getTime();
          setImageUrl(container, img, this.viewSrc, null);
        }
      }
      this.clearReRender();
    } else {
      log("Skipping updateView: " + renderVersion + " != " + this.renderVersion);
    }
  },
  
  //get the mouse position in layout scale relative to designer
  getLayoutMousePosition: function CPV_getLayoutMousePosition(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var pos = Position.cumulativeOffset(this.desPanel);
    pointer[0] -= pos[0];
    pointer[1] -= pos[1];
    pointer[0] /= this.bgScale;
    pointer[1] /= this.bgScale;
    return [pointer[0],pointer[1]];
  },
  
  
  //called from a disabled item to see if the click should go to an item in a canvas below
  //x/y are in layout scale relative to designer
  proxyMouseDown: function CPV_proxyMouseDown(x,y, event) {
    //make x/y relative to designer panel background...
    
    //convert x/y to layout scale...
    
    if(this.selectedArea != null) {
      var i = this.selectedArea.hitTest(x,y);
      if(i != null) {
        log("proxyMouseDown: hit test found item");
        i.select(false, event);
        i.dragable.initDrag(event);
        Event.stop(event);
        return;
      } else {
        log("proxyMouseDown: hit test missed");
      }
    } else {
      log("proxyMouseDown: selectedArea==null");
    }
    //if we got here then nothing hit...
    this.selectAreaFromMouse(x,y,event);
  },
  
  //x/y are in layout scale relative to designer
  
  selectAreaFromMouse: function CPV_selectAreaFromMouse(x,y,event) {
    if(!d.designerOptions.clickAreaSelects()) return;

    log("selectAreaFromMouse: " + this.toString());
    //find the smallest area that bounds the area....
    var smallestSize = -1;
    var smallestMatch = null;
    for(var i=0; i < this.productView.areas.list.size(); i++) {
      var area = this.productView.areas.list[i];
      if(area.isInArea(x,y)) {
        var sqArea =  area.w * area.h;
        if(smallestMatch == null || sqArea < smallestSize) {
          smallestMatch = area;
          smallestSize = sqArea;
        }
      }
    }
    if(smallestMatch != null) {
      d.selectCurrentArea(smallestMatch.id, true, true);
      if(event != null) {
        log("selectAreaFromMouse: event stopped");
        Event.stop(event);
      }
      return true;
    }
    return false;
  },
  
  viewBackgroundClicked: function(event) {
    
    if(!d.designerOptions.clickAreaSelects()) return;
    log("viewBackgroundClicked:" + this.toString());
    //if this happens no area was selected...unless ie... lets check
    var pointer = this.getLayoutMousePosition(event);
    if(this.selectAreaFromMouse(pointer[0],pointer[1], event)) { //must be ie or browser that ignored click events for transparent objects...
      return false;  
    }
    
    d.selectTab('d','layout', function(tab) { return d.selectDesignTab(tab); });
  },
  
  setChildReRender: function CPV_setChildReRender(queueUpdate) {
    this.reRenderChild = true;
    this.renderVersion ++;
    this.configuredProduct.setChildReRender();
    if(queueUpdate) {
      this.queueViewUpdate();
    }
  },
  
  //fromAbove: we are setting the reRender flag from the configuredProduct (color/product change)... go down and set reRender on children...
  setReRender: function CPV_setReRender(fromAbove) {
    this.reRender = true;
    this.renderVersion ++;
    if(fromAbove) {
      //for now dont mark rerender areas.. if they need rerendering (because of product change), mark for rerender at instance level
      /*for(k in this.areas) {
        var area = this.areas[k];
        if(area.isUsed()) {
          area.setReRender(true);
        }
      }*/
    } else {
      this.configuredProduct.setChildReRender();
      this.queueViewUpdate();
    }
  },
  
  //have we changed hte item at all from the loaded version (ie can we use the startUrl)
  isModified: function CPV_isModified() {
    return (this.renderVersion != this.loadedRenderVersion);
  },
  
  needsReRendering: function CPV_needsReRendering() {
    return (this.reRenderChild || this.reRender);
  },
  
  clearReRender: function CPV_clearReRender() {
    this.reRenderChild = false;
    this.reRender = false;
    for(var k in this.areas) {
      this.areas[k].clearReRender();
    }
  },
  
  //a user has changed product and ConfiguredProduct has matched this with oldView.. now we need to match areas
  //testOnly: check to make sure something will actually move across in the areas... ie embridery to mug will not work: need to warn...
  moveToView: function CPV_moveToView(oldView, testOnly) {
    //store old areas in an ordered array
    var oldAreas = [];
    for(var i=0; i < oldView.productView.areas.list.size(); i++) {
      var area = oldView.productView.areas.list[i];
      oldAreas[i] = oldView.areas[area.id];
      /*if((oldAreas[i] == null || !oldAreas[i].isUsed()) && (oldView.stash != null) && (oldView.stash[i] != null)) {
        oldAreas[i] = oldView.stash[i]; //we are not using the view.. there is one from the stash.. lets use it...
        log("using Stash for " + oldAreas[i].productArea.getName() + " (pos=" + i + ")");
      }*/
    }
    /*if( oldView.stash != null) {
      log("Attempting to add extra stash items");
      for(var i=oldView.productView.areas.list.size(); i < oldView.stash.size(); i++) {
        oldAreas[i] = oldView.stash[i];
        if(oldAreas[i] != null) {
          log("using Stash for " + oldAreas[i].productArea.getName() + " (pos=" + i + ")");
        } else {
          log("Stash at pos " + i + " was null");
        }
      }
    }*/
    
    this.areas = {};
    var moveResult = -1;
    var hasMatchedArea = false;
    //go through the areas in order.. use order to match....
    for(var i=0; i < this.productView.areas.list.size(); i++) {
      var area = this.productView.areas.list[i];
      log("Attempting to move view " + area.getName() + " (" + i + ")");
      if(oldAreas[i] != null) {
        //we can use an area from the same position....
        var newCArea = this.getArea(area.id, true);
        var mR = newCArea.moveElements(oldAreas[i], testOnly);
        if(testOnly) {
          if(moveResult < mR) {
            moveResult = mR;
          }
        }
        hasMatchedArea = true;
        oldAreas[i] = null;
      } else {
        log("Old Area is null");
      }
    }
    if(!testOnly) {
    //do we want to try and fill areas from stuff in oldAreas?... for now we wont do the order of areas wont change around...
      
      //keep the oldAreas around
      this.stash = oldAreas;
    }
    if(testOnly) {
      //did we loose some areas becuase the new view has less areas?
      for(var i=0; i < oldAreas.length; i++) {
        if((oldAreas[i] != null)&&(oldAreas[i].isUsed())) {
          //we used the old area and it does not match anything in the new view...
          if(!hasMatchedArea) {
            moveResult = 2; //nothing matched here...
          } else {
            moveResult = 1; //some stuff didnt match...
          }
        }
      }
      
    }
    if(moveResult==-2) moveResult = 2;
    return moveResult; //only used when testOnly... nothing moved...
  },
  
  validate: function CPV_validate() {
    for(var k in this.areas) {
      this.areas[k].validate();
    }
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns total this views costs to decorate
  calculatePrice: function CPV_calculatePrice(colorType, priceData, percentMarkup, pricingState, forProductViewAreaProcess) {
    var viewTotal = priceZero();
    var viewPriceData = {
      id: this.productView.id,
      type: 1,
      view: this,
      areas: new MapList()
    };
    for(var j=0; j < this.productView.areas.list.size(); j++) {
      var pArea =  this.productView.areas.list[j];
      if(forProductViewAreaProcess != null) {
        if(pArea.id == forProductViewAreaProcess.viewArea.id) {
          var cArea = this.getArea(forProductViewAreaProcess.viewArea.id, true);
          viewTotal = cArea.calculatePrice(colorType, priceData, viewPriceData, percentMarkup, pricingState,forProductViewAreaProcess);
          break;
        }
      } else {
        var cArea = this.areas[pArea.id];
        if((cArea != null) && (cArea.isUsed())) {
          viewTotal = viewTotal.add(cArea.calculatePrice(colorType, priceData, viewPriceData, percentMarkup, pricingState));
        }
      }
    }
    if(viewTotal.cost > 0 || viewTotal.isOverriden) {
      viewPriceData.total = viewTotal;// * percentMarkup;
      priceData.views.add(viewPriceData);
    }
    return viewTotal;
  },
  
  serialize: function CPV_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix="v[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[rv]") + "=" + encodeURIComponent(this.renderVersion));
    for(var k in this.areas) {
      area = this.areas[k];
      if(area.isUsed()) {
        var p = prefix + "[a][" + area.id + "]";
        area.serialize(queryComponents, p);
      }
    }
    return queryComponents.join('&');
  },
  
  setErrors: function CPV_setErrors(errors) {
    if(errors==null) errors = {};
    for(var k in this.areas) {
      area = this.areas[k];
      if(area.isUsed()) {
        area.setErrors(errors.areas[area.id]);;
      }
    }
  }
});

var ConfiguredViewArea = Class.create({
  CLASSDEF: {
      name: 'ConfiguredViewArea'
  },
  
  initialize: function CPVA_initialize(configuredView, productArea) {
    this.id = productArea.id;
    this.configuredView = configuredView;
    this.configuredProduct = configuredView.configuredProduct;
    this.productArea = productArea;
    this.productView = configuredView.productView;
    
    this.selected = false;
    this.renderVersion = 0;
    this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
    this.reRenderChild = false; //track if children have made changes requiring rerendering
    
    this.allItems = {}; //used to lookup where an item is (which process)
    this.itemCount = 0;
    this.visibleItemCount = 0;
    
    this.zIndex = configuredView.getNextZIndex();
    
    
    this.initProcesses();
    
    
    
    this.initialised = false;
    this.canvasPlaced = false;
    this.allowedProcesses = null;
    
    
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT && !this.configuredView.isTempView) {
      this.canvas = document.createElement("DIV");
      this.canvas.id = "canvas_" + d.getNextId();
      this.canvas.style.position = "absolute";
      this.canvas.style.border="dotted 1px white";
      this.canvas.style.overflow ="hidden";
      this.canvas.style.display = "none";
      this.canvas.style.zIndex = this.zIndex;
      this.eventMouseDown =  this.mouseDown.bindAsEventListener(this);
      Event.observe(this.canvas, "mousedown", this.eventMouseDown);
      
      if(this.productArea.maskUrl != null) {
        this.usingOverlay = true;
        this.overlay = document.createElement("IMG");
        this.overlay.style.position = "absolute";
        this.overlay.style.display = "none";
        this.overlay.style.zIndex = this.zIndex + 1;
        this.overlay.src = d.pathPrefix + "/images/trans.gif";
        this.configuredView.desPanel.appendChild(this.overlay);
        this.overlay = $(this.overlay);
        this.overlayMouseDownEvent = this.overlayMouseDown.bindAsEventListener(this);
        Event.observe(this.overlay, "mousedown", this.overlayMouseDownEvent);
        this.currentOverlayUrl = null;
        this.canvas.style.borderWidth="0px";
      } else {
        this.usingOverlay = false;
      }
      
      this.configuredView.desPanel.appendChild(this.canvas);
      this.canvas = $(this.canvas);
      if(!this.usingOverlay) {
        this.canvas2 = document.createElement("DIV");
        this.canvas2.id = "canvas_" + d.getNextId();
        this.canvas2.style.position = "absolute";
        this.canvas2.style.border="solid 1px black";
        this.canvas2.style.overflow ="hidden";
        this.canvas2.style.display = "none";
        this.canvas2.style.zIndex = this.zIndex-1;
        this.configuredView.desPanel.appendChild(this.canvas2);
        this.canvas2 = $(this.canvas2);
      }
      //this.vCanvas = Raphael(this.canvas, 400, 400); TODO: change to VML/SVG rendering...
    }
  },
  
  toString: function() {
    return this.configuredView.toString() + ":" + this.productArea.name;
  },
  
  getName: function() {
    return this.toString();  
  },
  
  //load from server through hashmap
  loadItems: function CPVA_loadItems(itemOptions) {
    for(var i=0; i < itemOptions.length; i++) {
      var itemData = itemOptions[i];
      var itemConfig = itemData.c;
    
      itemConfig.rv = itemData.rv; //put the render version into the config map
      var item = null;
      
      var id = (itemData.id == null) ? this.configuredProduct.getNextItemId() : this.configuredProduct.registerServerItemId(itemData.id);
      var processId = itemData.p;
      
      var cProcess = this.processes[processId];
      if(cProcess != null) {//the process may have been removed ..
        if(parseInt(itemConfig.it, 10) == "0") {
          item = new ImageItem(id, cProcess, d.assets[parseInt(itemConfig.aid, 10)], itemConfig);
        } else if(parseInt(itemConfig.it, 10) == "1") {
          item = new TextItem(id, cProcess, new TextAsset(new Hash()), itemConfig);
        } else if(parseInt(itemConfig.it, 10) == "2") {
          item = new TeamItem(id, cProcess, new TeamNameAsset(new Hash()), itemConfig);
        } else if(parseInt(itemConfig.it, 10) == 3) { //placeholder
          item = new PlaceholderItem(id, cProcess, new PlaceholderAsset(), itemConfig);
        }
        cProcess.addItem(item);
      }
    }  
  },
  
  isUsed: function CPVA_isUsed(processId) {
    if((this.bgColor != null)&&(this.bgColor != "Transparent")&&(this.bgColor != "")) {
      return true;
    }

    if(processId == null) {
      for(var k in this.processes) {
        if(this.processes[k].isUsed()) {
          return true;
        }
      }
      return false;
    } else {
      var cProcess = this.processes[processId];
      if((cProcess == null)||(!cProcess.isUsed())) return false;
      return true;
    }
  },
  
  registerItem: function CPVA_registerItem(item) {
    this.allItems[item.id] = item;
    this.itemCount ++;
    if (item.itemVisible()) this.visibleItemCount ++;
  },
  
  deRegisterItem: function CPVA_deRegisterItem(item) {
    this.itemCount --;
    if (item.itemVisible()) this.visibleItemCount --;
    delete this.allItems[item.id];
  },
    
  //called once the items have been added 
  loadConfiguration: function CPVA_loadConfiguration(configuration) {
    this.bgColor = configuration.bg; //Color;
    this.layoutScale = 1;
    if(configuration.rv != null) {
      this.renderVersion = configuration.rv;
    }
  },
  
  //init the ConfiguredViewProcesses
  initProcesses: function CPVA_initProcesses() {
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      this.managePane = $(document.createElement("DIV"));
      this.managePane.id = "mp_" + d.getNextId();
      this.managePane.hide();
      d.managePaneContainer.appendChild(this.managePane);
      //get the list of available processes
    }
    this.processes = {};
    for(var i=0; i < this.productArea.processes.list.size(); i++) {
      var process = this.productArea.processes.list[i];
      this.processes[process.id] = new ConfiguredViewProcess(this, process);
    }
    
  },
  
  updateProcesses: function CPVA_updateProcesses(processOptions) {
    var self = this;
    processOptions.each(function(p) {
        cProcess = self.processes[p.p];
        if (cProcess) {
          cProcess.usingOverridingDec = p.uod;
          cProcess.usingOverridingClipart = p.uoc;
          cProcess.overridingDecorationPrice = p.odp;
          cProcess.overridingClipartPrice = p.ocp;
          cProcess.legacyKey = p.lk;
        }
    });
  },
  
  //choose what process to select when the area has been selected
  selectStartProcess: function CPVA_selectStartProcess() {
    
    if(d.userSelectedProcessId == null) { //user has selected 'all'
      log("selectStartProcess: user has selected 'all'");
      if(this.productArea.processes.list.size() ==1) {  
        log('But there is only one process, so lets choose that one.') ;
        return hashFirstElement(this.processes);
      }
      return null;
    }
    if(this.getAllowedProcesses()[d.userSelectedProcessId]==true) { //user has selected a process we can use
      log("selectStartProcess: user has selected allowed process");
      return this.processes[d.userSelectedProcessId];
    } else {
      log("selectStartProcess: user has selected disallowed process");
    }
    if((d.defaultProcess != null)&&(this.getAllowedProcesses()[d.defaultProcess.id]==true)) { //this area can use the default process
      log("selectStartProcess: default process is allowed");
      return this.processes[d.defaultProcess.id];
    }
    
    //find the first process with an item...
    for(var k in this.processes) {
      if(this.processes[k].isUsed()) {
        log("selectStartProcess: found used process");
        return this.processes[k];
      }
    }
    if(this.productArea.processes.list.size() ==1) {  
      return hashFirstElement(this.processes);
    }  
    log("selectStartProcess: defaulting to 'all'");
    //final fallback: all
    return null;
  },
  
  //position the canvas and set the style
  //doItems: reposition the items to the correct scale...
  prepareCanvas: function CPVA_prepareCanvas(doItems) {
    this.positionCanvas(doItems);
    this.setCanvasStyle();
  },
  
  deSelect: function CPVA_deSelect() {
    this.selected = false;
    this.canvas.style.zIndex = this.zIndex;
    if(this.canvas2 != null) {
      this.canvas2.style.zIndex = this.zIndex-1;
    }
    if(this.overlay != null) {
      this.overlay.style.zIndex = this.zIndex + 1;
    }
    this.selectItem(null);
    this.setCanvasStyle(); //deselect the canvas areas
    this.hideCurrentProcessPanels(); //hide the management panels
    this.enableCanvasItems(false); //disable the canvas items...
  },
  
  select: function CPVA_select() {
    this.selected = true;
    this.canvas.style.zIndex = this.configuredView.nextZIndex + 2;
    if(this.canvas2 != null) {
      this.canvas2.style.zIndex = this.configuredView.nextZIndex + 1;
    }
    if(this.overlay != null) {
      this.overlay.style.zIndex = this.configuredView.nextZIndex + 3;
    }
  },
  
  //set 
  setCanvasStyle: function CPVA_setCanvasStyle() {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    
    this.canvas.show();
    if(this.canvas2!=null) this.canvas2.show();
    if(((this.usingOverlay)||(!d.showGrid))/*||(d.currentCanvasType==1&&!this.selected)*/) {
      this.canvas.style.borderWidth="0px";
      if(this.canvas2 != null) this.canvas2.hide(); 
    } else {
      this.canvas.style.borderWidth="1px";
      if(this.canvas2 != null) this.canvas2.show();
      if(this.selected) {
        this.canvas.style.borderColor="#FFFF00";
        if(this.canvas2 != null) this.canvas2.style.borderColor="#FF0000";
      } else {
        this.canvas.style.borderColor="#FFFFFF";
        if(this.canvas2 != null) this.canvas2.style.borderColor="#000000";
      }
    }
  },
  
  //turn the management panels on/off based on selected process
  showManagementPanels: function CPVA_showManagementPanels() {
    var allowedProcesses = this.getAllowedProcesses();
    var uniqueProcs = 0;
    for(var k in allowedProcesses) {
      if(this.processes[k].isUsed()) uniqueProcs+=1;
    }
    var showHeadings = (uniqueProcs > 1);
    for(var k in allowedProcesses) {
      this.processes[k].showPanel(showHeadings, true);
    }
    this.managePane.show();
    this.checkNoOptions();
  },
  
  hideCurrentProcessPanels: function CPVA_hideCurrentProcessPanels() {
    log("hideCurrentProcessPanels()");
    for(var k in this.processes) {
      this.processes[k].hidePanel();
    }
  },
  
  updateAllowedProcesses: function CPVA_updateAllowedProcesses() {
    this.allowedProcesses = {};
    for(var k in this.processes) {
      this.allowedProcesses[k] = true;
    }
    for(var k in this.processes) {
      var cProcess = this.processes[k];
      if((cProcess.isUsed()) && (this.allowedProcesses[cProcess.id])) {
        this.allowedProcesses = cProcess.productProcess.productTypeProcess.getAllowedProcesses(this.allowedProcesses);
      }
    }
    return this.allowedProcesses;
  },
  
  //get the allowed item types for all allowed processes on this area
  getAllAllowedItemTypes: function CPVA_getAllAllowedItemTypes() {
    var allowedProcs = this.getAllowedProcesses();
    var allowed = {
      image: { count:0, single:null},
      text: { count:0, single:null},
      teamname: { count:0, single:null}
    };
    for(var i=0; i < this.productArea.processes.list.length; i++) {
      var pvap = this.productArea.processes.list[i];
      if(allowedProcs[pvap.id]) { //can be used on this area
        var proc = pvap.process;
        if(proc.allowImages) {
          allowed.image.count ++;
          allowed.image.single = pvap.id;
        }
        if(proc.allowText) {
          allowed.text.count ++;
          allowed.text.single = pvap.id;
        }
        if(proc.allowTeamNames) {
          allowed.teamname.count ++;
          allowed.teamname.single = pvap.id;
        }
      }
    }
    return allowed;
  },
  
  //get allowed processes based on what processes are currently used
  getAllowedProcesses: function CPVA_getAllowedProcesses() {
    if(this.allowedProcesses==null) {
      this.updateAllowedProcesses();
    }
    return this.allowedProcesses;
  },
  
  addNewItem: function CPVA_addNewItem(processId, asset, initData, fromOMS) {
    if(processId == null) { //find a matching process...
      for(var i=0; i < asset.processes.length;i++) {
        if(this.processes[asset.processes[i]]!=null) {
          processId = asset.processes[i];
          break;
        }
      }
      if(processId == null) {
        alert(ml("Unable to add image to product as it does not support the decoration process used by the image"));
        return;
      }
    }
    
    
    var process = this.processes[processId];
    var itemType = asset.getItemType();
    var newItem = null;
    if(itemType==0) { //normal image
      newItem = new ImageItem(this.configuredProduct.getNextItemId(), process, asset, initData);
    } else if(itemType==1) { //text
      newItem = new TextItem(this.configuredProduct.getNextItemId(), process, asset, initData);
      d.track("add-new-text");
    } else if(itemType==2) { //team name
      newItem = new TeamItem(this.configuredProduct.getNextItemId(), process, asset, initData);
      d.track("add-new-teamname");
    } else if(itemType==3) { //placeholder
      newItem = new PlaceholderItem(this.configuredProduct.getNextItemId(), process, asset, initData);
      d.track("add-new-placeholder");
    }
    return this.insertNewItem(newItem, fromOMS);
  },
  
  insertNewItem: function CPVA_insertNewItem(newItem, inBackground) {
    var wasUsing = newItem.cViewProcess.isUsed();
    newItem.cViewProcess.addNewItem(newItem);
    if(inBackground != true) {
      this.showManagementPanels();
      if(!wasUsing) {
        //we are using a process for the first time.. lets update what process we can now use..
        this.updateAllowedProcesses();
      }
      this.setReRender(true);
      d.checkCopyPasteState();
    }
    return newItem;
  },
  
  pasteItems: function CPVA_pasteItems(items) {
    for(var i=0; i < items.length; i++) {
      var newItem = items[i];
      var process = this.processes[processId];
      
    }
  },
  
  mouseDown: function CPVA_mouseDown(event) {
    log("mouseDown:" + this.toString());
    if(!this.selected) {
      if(event == null) {
        log("area isnt selected..event is null..not passing through....");
        return false;
      }
      var pointer = this.configuredView.getLayoutMousePosition(event);
      log("area isnt selected..passing through....");
      this.configuredView.proxyMouseDown(pointer[0],pointer[1], event);
      return false;
    } else {
      if(event == null) {
        log("area is selected..event is null..not selectAreaFromMouse....");
        return false;
      }
      var pointer = this.configuredView.getLayoutMousePosition(event);
      //click on area that did not hit an item...
      
      this.configuredView.selectAreaFromMouse(pointer[0],pointer[1],event);
    }
  },
  
  overlayMouseDown: function CPVA_overlayMouseDown(event) {
    log("overlayMouseDown:" + this.toString());
    if(!this.selected) {
      log("Area No Selected: Proxy through mouseDown");
      return this.mouseDown(event);
    }
    var pointer = this.configuredView.getLayoutMousePosition(event);
    //var dims = this.rel({l:pointer[0], t: pointer[1]});
    
    var i = this.hitTest(pointer[0], pointer[1]);
    if(i != null) {
      log("hit test found item");
      i.select();
      i.dragable.initDrag(event);
      Event.stop(event);
    } else {
      this.configuredView.selectAreaFromMouse(pointer[0],pointer[1],event);
    }
    return false;
  },
  
  removeNewItem: function CPVA_removeNewItem(item) {
    this.items[this.items.indexOf(item)] = null;
    this.items = this.items.compact();
    this.itemsById[item.id] = null;
    this.showManagementPanels();
    d.checkCopyPasteState();
  },
  
  tbTransformed: function CPVA_tbTransformed(options) {
    var item = this.allItems[options.id];
    if(item!=null) {
      item.tbTransformed(options);
    } else {
      log("tbTransformed: unable to get item " + options.id);
    }
  },
  
  setBgColor: function CPVA_setBgColor() {
    if(this.productArea.canSetBgColor) {
      if((this.bgColor == null) || (this.bgColor == "Transparent") || (this.bgColor == "")) {
        this.canvas.style.backgroundColor = "";
      } else {
        this.canvas.style.backgroundColor = "#" + this.bgColor;
      }
    } else {
      this.canvas.style.backgroundColor = "";
    }
  },
  
  getOverlayURL: function CPVA_getOverlayURL(size) {
    if(this.productArea.maskUrl != null) {
      var colorId = this.configuredProduct.getSelectedColorId();
      var url = this.productArea.maskUrl.replace(d.layoutViewUrlCID, colorId);
      return url.replace(d.layoutViewUrlS, size); 
    } else {
      return null;
    }
  },
  
  updateOverlay: function CPVA_updateOverlay() {
    if(this.overlay!= null) {
      log("setting overlay", true);
      var url = this.getOverlayURL("Z"); //generate the url as if we are zoomed in...
      if(url != this.currentOverlayUrl) { 
        if(d.currentCanvasType == 0) { //we are not currently zoomed in... lets try the unzoomed version... 
          url = this.getOverlayURL(1);
          if(url != this.currentOverlayUrl) {
            setTransparentImage(d.designPane, this.overlay, url); //get the unzoomed version
          }
        } else {
          setTransparentImage(d.designPane, this.overlay, url); //get the zoomed version
        }
        this.currentOverlayUrl = url;
      }
    }
  },
  
  canZoom: function() {
    return (this.productArea.reScale > 1.2);
  },
  
  positionCanvas: function CPVA_positionCanvas(doItems) {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    //log("positionCanvas. top=" + (this.productArea.t * d.zoom));
  
    
      
    var delta = 0;
    if(((!d.showGrid)&&(this.overlay== null))/*||(!this.selected && d.currentCanvasType==1)*/) {
      delta = 1;
    }
    
    this.canvas.style.top = (this.productArea.t * d.zoom + delta) + "px";
    this.canvas.style.left = (this.productArea.l * d.zoom + delta) + "px";
    this.canvas.style.width = (this.productArea.w * d.zoom - delta) + "px";
    this.canvas.style.height = (this.productArea.h * d.zoom - delta) + "px";
    if(this.canvas2 != null) {
      this.canvas2.style.top = (this.productArea.t * d.zoom + delta) + "px";
      this.canvas2.style.left = (this.productArea.l * d.zoom + delta) + "px";
      this.canvas2.style.width = (this.productArea.w * d.zoom - delta) + "px";
      this.canvas2.style.height = (this.productArea.h * d.zoom - delta) + "px";
    }
    
    if(this.overlay!= null) {
      this.overlay.style.top = (this.productArea.t * d.zoom) + "px";
      this.overlay.style.left = (this.productArea.l * d.zoom) + "px";
      this.overlay.style.width = (this.productArea.w * d.zoom)  + "px";
      this.overlay.style.height = (this.productArea.h * d.zoom) + "px";
      this.overlay.style.display = "";
      this.canvas.style.borderWidth="0px";
    } else {
      /*if(!this.selected && d.currentCanvasType==1) { //hide outlines of unselected areas when zoomed in
        this.canvas.style.borderWidth="0px";
        if(this.canvas2 != null) this.canvas2.hide();
      } else {*/
        if(d.showGrid) {
          this.canvas.style.borderWidth="1px";
          if(this.canvas2 != null) this.canvas2.show();
        } else {
          this.canvas.style.borderWidth="0px";
          if(this.canvas2 != null) this.canvas2.hide();
        }
      //}
    }
      

    
    this.canvasX=null; //will reset the calibration
        
    if((doItems)&&(this.initialised)) {
      this.recalibrate();
    }
  },
  
  canvasSize: function CPVA_canvasSize() {
    //if(d.currentCanvasType==0) { //LAYOUT
      return {w: this.productArea.w * d.zoom, h:this.productArea.h * d.zoom};
    //} else { //DESIGN
    //  return {w: this.productArea.dW, h:this.productArea.dH};
    //}
  },
  
  abs: function CPVA_abs(dims) {
    this.checkCalibration();
    if(dims.l != null) {
      dims.l += this.canvasX;
    }
    if(dims.t != null) {
      dims.t += this.canvasY;
    }
    return dims;
  },   
  
  rel: function CPVA_rel(dims) {
    //this.checkCalibration();
    if(dims.l != null) {
      dims.l -= this.productArea.l;
    }
    if(dims.t != null) {
      dims.t -= this.productArea.t;
    }
    return dims;
  },
  
  checkCalibration: function CPVA_checkCalibration() {
    if(this.canvasX==null) {
      var pos = Position.cumulativeOffset(this.canvas);
      this.canvasX = pos[0];
      this.canvasY = pos[1];
    }
  },
  
  recalibrate: function CPVA_recalibrate() {
    this.canvasX=null; //will reset the calibration
        
    if(this.initialised) {
      for(var k in this.allItems) {
        this.allItems[k].quickSetPosition();
      }
      if((this.selectedItem)&&(this.selected)) {
        this.selectedItem.autoRepositionHandles();
      }
    }
  }, 
  
  checkInitialised: function CPVA_checkInitialised() {
    if(!this.initialised) {
      log("checkInitialised");
      log(this);
      this.updateOverlay();
      for(k in this.processes) {
        var process = this.processes[k];
        process.initItems();
      }
      this.initialised = true;
    }
  },
  
  //select the area from the interface...
  show: function CPVA_show(allowTransition) {
    this.checkInitialised();
    this.configuredView.selectArea(this, allowTransition);
    
    this.enableCanvasItems(true);
    this.checkNoOptions();
    this.canvas.show();
    if(this.canvas2 != null) {
      this.canvas2.show();
    }

    //this.canvasX=null; //will reset the calibration // this.configuredView.selectArea will do this
    //this.recalibrate(); //make sure all the items use the correct scale
    
    this.setBgColor();
    

    var bgcDiv = $("canvas_bg_color");
    if(this.productArea.canSetBgColor) {
      var button = $("bg_color_button");
      if((this.bgColor==null)||(this.bgColor=="Transparent")||(this.bgColor=="")) {
        button.style.backgroundColor = "";
        button.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
      } else {
        button.style.backgroundColor = "#" + this.bgColor;
        button.style.backgroundImage = "";
      }
      bgcDiv.style.display="";
    } else {
      bgcDiv.style.display="none";
    }
    this.showManagementPanels();
  },
  
  
  //enable/disable canvas items based on area selection
  enableCanvasItems: function CPVA_enableCanvasItems(enabled) {
    for(var k in this.allItems) {
      this.allItems[k].setEnabled(enabled);
    }
  },
  
  hide: function CPVA_hide() {
    this.canvas.hide();
    if(this.canvas2 != null) {
      this.canvas2.hide();
    }
    this.managePane.hide();
    if(this.selectedItem) {
      this.selectedItem.deSelect();
    }
    if(this.overlay!= null) {
      this.overlay.hide();
    }
  },
  
  selectItem: function CPVA_selectItem(item) {
    if(this.selectedItem) {
      this.selectedItem.deSelect();
    } else {
      log("Area.selectItem: no existing selection");
    }
    this.selectedItem = item;
    d.checkCopyPasteState();
  },
  
  findAndSelectItem: function CPVA_findAndSelectItem() {
    var proc = null;

    for(var k in this.processes) {
      if(this.processes[k].isUsed()) {
        proc = this.processes[k];
      }
    }

    if((proc == null) || (!proc.hasVisibleItems())) {
      return false;
    }
    log("findAndSelectItem");
    proc.visibleItems.list[0].select();
    return true;
  },
  
  checkNoOptions: function CPVA_checkNoOptions() {
    log("checkNoOptions.itemCount:" + this.visibleItemCount);
    if(this.visibleItemCount == 0) {
      $("no_items").show();
      $("has_items").hide();
    } else {
      $("no_items").hide();
      $("has_items").show();
    }
  },
  
  serialize: function CPVA_serialize(queryComponents, prefix) {
    this.checkInitialised();
    if(prefix==null) {
      prefix="a[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[rv]") + "=" + encodeURIComponent(this.renderVersion));
    if((this.bgColor == null)||(this.bgColor == "Transparent")) {
      queryComponents.push(encodeURIComponent(prefix + "[bg]") + "=" + encodeURIComponent("Transparent"));
    } else {
      queryComponents.push(encodeURIComponent(prefix + "[bg]") + "=" + encodeURIComponent(this.bgColor));
    }
    
    for(var k in this.processes) {
      var item = this.processes[k];
      var p = prefix + "[p][" + k + "]";
      item.serialize(queryComponents, p);
    }
    
    for(var k in this.allItems) {
      var item = this.allItems[k];
      var p = prefix + "[i][" + item.id + "]";
      item.serialize(queryComponents, p);
    }
    return queryComponents.join('&');
  },
  
  remove: function CPVA_remove() {
    for(var k in this.processes) {
      this.processes[k].remove();
    }
    
    if(this.canvas != null) {
      Event.stopObserving(this.canvas, "mousedown", this.eventMouseDown);
    }
    if(this.overlay != null) {
      Event.stopObserving(this.overlay, "mousedown", this.overlayMouseDownEvent);
    }
    
    this.canvas.parentNode.removeChild(this.canvas);
    if(this.canvas2 != null) {
      this.canvas2.parentNode.removeChild(this.canvas2);
    }
  },
  

  
  hitTest: function CPVA_hitTest(x,y) {
    log("hit test:" + x + "." + y);
    log(this.productArea);
    
    var dims = this.rel({l:x, t: y});
    
    x = dims.l;
    y = dims.t;
    
    log("hit test rel:" + x + "." + y);
    
    if(x < 0 || x > this.productArea.w  || y < 0 || y > this.productArea.h) {
      log("Hit Test Fail: Not even in area");
      return null;
    }
    
    
    
    //var dims = d.fromCanvasDims({l:x, t:y});
    //x = dims.l;
    //y = dims.t;
    //log(dims);
    var bestItem = null;
    var bestZIndex = -1;
    for(var k in this.allItems) {
      if(this.allItems[k].hitTest(x,y)) {
        if(this.allItems[k].selected) {
          return this.allItems[k];
        } else if(this.allItems[k].zIndex > bestZIndex) {
          bestItem = this.allItems[k];
          bestZIndex = bestItem.zIndex;
        }
      }
    }
    return bestItem;
  },
  
  // move all the elements from the src layout manager, at the same time rescaling them to fit in the current canvas
  moveElements: function CPVA_moveElements(src, testOnly, makeCopy, itemsToCopy) {
    if(testOnly) {
      return this.testIfCanMove(src);
    }
    //the items store dims in "real" co-ords... convert them to design canvas co-ords, rescale, the convert back to new "real" co-ords
    log("Moving Elements from " + src.productArea.getName() + " to " + this.productArea.getName());
    var sDims = src.getMinCanvasSize();
    
    log(sDims);
    
    var x1 = sDims.left; //left gap
    var x2 = src.productArea.dW - (sDims.left + sDims.width); //right gap
    var horizontalGapScale = (x1 + x2==0 || x1==0 && Math.abs(x2) <= 1) ? 0.5 : parseFloat(x1) / parseFloat(x1 + x2);
    
    var y1 = sDims.top; //top gap
    var y2 = src.productArea.dH - (sDims.top + sDims.height); //bottom gap
    var verticalGapScale = (y1 + y2==0) ? 0.5 : parseFloat(y1) / parseFloat(y1 + y2);
    var selfCopy = (src == this); //are we copying over ourselves?
    
    
    
    var xScale = parseFloat(sDims.width) / parseFloat(this.productArea.dW);
    var yScale = parseFloat(sDims.height) / parseFloat(this.productArea.dH);
    
    var rScale = (xScale > yScale) ? xScale : yScale;
    
    if(rScale < 1) { //we dont want to make it any bigger....
      rScale= 1.0;
    }
    
    log("y1=" + y1 + " y2=" + y2 + " selfCopy=" + selfCopy);
    
    log("xScale=" + xScale + " yScale=" + yScale + " rScale=" + rScale + " horizontalGapScale=" + horizontalGapScale + " verticalGapScale=" + verticalGapScale);
    
    //we want to use rScale on the items.. lets check we can....
    
    var allowedProcesses = this.getAllowedProcesses();
    var needAllowUpdate = false;
    
    var addedItems = [];
    
    var srcArray = []; //make a copy of the src becuase copy() will add it to src.allItems causing an infiniate loop
    if(itemsToCopy == null) {
      for(var k in src.allItems) {
        srcArray.push(src.allItems[k]);
      }
    } else {
      srcArray = itemsToCopy;
    }
    
    for(var i = 0; i < srcArray.length; i++) {
      needAllowUpdate = false;
      var item = srcArray[i];
      if(allowedProcesses[item.cViewProcess.id]) {
        var cProcess = this.processes[item.cViewProcess.id];
        if(!cProcess.isUsed()) {
          needAllowUpdate = true;
        }
        var itemToAdd = (makeCopy==true) ? item.copy() : item;
        addedItems.push(itemToAdd);
        if(!selfCopy) cProcess.addItem(itemToAdd);
        //check the largest scale we can use...
        rScale = itemToAdd.checkScale(rScale, cProcess.productProcess);
        if(needAllowUpdate) {
          //we have used a process for the first time.. this could exclude other processes...
          allowedProcesses = this.updateAllowedProcesses();
          log(allowedProcesses);
        }
      } else {
        log("Cannot use item for process " + item.cViewProcess.id);
        //store unusable items in case we change products later..
      }
    }
    
    
    //refactor the width/height using the new scale...
    var finalWidth = parseInt(sDims.width / rScale, 10);
    var finalHeight = parseInt(sDims.height / rScale, 10);
    //var newLeft = parseInt(((this.productArea.dW - finalWidth)/2) * horizontalGapScale, 10);
    //var newTop = parseInt(((this.productArea.dH - finalHeight)/2) * verticalGapScale, 10);
    
    var offsetLeft = parseInt(((this.productArea.dW - finalWidth)) * horizontalGapScale, 10); //sDims.left - newLeft;
    var offsetTop = parseInt(((this.productArea.dH - finalHeight)) * verticalGapScale, 10); //sDims.top - newTop;
    log("after checking scale rScale=" + rScale + " this.productArea.dH=" + this.productArea.dH + " finalWidth=" + finalWidth + " finalHeight=" + finalHeight + " offsetLeft=" + offsetLeft + " offsetTop=" + offsetTop);
    
    log(this);
                         
    //sort addedItems by zindex
    if(makeCopy == true) {
      addedItems = addedItems.sortBy( function(i) { return i.zIndex;});
      
    }
    for(var i=0; i < addedItems.length; i++) {
      var item = addedItems[i];
      var cProcess = this.processes[item.cViewProcess.id];
      if(!selfCopy) item.cViewProcess.removeItem(item); //we need to remove from src process's item list otherwise cleanup of src will destroy this...
      if(makeCopy == true) {
        item.zIndex = null;
      }
      item.moveToDesigner(cProcess, rScale, sDims.left, sDims.top, offsetLeft, offsetTop);
      item.setPosition(true);
    }
    this.initialised = src.initialised;
    
    sDims = this.getMinCanvasSize();
    log(sDims);
  },
  
  
  copyTo: function CPVA_copyTo(destArea, copyState) {
    var existingCount = hashSize(destArea.allItems);
    var thisCount = copyState.length;
    destArea.moveElements(this, false, true, copyState);
    destArea.showManagementPanels();
    destArea.setReRender(true);
   // this.setReRender(true);
    var newCount = hashSize(destArea.allItems);
    return [newCount-existingCount, thisCount];
  },
  
  getCopyState: function CPVA_getCopyState() {
    var srcArray = []; //make a copy of the src becuase copy() will add it to src.allItems causing an infiniate loop
    for(var k in this.allItems) {
      srcArray.push(this.allItems[k]);
    }
    return srcArray;
  },
  
  //called after the items have all moved to another product and everything is setup again
  validate: function CPVA_validate() {
    for(var k in this.allItems) {
      var item = this.allItems[k];
      item.validate();
    }
  },
  
  getMinCanvasSize: function CPVA_getMinCanvasSize() {
    var top = -1;
    var bottom = -1;
    var left = -1;
    var right = -1;
    for(var k in this.allItems) {
      var item = this.allItems[k];
      var dims = item.getCanvasDims();
      if(dims.t < top || top == -1) {
        top = dims.t;
      }
      if(dims.t + dims.h > bottom || bottom == -1) {
        bottom = dims.t + dims.h;
      }
      if(dims.l < left || left == -1) {
        left = dims.l;
      }
      if(dims.l + dims.w > right || right == -1) {
        right = dims.l + dims.w;
      }
    }
    
    //we now have the max dims...
    return {top : top, left: left, width: right - left, height: bottom - top};
  },
  
  testIfCanMove: function CPVA_testIfCanMove(src) {
    var allowedProcesses = this.getAllowedProcesses();
    var all_ok = true;
    var any_ok = false;
    for(var k in src.allItems) {
      var item = src.allItems[k];
      if(allowedProcesses[item.cViewProcess.id]) {
        any_ok = true ;
      } else {
        all_ok = false;
      }
    }
    //we move to the higher number so 'any match' overrides 'no match' or 'all matches'
    if(all_ok) {
      return 0;
    } else if(any_ok) {
      return 2;
    } else {
      return 1;
    }
  },
  
  selectBackgroundColor: function CPVA_selectBackgroundColor(processId) {
    if((processId == null)||(processId == -1)) {
      processId = null;
      //try find the first process that is used 
      for(var i=0; i < processes.list.size(); i++) {
        var process = processes.list[i];
        var cProcess = this.processes[process.id];
        if(cProcess != null && cProcess.isUsed()) {
          processId = process.id;
          break;
        } else if(cProcess != null) {
          if(processId == null) processId = process.id; //fallback...
        }
      }
    }
    var self = this;
    var button = $("bg_color_button");
    pwColorPicker.selectColor(processId, button, self.bgColor, 
    //showColorPicker(button, 
      function(c) { 
        if(c=="Transparent") {
          self.bgColor = "Transparent"; 
          button.style.backgroundColor = "";
          button.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
          self.canvas.style.backgroundColor = "";
        } else {
          self.bgColor = c.substr(1, 6);
          button.style.backgroundColor = c;
          button.style.backgroundImage = "";
          self.canvas.style.backgroundColor = c;
        }
        self.setReRender();
      }, {allow_transparency:true});
    
  },
  
  setChildReRender: function CPVA_setChildReRender(queueUpdate) {
    this.reRenderChild = true;
    this.renderVersion ++;
    this.configuredView.setChildReRender(queueUpdate);
  },
  
  setReRender: function CPVA_setReRender(queueUpdate) {
    this.reRender = true;
    this.renderVersion ++;
    this.configuredView.setChildReRender(queueUpdate);
  },
  
  clearReRender: function CPVA_clearReRender() {
    this.reRenderChild = false;
    this.reRender = false;
    for(var k in this.allItems) {
      this.allItems[k].clearReRender();
    }
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns total this views costs to decorate
  calculatePrice: function CPVA_calculatePrice(colorType, fullPriceData, priceData, percentMarkup, pricingState, forProductViewAreaProcess) {
    var areaTotal = priceZero();
    var areaPriceData = {
      id: this.productArea.id,
      type: 2,
      area: this,
      processes: new MapList()
    };
    for(var i=0; i < processes.list.size(); i++) {
      var process = processes.list[i];
      var cProcess = this.processes[process.id];
      if(forProductViewAreaProcess != null) {
        if(forProductViewAreaProcess.id == process.id) {
          areaTotal = cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup, pricingState);
          break;
        }
      } else {
        if(cProcess != null && cProcess.isUsed()) {
          areaTotal = areaTotal.add(cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup, pricingState));
        }
      }
    }
    if(areaTotal.cost > 0 || areaTotal.isOverriden) {
      areaPriceData.total = areaTotal;
      priceData.areas.add(areaPriceData);
    }
    return areaTotal;
  },
  
  setErrors: function CPVA_setErrors(errors) {
    if(errors==null) errors = {};
    if(this.lastErrors != null) {
      //clear all errors from last errors not in these errors before setting these errors...
      for(var k in this.lastErrors) {
        if(errors[k] == null) {
          var error = this.lastErrors[k];
          var item = this.allItems[error.cause];
          if(item != null) { //it may have been deleted...
            item.removeAlert(0, error.type);
          } else {
            this.configuredProduct.setAlertIcons(); //it may need to be recalced...
          }
        }
      }
    }
    for(var k in errors) {
      var error = errors[k];
      var item = this.allItems[error.cause];
      if(item != null) {
        var message = error.message == null ? ml(error.type) : error.message;
        if(this.lastErrors!=null && this.lastErrors[k] != null) { //same error...
          item.updateAlertMessage(0, error.type, "", message);
        } else {
          item.addAlert(0, error.type, "alert_" + error.type, "", message, true);
        }
      }
    }
    this.lastErrors = errors;
  },
  
  getItems: function CPVA_getItems(process, typeid) {
    if(typeof typeid == 'number') {
      typeid = [typeid];
    }
    var items = [];
    $H(this.allItems).each(function(pair) {
        item = pair.value;
        if(item.cViewProcess == process) {
          for(var i=0; i < typeid.length; i++) {
            if (item.itemType == typeid[i]) {
              items.push(item);
            }
          }
        }
    });
    
    return items;
  }
      
});

var ConfiguredViewProcess = Class.create({
  CLASSDEF: {
      name: 'ConfiguredViewProcess'
  },
  
  initialize: function CPVAP_initialize(configuredViewArea, productProcess) {
    this.id = productProcess.id;
    this.configuredViewArea = configuredViewArea;
    this.productProcess = productProcess;
    //build the div that will contain the 
    this.htmlId = d.getNextId();
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      this.managePaneFull = document.createElement("DIV");
      this.managePaneFull.id = "mp_c_" + this.htmlId;
      this.managePaneFull.className="manage_pane_width";
      this.managePaneFull.style.display = "none";
      
      this.managePaneFull.innerHTML = '<h3 class="manage_pane_header" style="display:none;" id="mp_h_' + this.htmlId + '">' + productProcess.process.name + '</h3>' +
      '<div id="mp_' + this.htmlId + '" class="manage_pane_body"></div>';
        
      configuredViewArea.managePane.appendChild(this.managePaneFull);
      
      this.managePane = $('mp_' + this.htmlId);
      this.managePaneHeader = $('mp_h_' + this.htmlId);
    }
    this.items = new MapList();
    this.visibleItems = new MapList();
    this.enabled = false;
    
    this.legacyKey = null;
    this.usingOverridingDec = false;
    this.usingOverridingClipart = false;
    this.overridingDecorationPrice = null;
    this.overridingClipartPrice = null;
    var self = this;
    this.clipArtObj = {
      getName: function() {
        self.getName() + ": Clipart Charge"; 
      }
    }
    this.decObj = {
      getName: function() {
        self.getName() + ": Decoration Charge"; 
      }
    }
  },
  
  getName: function() {
    return this.configuredViewArea.getName() + ": " + this.productProcess.getName();
  },
  
  reSortZOrder: function CPVAP_reSortZOrder() {
    
    this.items.resort();
    
    for(var i=0;i<this.items.list.size();i++) {
      var item = this.items.list[i];
      item.pos = i;
      
      if(i==0) {
        if(!item.isFirstZ) {
          //item.titleZUp.src = d.pathPrefix + "/images/mp/move_up_off.gif";
          //item.titleZUp.className="off";
          item.titleZDown.className="off";
          //this.items[i].titleZUp0.src = d.pathPrefix + "/images/mp/move_up_off.gif";
          item.isFirstZ = true;
        }
      } else if(item.isFirstZ) {
        //item.titleZUp.src = d.pathPrefix + "/images/mp/move_up_on.gif";
        //item.titleZUp.className=null;
        item.titleZDown.className=null;
        //this.items[i].titleZUp0.src = d.pathPrefix + "/images/mp/move_up_on.gif";
        item.isFirstZ = false;
      }
      
      if(i==this.items.list.size()-1) {
        if(!item.isLastZ) {
          //item.titleZDown.src = d.pathPrefix + "/images/mp/move_down_off.gif";
          //item.titleZDown.className="off";
          item.titleZUp.className="off";
          //this.items[i].titleZDown0.src = d.pathPrefix + "/images/mp/move_down_off.gif";
          item.isLastZ = true;
        }
      } else if(item.isLastZ) {
        //item.titleZDown.src = d.pathPrefix + "/images/mp/move_down_on.gif";  
        //item.titleZDown.className=null;
        item.titleZUp.className=null;
        //this.items[i].titleZDown0.src = d.pathPrefix + "/images/mp/move_down_on.gif";
        item.isLastZ = false;
      }
    }
  },
  
  initItems: function CPVAP_initItems() {
    for(var i=0; i < this.items.list.size(); i++) {
      var item = this.items.list[i];
      log("initing item:" + item.id);
      item.initialiseManagePane();
      item.addToDesigner(false);
      item.showPanel();
      log("inited item:" + item.id);
    }
    this.reSortZOrder();
  },
  
  addItem: function CPVAP_addItem(item) {
    if (item.itemVisible()) this.visibleItems.add(item);
    this.items.add(item);
    this.configuredViewArea.registerItem(item);
  },
  
  removeItem: function CPVAP_removeItem(item) {
    if (item.itemVisible()) this.items.remove(item.id);
    this.visibleItems.remove(item.id);
    this.configuredViewArea.deRegisterItem(item);
  },
  
  //called by the interface
  addNewItem: function CPVAP_addNewItem(newItem) {
    
    //set the top items up arrow to on
    if((this.items.list.size() > 0)&&(this.items.list[this.items.list.size()-1].titleZUp!=null)) {
      //this.items.list[0].titleZUp.src = d.pathPrefix + "/images/mp/move_up_on.gif";
      this.items.list[0].titleZUp.className=null;
    }
    this.addItem(newItem);
    newItem.initialiseManagePane();
    newItem.addToDesigner(false);
    newItem.showPanel();
    this.reSortZOrder();
    d.currentProductType.updatePrice();
  },
  
  deleteItem: function CPVAP_deleteItem(item) {
    this.removeItem(item);
    this.reSortZOrder();
    if(this.visibleItems.list.size() == 0) {
      this.configuredViewArea.updateAllowedProcesses();
      this.configuredViewArea.showManagementPanels();
    }
    d.currentProductType.updatePrice();
  },
  
  isOverridingPrices: function() {
    return (this.overridingDecorationPrice || this.overridingClipartPrice);
  },
  
  isUsed: function CPVAP_isUsed() {
    return (this.items.list.size() > 0);//(this.isOverridingPrices() || (this.items.list.size() > 0));
  },
  
  hasVisibleItems: function CPVAP_hasVisibleItems() {
    return (this.visibleItems.list.size() > 0);
  },
  
  isUsingItemOfType: function CPVAP_isUsingItemOfType(type) {
    for(var i=0; i < this.items.list.length; i++) {
      if(this.items.list[i].asset.getItemType() == type) {
        return true;
      }
    }
    return false;
  },
  
  showPanel: function CPVAP_showPanel(useHeader, onlyIfHasItems) {
    // return if already removed.
    if (this.managePaneFull == null) return;
    
    if(useHeader) {
      this.managePaneHeader.style.display="";
    } else {
      this.managePaneHeader.style.display="none";
    }
    if(onlyIfHasItems && !this.isUsed()) {
      this.managePaneFull.style.display="none";
    } else {
      this.managePaneFull.style.display="";
    }
  },
  
  hidePanel: function CPVAP_hidePanel() {
    if(this.managePaneFull != null) {
      this.managePaneFull.style.display="none";
    }
  },
  
  select: function CPVAP_select() {
    
  },
  
  remove: function CPVAP_remove() {
    for(var i=0;i<this.items.list.length;i++) {
      this.items.list[i].del();
    }
    if(this.managePaneFull != null) {
      this.managePaneFull.parentNode.removeChild(this.managePaneFull);
      this.managePaneFull = null;
    }
  },
  
  getNextZOrder: function CPVAP_getNextZOrder() {
    var maxZ = -1;
    for(var i=0;i<this.items.list.size();i++) {
      if(this.items.list[i].zIndex > maxZ) {
        maxZ = this.items.list[i].zIndex;
      }
    }
    return maxZ+1;
  },
  
  buildManagePane: function CPVAP_buildManagePane(item) {
    // return if existed
    var html = types[item.itemType];
    var re = new RegExp("\\[ID\\]", "g");
    html = html.replace(re,item.elId);

    var x = new Insertion.Top(this.managePane, html);
    
    //html = type_min.replace(re,item.id);
    //x = new Insertion.Top(this.managePanes[0], html);
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns [decTotal, assetTotal] this views costs to decorate in price objects
  calculatePrice: function CPVAP_calculatePrice(colorType, fullPriceData, priceData, percentMarkup, pricingState) {
    
    var processPriceState = pricingState.processes[this.productProcess.process.id];
    processPriceState.currentCount ++;
    var processPriceData = {
      id: this.productProcess.process.id,
      type: 3,
      process: this,
      priceType: 0,
      extras: []
    };
    var stitchCount = 0;
    var incompleteStitchCount = false; //do we have a stitch count for all the items?
    var digitizationFees = 0;
    var dynLegacyId = "";
    
    if(this.productProcess.process.isWilcomEMB()) {
      //get the thread count of each item used....
      
      if(this.items.list.size() == 0) {
        incompleteStitchCount = true;
      } else {
        for(var i=0;i < this.items.list.size(); i++) {
          var item = this.items.list[i];
          if((item.isWilcomEMB)||(item.digitize)) {
            log("emb item, item.digitize=" +item.digitize + ", item.digitizationFee=" + item.digitizationFee + ", item.stitchCount=" + item.stitchCount);
            if((item.digitize)&&(item.digitizationFee == null)) {
              item.updateDigitizationCosts();
            }
            
            if(item.stitchCount != null) {
              stitchCount += item.stitchCount;
            } else {
              log("Found incomplete stitch count");
              incompleteStitchCount = true;
            }
          } else if(item.isSavedDigitized) {
            if(item.stitchCount != null) {
              stitchCount += item.stitchCount;
            } else {
              log("Found incomplete stitch count");
              incompleteStitchCount = true;
            }
          }
          //We now display as separate cart item
          //if(item.digitize) {
          //  digitizationFees += item.digitizationFee;
          //}
        }
      }
      if(incompleteStitchCount) {
        var defaultStitchCount = this.productProcess.productTypeProcess.defaultStitchCount;
        if(defaultStitchCount==null) {
          defaultStitchCount = 0;
        }
        if(defaultStitchCount > stitchCount) {
          log("Reset stitch count from " + stitchCount + " to " + defaultStitchCount);
          stitchCount = defaultStitchCount;
        }
      }
    }
    
    var pricingData = this.productProcess.getPricingData();
    
    
    var cost = 0;
    
    if(this.productProcess.productTypeProcess.priceMode == 0) { //lwd
      cost = pricingData.prices[colorType];
      processPriceData.priceType = colorType;
      processPriceData.priceDelta = pricingData.prices[colorType] - pricingData.prices[0];
      
      dynLegacyId = "LWD:CT=" + colorType;
      
    } else if(this.productProcess.productTypeProcess.priceMode == 1) { //single price
      cost = pricingData.prices[0];
      processPriceData.priceType = -1;
      processPriceData.priceDelta = 0;
      dynLegacyId = "SP";
    } else { //lookup tables
      processPriceData.priceType = -2;
      
      var priceTable = pricingData.priceTable;
      var colorType = this.productProcess.process.usesColorTypes ? colorType : 0;
      if(this.productProcess.process.usesStitchCounts) {
        var embPriceData = priceTable.calcPrice(stitchCount, this.configuredViewArea.configuredProduct.qty, colorType);
      
        processPriceData.stitchCount = stitchCount;
        cost = embPriceData[0];
        processPriceData.threadCountLower = embPriceData[1];
        processPriceData.threadCountUpper = embPriceData[2];
        processPriceData.priceDelta = 0;
        dynLegacyId = "PT:Q=" + this.configuredViewArea.configuredProduct.qty + "&SC=" + stitchCount;
      } else {
        var tablePriceData = null;
        //if(!this.configuredViewArea.configuredProduct.product.useSingleDecorationPricing) {
          tablePriceData = priceTable.calcPrice(processPriceState.currentCount, this.configuredViewArea.configuredProduct.qty, colorType);
          cost = tablePriceData[0];
          log("priceTable.calcPrice, cost = " + cost);
          dynLegacyId = "PT:Q=" + this.configuredViewArea.configuredProduct.qty + "&CT=" + colorType + "&AC=" + processPriceState.currentCount;
        /*} else {
          
          tablePriceData = priceTable.calcSingleAreaPrice(processPriceState.totalUsed, this.configuredViewArea.configuredProduct.qty, colorType, processPriceState.currentCount); 
          cost = tablePriceData[0];
          log("calcSingleAreaPrice, cost = " + cost);
          processPriceData.priceDelta = tablePriceData[0] - priceTable.calcSingleAreaPrice(processPriceState.totalUsed, this.configuredViewArea.configuredProduct.qty, 0, processPriceState.currentCount)[0];
          dynLegacyId = "PT:Q=" + this.configuredViewArea.configuredProduct.qty + "&CT=" + colorType + "&AC=" + processPriceState.currentCount;
        }*/
      }
      log(priceData);
    }
    
    
    var usedAssetIds = [];
    var decLibCost = 0;
    //add decoration library asset charges
    for (var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      if(item.asset != null) {
        if(item.asset.decorationLibraryId != 0) {
          log('trying to calculate cost from process');
          var itemcost = item.asset.itemCost();
          log("item declib cost " + itemcost);
          if(itemcost > 0) {
            usedAssetIds.push(item.asset.id);
            decLibCost += itemcost;
            log('dec lib charge is now ' + decLibCost);
          }
        }
      }
    }
    
    usedAssetIds = usedAssetIds.sortBy( function(el) { el });
    
    var dynClipartLegacyId = usedAssetIds.join(",");
    
    log("legacyKey=" + this.legacyKey + ", dynLegacyId=" +dynLegacyId + ", legacyCKey=" + this.legacyCKey + ", dynClipartLegacyId=" + dynClipartLegacyId);
    
    
    var oDecPrice = null;
    var oClipArtPrice = null;
    var lDecPrice = null;
    var lClipArtPrice = null;
    if((!this.usingOverridingDec) && (dynLegacyId == this.legacyKey) && (d.useLegacyPrices)) {
      log("using legacy dec price " + this.overridingDecorationPrice);
      lDecPrice = this.overridingDecorationPrice;
    } else if(!this.usingOverridingDec) {
      log("not using legacy dec prices");
    } else {
      log("using override dec price " + this.overridingDecorationPrice);
      oDecPrice = this.overridingDecorationPrice;
    }
    if((!this.usingOverridingClipart) && (dynClipartLegacyId == this.legacyCKey) && (d.useLegacyPrices)) {
      log("using legacy clipart price " + this.overridingClipartPrice);
      lClipArtPrice = this.overridingClipartPrice;
    } else if(!this.usingOverridingClipart) {
      log("not using legacy clipart prices");
    } else {
      log("using override clipart price " + this.overridingClipartPrice);
      oClipArtPrice = this.overridingClipartPrice;
    }
    
    var price = priceFromCost(cost, d.commissionRate, percentMarkup, 0, oDecPrice, this.usingOverridingDec, lDecPrice);
    var clipArtPrice = priceFromCost(decLibCost, d.commissionRate, percentMarkup, 0, oClipArtPrice, this.usingOverridingClipart, lClipArtPrice);
    
    if(lDecPrice != null) {
      this.configuredViewArea.configuredProduct.registerLegacyPrice(lDecPrice, price.crp, this.decObj);
    }
    if(lClipArtPrice != null) {
      this.configuredViewArea.configuredProduct.registerLegacyPrice(lClipArtPrice, price.crp, this.clipArtObj);
    }
      
    processPriceData.clipartPrice = clipArtPrice;
    processPriceData.decorationPrice = price;
    
    
    
    //processPriceData.usingOverridingPrices = this.usingOverridingPrices;
    //processPriceData.overridingDecorationPrice = this.overridingDecorationPrice;
    //processPriceData.overridingClipartPrice = this.overridingClipartPrice;
    
    log("pre round price=" + price.toString() + ", " + clipArtPrice.toString());
    
    price.round();
    clipArtPrice.round();
    
    
    
    var totalPrice = price.add(clipArtPrice);
    totalPrice.round();
    
    log("price=" + price.toString() + ", clipArtPrice=" + clipArtPrice.toString() + ", totalPrice=" + totalPrice.toString());
    
    
    if(totalPrice.cost > 0 || totalPrice.isOverriden) {
      processPriceData.total = totalPrice;
      priceData.processes.add(processPriceData);
    }
    return totalPrice;
  },
  
  serialize: function CPVAP_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix="p[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[uod]") + "=" + encodeURIComponent(this.usingOverridingDec));
    queryComponents.push(encodeURIComponent(prefix + "[uoc]") + "=" + encodeURIComponent(this.usingOverridingClipart));
    queryComponents.push(encodeURIComponent(prefix + "[odp]") + "=" + encodeURIComponent(this.overridingDecorationPrice));
    queryComponents.push(encodeURIComponent(prefix + "[ocp]") + "=" + encodeURIComponent(this.overridingClipartPrice));
    
    return queryComponents.join('&');
  }
});


var TeamNames = Class.create({
  CLASSDEF: {
      name:  'TeamNames'
  },
  
  initialize: function TeamNames_initialize(configuredProduct) {
    this.configuredProduct = configuredProduct;
    this.names = new MapList(this);
    this.items = {};
    this.nextId = 1;
    this.selectedTeamName = null;
  },
  
  registerTeamNameItem: function TeamNames_registerTeamNameItem(teamNameItem) {
    this.items[teamNameItem.id] = teamNameItem;
  },
  
  unregisterTeamNameItem: function TeamNames_unregisterTeamNameItem(teamNameItem) {
    delete this.items[teamNameItem.id]
    if(hashFirstElement(this.items) == null) {
      this.configuredProduct.usingTeamnames = false;
      var hasSizes = (this.configuredProduct.product.type.sizeField != null);
      if(hasSizes) {
        var sizeField = this.configuredProduct.getSelectedSize();
        sizeField.reloadHtml();
      }
      this.configuredProduct.product.type.updatePrice();
    }
  },
  
  getNextId: function TeamNames_getNextId() {
    return this.nextId++;
  },
  
  load: function TeamNames_load(params) {
    for(var i=0; i < params.length; i++) {
      var nameParam = params[i];
      nameParam.id = this.getNextId();
      this.names.add(new TeamName(nameParam, this));
    }
    if((this.selectedTeamName == null)&&(this.names.list.length > 0)) {
      this.selectedTeamName = this.names.list[0];
    }
  },
  
  initNew: function TeamNames_initNew(newTeamNameItem) {
    this.newTeamNameItem = newTeamNameItem;
    this.loadTeamNameIds();
    var existingGroups = this.groupNamesBySizes();
    
    if(this.configuredProduct.product.type.sizeField != null) {
      var sizeField = this.configuredProduct.getSelectedSize();
      
      for(var i=0; i < sizeField.productField.options.list.length; i++) {
        var pOpt = sizeField.productField.options.list[i];
        if(!pOpt.def.isMulti) {
          if(pOpt.subs != null) {
            for(var k in pOpt.subs) {
              var pSub = pOpt.subs[k];
              var el = $('tn_mqs_' + pSub.id);
              var val = 0;
              if(el != null) {
                val = parseInt(el.value ,10);
                this.addTeamNames(val, pOpt.id, pSub.id, existingGroups);
              } else {
                log("Missing multi qty el for sub option " + pSub.id);
              }
              
            }
          } else {
            var el = $('tn_mq_' + pOpt.id);
            var val = 0;
            if(el != null) {
              val = parseInt(el.value ,10);
              this.addTeamNames(val, pOpt.id, null, existingGroups);
            } else {
              log("Missing multi qty el for option " + pOpt.id);
            }
          }
        }
      }
    } else {
      //just 1 qty...
      var qty = parseInt($("tn_qty").value, 10);
      this.addTeamNames(qty, null, null, existingGroups);
    }
    //remove any existing that are no longer used....
    for(var k in existingGroups) {
      var group = existingGroups[k];
      for(var i=0; i < group.length; i++) {
        this.removeMember(group[i].id, true);
      }
    }
    
    if(this.tmpNames.list.length == 0) {
      return false;
    }
    
    if((this.tmpSelectedTeamName == null)&&(this.tmpNames.list.length > 0)) {
      this.tmpSelectedTeamName = this.tmpNames.list[0];
    }

    $("teamname_edit_container").update(this.renderHtml());
    
    
    return true;
  },
  
  groupNamesBySizes: function TeamNames_groupNamesBySizes() {
    var grouping = {};
    for(var i=0; i < this.names.list.length; i++) {
      var name = this.names.list[i];
      var id = name.selectedId();
      if(grouping[id] == null) {
        grouping[id] = [];
      }
      grouping[id].push(name);
    }
    return grouping;
  },
  
  editTeamNames: function TeamNames_editTeamNames() {
    this.loadTeamNameIds();
    $("teamname_edit_container").update(this.renderHtml());
    popup('edit_teamname_popup');
  },
  
  //we keep a temp list of teamname ids so we know whats available to save when save is clicked.. means cancel has to do nothing
  loadTeamNameIds: function TeamNames_loadTeamNameIds() {
    this.tmpNames = this.names.clone();
    if((this.selectedTeamName == null)&&(this.names.list.length > 0)) {
      this.selectedTeamName = this.names.list[0];
    }
    this.tmpSelectedTeamName = this.selectedTeamName; //tmp var in case cancel is clicked...
  },
  
  cancelNew: function TeamNames_cancelNew() {
    if(this.newTeamNameItem != null) {
      this.newTeamNameItem.del();
      //this.unregisterTeamNameItem(this.newTeamNameItem);
      this.newTeamNameItem = null;
    }
  },
  
  //from init new
  addTeamNames: function TeamNames_addTeamNames(qty, optionId, subOptionId, existingGroups) {
    if(existingGroups != null) {
      var id = optionId;
      if(subOptionId) {
        id += "-" + this.subOptionId;
      }
      var existingGroup = existingGroups[id];
      if(existingGroup != null) {
        log("Found existing group:" + id);
        qty -= existingGroup.length;
        if(qty < 0) { //we need to remove some...
          existingGroup.reverse(); //take from end of list....
          while(qty < 0) {
            var toDel = existingGroup.pop();
            log("Removing existing group member:" + toDel.id);
            this.removeMember(toDel.id, true);
            qty ++;
          }
        }
        delete existingGroups[id]; //track which were used....
      }
    }
    
    for(var i=0; i < qty; i++) {
      this.tmpNames.add(new TeamName({id : this.getNextId(), o: optionId, s: subOptionId}, this));
    }
  },
  
  //from interface button 
  addMember: function TeamNames_addMember() {
    var options = null;
    var hasSizes = (this.configuredProduct.product.type.sizeField != null);
    
    if(hasSizes) {
      options = this.configuredProduct.product.type.sizeField.getOptionList(this.configuredProduct, this.configuredProduct.product.getSizeField(), this.configuredProduct.getSelectedSize(), this.configuredProduct.product.sizeColorCombinations, false);
    }
    
    var views = this.getItemsByAreas();
    var name = new TeamName({id : this.getNextId(), o: null, s: null}, this);
    this.tmpNames.add(name);

    var rowHtml = this.renderNameHtml(name, hasSizes,options,views, (this.tmpNames.list.length == 1));
    new Insertion.Bottom("tn_names_tbody", rowHtml);
  },
  
  //from interface button 
  removeMember: function TeamNames_removeMember(id, dontReselect) {
    if((this.tmpNames.list.length == 1)&&(dontReselect != true)) {
      alert(ml("You cannot remove the last teamname. If you do not want to use teamnames remove the teamname item from your design."));
      return;
    }
    var wasSelected = (this.tmpSelectedTeamName == this.tmpNames.byId[id]);
    this.tmpNames.remove(id);
    $('tn_' + id + '_row').remove();
    if(wasSelected && dontReselect) {
      this.tmpSelectedTeamName = null;
      this.selectedTeamName = null;
    } else if(wasSelected && this.tmpNames.list.length > 0) {
      this.tmpSelectedTeamName = this.tmpNames.list[0];
      $('tn_v_' + this.tmpSelectedTeamName.id).checked = true;
    }
  },
  
  //from UI
  saveTeamNames: function TeamNames_saveTeamNames() {
    if(this.tmpNames.list.length == 0) {
      alert(ml("You must have defined at least one teamname"));
      return;
    }
    this.saveTeamNamesFromUI();
    this.updateConfiguredProductFromTeamNames();
    d.track("save-teamnames");
    this.configuredProduct.setQtyFromMulti();
    this.configuredProduct.product.type.updatePrice();
    this.selectedTeamName = this.tmpSelectedTeamName;
    this.updateTeamNameItems();
    d.itemChanged();
  },
  
  //save from form elements back into data model
  saveTeamNamesFromUI: function TeamNames_saveTeamNamesFromUI() {
    if(this.newTeamNameItem != null) {
      this.newTeamNameItem = null;
    }
    var hasSizes = (this.configuredProduct.product.type.sizeField != null);

    var sizeField = hasSizes ? this.configuredProduct.getSelectedSize() : null;
    var views = this.getItemsByAreas();
    
    for(var i =0; i < this.tmpNames.list.length; i++) {
      this.tmpNames.list[i].clear();
    }
    this.names = this.tmpNames;
    for(var x =0; x < this.tmpNames.list.length; x++) {
      var name = this.tmpNames.list[x];
      if(hasSizes) {
        var sizeSelection = $F('tn_size_' + name.id);
        var ids = sizeField.extractOptIds(sizeSelection);
        name.optionId = ids[0];
        name.subOptionId = ids[1];
      }
                
      for(var i=0; i < views.length; i++) {
        var view = views[i];
        for(var j=0; j < view.areas.length; j++) {
          var area = view.areas[j];
          for(var k=0; k < area.items.length; k++) {
            var item = area.items[k];
            for(var l=0; l< item.teamNameTemplate.config.sections.length; l++) {
              var section = item.teamNameTemplate.config.sections[l];
              var nameData = $F('tn_' + name.id + '_' + item.id + '_' + l);
              name.setCellData(item.id, l, nameData);
            }
          }
        }
      }
    }
  },
  
  //update the size/qty of the configured product to match the config of the teamnames,,,
  updateConfiguredProductFromTeamNames: function TeamNames_updateConfiguredProductFromTeamNames() {
    var hasSizes = (this.configuredProduct.product.type.sizeField != null);
    this.configuredProduct.usingTeamnames = true;
    if(hasSizes) {
      var sizeField = this.configuredProduct.getSelectedSize();
      var productSizeField = this.configuredProduct.product.getSizeField();
      if(productSizeField.multiOption == null) {
        log("dynamically adding multiple sizes option to product to support teamnames");
        productSizeField.addMultiOption();
      }
      sizeField.setSelectedOption(productSizeField.multiOption.id, 1, null, true);
      
      //group by option selections...
      var sizeGrouping = {};
      for(var i=0; i < this.names.list.length; i++) {
        var name = this.names.list[i];
        var key = name.selectedId();
        if(sizeGrouping[key] == null) {
          sizeGrouping[key] = {qty: 0, optionId: name.optionId, subOptionId: name.subOptionId};
        }
        sizeGrouping[key].qty +=1;
      }
      for(var k in sizeGrouping) {
        var grouping = sizeGrouping[k];
        sizeField.addSelectedOption(grouping.optionId, grouping.qty, grouping.subOptionId);
      }
      sizeField.reloadHtml();
    } else {
      this.configuredProduct.qty = this.names.list.length;
    }
    
  },
  
  updateTeamNameItems: function TeamNames_updateTeamNameItems() {
    for(var k in this.items) {
      var item = this.items[k];
      item.setText(false, 2);
    }
    
  },
  
  renderHtml: function TeamNames_renderHtml() {
    var options = null;
    var hasSizes = (this.configuredProduct.product.type.sizeField != null);
    
    if(hasSizes) {
      options = this.configuredProduct.product.type.sizeField.getOptionList(this.configuredProduct, this.configuredProduct.product.getSizeField(), this.configuredProduct.getSelectedSize(), this.configuredProduct.product.sizeColorCombinations, false);
    }
    
    var views = this.getItemsByAreas();
    
    var cells = [];
    var rows = [];
    var useViewName = views.length > 1 ? true : false;
    if(hasSizes) {
      cells.push('<th>&nbsp</th>');
    }
    for(var i=0; i < views.length; i++) {
      var view = views[i];
      var useAreaName = view.areas.length > 1 ? true : false;
      for(var j=0; j < view.areas.length; j++) {
        var area = view.areas[j];
        var locationLabel = "";
        if(useViewName) {
          locationLabel = view.view.name;
          if(useAreaName) {
            locationLabel += " " + area.area.name;
          }
          locationLabel = " (" + locationLabel + ")";
        } else if(useAreaName) {
          locationLabel += " (" + area.area.name + ")";
        }
        for(var k=0; k < area.items.length; k++) {
          var item = area.items[k];
          for(var l=0; l< item.teamNameTemplate.config.sections.length; l++) {
            var section = item.teamNameTemplate.config.sections[l];
            var sName = (section.name == null) ? "Name " + (l+1) : section.name;
            sName += locationLabel;
            cells.push('<th>' + sName + '</th>');
          }
        }
      }
    }
    cells.push('<th>view</th>');
    cells.push('<th>remove</th>');
    header = '<tr>' + cells.join('') + '</tr>';
    
    for(var x=0; x < this.tmpNames.list.length; x++) {
      var name = this.tmpNames.list[x];
      
      rows.push('<tr>' + this.renderNameHtml(name, hasSizes,options,views, (this.names.list.length==1 || this.selectedTeamName==name) ) + '</tr>');
    }
    return "<table class='base hundred'>" + header + '<tbody id="tn_names_tbody">' + rows.join("\n") + "</tbody></table>";
  },
  
  renderNameHtml: function TeamNames_renderNameHtml(name, hasSizes,options,views, isSelected) {
    cells = [];
    if(hasSizes) {
      cells.push('<td><select id="tn_size_' + name.id + '">' + selectOptionHtml(options, name.selectedId() ) + '</select></td>');
    }
    for(var i=0; i < views.length; i++) {
      var view = views[i];
      for(var j=0; j < view.areas.length; j++) {
        var area = view.areas[j];
        for(var k=0; k < area.items.length; k++) {
          var item = area.items[k];
          for(var l=0; l< item.teamNameTemplate.config.sections.length; l++) {
            var section = item.teamNameTemplate.config.sections[l];
            var cellData = name.getCellData(item.id, l);
            if(cellData == null) cellData = "";
            cells.push('<td><input type="text" id="tn_' + name.id + '_' + item.id + '_' + l + '" value="' + cellData + '" size="15"/></td>');
          }
        }
      }
    }
    var selHtml = isSelected ? ' checked="true"' : '';
    cells.push('<td><input type="radio" id="tn_v_' + name.id + '" name="tn_view"' + selHtml + ' onclick="d.currentCProduct.getTeamNames().view(' + name.id + ');"/></td>');
    cells.push('<td><a href="#" onclick="d.currentCProduct.getTeamNames().removeMember(' + name.id + '); return false;" class="icon delete">delete</a></td>');
    return '<tr id="tn_' + name.id + '_row">' + cells.join('') + '</tr>';
  },
  
  getItemsByAreas: function TeamNames_getItemsByAreas() {
    var byViews = {};
    for(var k in this.items) {
      var item = this.items[k];
      if(byViews[item.cView.id] == null) {
        byViews[item.cView.id] = {};
      }
      if(byViews[item.cView.id][item.cViewArea.id] == null) {
        byViews[item.cView.id][item.cViewArea.id] = [];
      }
      byViews[item.cView.id][item.cViewArea.id].push(item);
    }
    
    var views = [];
    //we want these in the same order as what is defined in the product...
    for(var i=0; i < this.configuredProduct.product.views.list.length; i++) {
      var view = this.configuredProduct.product.views.list[i];
      if(byViews[view.id] != null) {
        var viewData = {view: view, areas:[]};
        for(var j=0; j < view.areas.list.length; j++) {
          var area = view.areas.list[j];
          if(byViews[view.id][area.id] != null) {
            viewData.areas.push({area:area, items: byViews[view.id][area.id]});
          }
        }
        views.push(viewData);
      }
    }
    return views;
  },
  
  view: function TeamNames_view(id) {
    this.tmpSelectedTeamName = this.tmpNames.byId[id];
  },
  
  serialize: function TeamNames_serialize(queryComponents, prefix) {
    for(var i=0; i < this.names.list.length; i++) {
      this.names.list[i].serialize(queryComponents, prefix + "[" + i + "]");
    }
  },
  
  serializeToOptions: function TeamNames_serializeToOptions() {
    var o = {};
    for(var i=0; i < this.names.list.length; i++) {
      o[i] = this.names.list[i].serializeToOptions();
    }
    return o;
  }
  
  
});


var TeamName = Class.create({
  CLASSDEF: {
      name:  'TeamName'
  },
  
  initialize: function TeamName_initialize(config, teamNames) {
    this.id = config.id;
    this.optionId = config.o;
    this.subOptionId = config.s;
    this.names = { names:config.n};
    this.teamNames = teamNames;
    if(this.names == null) this.names = {};
    if(this.names.names == null) this.names.names = {};
  },
  
  selectedId: function TeamName_selectedId() {
    if(this.subOptionId != null) {
      return this.optionId + "-" + this.subOptionId;
    } else {
      return this.optionId;
    }
  },
  
  clear: function() {
    this.names.names = {};
  },
  
  getCellData: function TeamName_getCellData(item_id, sectionIdx) {
    return this.names.names[item_id + '_' + sectionIdx];
  },
  
  setCellData: function TeamName_setCellData(item_id, sectionIdx, value) {
    this.names.names[item_id + '_' + sectionIdx] = value;
  },
  
  serialize: function TeamName_serialize(queryComponents, prefix) {
    if(this.optionId != null) {
      queryComponents.push(encodeURIComponent(prefix + "[o]") + "=" + encodeURIComponent(this.optionId));
    }
    if(this.subOptionId != null) {
      queryComponents.push(encodeURIComponent(prefix + "[s]") + "=" + encodeURIComponent(this.subOptionId));
    }
    for(var k in this.names.names) {
      queryComponents.push(encodeURIComponent(prefix + "[n][" + k + "]") + "=" + encodeURIComponent(this.names.names[k]));
    }
  },
  
  serializeToOptions: function TeamName_serializeToOptions() {
    var o = {};
    if(this.optionId != null) {
      o.o = this.optionId;
    }
    if(this.subOptionId != null) {
      o.s = this.subOptionId;
    }
    var names = {}; 
    for(var k in this.names.names) {
      names.k = this.names.names[k];
    }
    o.n = names;
    return o;
  }
});

var Product = Class.create({
  CLASSDEF: {
      name: 'Product'
  },
  
  initialize: function P_initialize(id, options) {
    this.id = id;
    this.options = options;
    this.name = options.name;
    this.code = options.code;
    this.description = options.desc;
    
    this.type = d.productTypes[options.type];
    if(this.type.noCategores) {
      this.categoryId = -1;
      this.subCategoryId = -1;
    } else {
      this.categoryId = options.cid;
      this.subCategoryId = options.sc;
    }
    
    this.availableProcesses = {};
    this.displayImage = options.di;
    this.imageURL = options.u;
    this.largeImageURL = options.u2;
    this.sizeChartURL = options.szc;
    this.sizeTable = options.szt;
    
    this.price = [parseFloat(options.p[0]), parseFloat(options.p[1]), parseFloat(options.p[2])];
    
    this.taxExempt = options.te;
    
    this.minQty = options.mq;
    if(this.minQty == -1) {
      this.minQty = this.type.minQty;
      this.bundleSize = this.type.bundleSize;
    } else {
      this.bundleSize = options.bs;
    }
    
    this.discountId = options.discid;
    this.discountToBase = options.datb;
    
    this.useDefaultDecorationPricing = options.uddp;
    if(this.useDefaultDecorationPricing) {
      //load the product as if it had single decoration pricing, but all pointing to default prices....
      this.useSingleDecorationPricing = true;
      this.singleDecPricing = new MapList(this);
      for(var i=0; i < this.type.processes.list.length; i++) {
        var tproc = this.type.processes.list[i];
        this.singleDecPricing.add({ id: tproc.id, use_default: true}); 
      }
    } else {
      this.useSingleDecorationPricing = options.sp;
      if(this.useSingleDecorationPricing) {
        this.singleDecPricing = new MapList(this);
        for(var i=0; i < options.spp.length; i++) {
          if(options.spp[i].pt != null) {
            options.spp[i].priceTable = d.priceTables[options.spp[i].pt];
            if(options.spp[i].priceTable == null) {
              options.spp[i].priceTable = this.type.processes.byId[options.spp[i].id].getPriceTable();
              log("ERROR: unable to get price table '" + options.spp[i].pt + "', defaulting to:" + (options.spp[i].priceTable == null ? 'null' : options.spp[i].priceTable.id) );
            }
          }
          this.singleDecPricing.add(options.spp[i]);
        }
      }
    }
    
    
    
    this.views = new MapList(this);
  
    for(var i=0; i < options.v.size(); i++) {
      this.views.add(new ProductView(this, options.v[i], i));
    }
    
    log("Product(): Loaded Views");
    
    this.defaultView = this.views.byId[options.sdv];
    this.displayView = this.views.byId[options.sdisv];
    
    this.colors = new MapList(this);
    this.discontinuedColors = new MapList(this);
    
    var pc = options.c;
    for(var i=0;i<pc.length;i++) {
      var l = new ProductColor(this, pc[i]);
      if (l.discontinued) {
        this.discontinuedColors.add(l);
      } else {
        this.colors.add(l);
      }
    }
    log("Product(): Loaded Colors");
    this.defaultColor = this.colors.byId[options.dc];
    
    this.hasDerivedViews = options.vad;
    
    
    this.optionsByProdChosenOptId = {}; //mapping from product_chosen_option_id to FieldChoice
    this.fields = new MapList(this);
    for(var i=0; i < options.f.size(); i++) {
      var pf = new ProductField(this, options.f[i]);
      if(pf.fieldDef == null) {
        log("Dropping field " + options.f[i].id + " as it is not in the defn");
      } else {
        this.fields.add(pf);
      }
    }
    log("Product(): Loaded Fields");
    //dynamically add all fields that are default on yet are not in the product def...
    for(var i=0; i < this.type.fields.list.length; i++) {
      var fieldDef = this.type.fields.list[i];
      if(fieldDef.defaultOn && this.fields.byId[fieldDef.id] == null) {
        var pf = new ProductField(this, {id:fieldDef.id, setupDefaults:true, u:true, uDef: true, opts: fieldDef.getProductDefaultOptions()});
        this.fields.add(pf);
        log("Added defaultOn field " + fieldDef.name);
      }
    }
    this.limitSizeColors = false;
    if(options.scc != null) {
      this.sizeColorCombinations = options.scc;
      this.limitSizeColors = true;
    } else {
      this.sizeColorCombinations = null;
    }
    log(this);
  },
  
  getDisplayImageURL: function P_getDisplayImageURL() {
    if(this.displayImage != null) {
      return this.displayImage;
    } else {
      return this.displayView.getViewURL(2, this.defaultColor.productChosenOptionId, 1);
    }
  },
  
  getOverlayURL: function P_getOverlayURL(layoutId, color, size) {
    var l = this.layoutsById[layoutId];
    if(l != null) {
      var url = l.oUrl.replace(d.layoutViewUrlCID, color);
      return url.replace(d.layoutViewUrlS, size); //d.pathPrefix + 
    }
    return "";
  },
  
  getColor: function P_getColor(id) {
    return this.colorsById[id];
  },
  
  //get the first used process...
  getFirstProcessId: function P_getFirstProcessId() {
    for(var i=0; i < this.views.list.size(); i++) {
      var v = this.views.list[i];
      for(var j=0; j < v.areas.list.size(); j++) {
        var a= v.areas.list[j];
        for(var k=0; k < a.processes.list.size(); k++) {
          return a.processes.list[k].id;
        }
      }
    }
  },
  
  getSizeField: function P_getSizeField() {
    if(this.type.sizeField == null) {
      return null;
    }
    var pField = this.fields.byId[this.type.sizeField.id];
    if (pField && pField.used) return pField;
    return null;
  },
  
  getSizesHtml: function P_getSizesHtml() {
    var pSizeField = this.getSizeField();
    if (pSizeField == null) return '';
    
    var html = '';
    for(var i=0; i < pSizeField.options.list.length; i++) {
      var pOpt = pSizeField.options.list[i];
      if(!pOpt.def.isMulti) {
        html += pOpt.def.value + ' ';
        
        if(pOpt.def.subs != null) {
          for(var j=0; j < pOpt.def.subs.length; j++) {
            var pSubOpt = pOpt.subs[pOpt.def.subs[j].id];
            if(pSubOpt != null) {
              html += pSubOpt.def.value + ' ';
            }
          }
        }
      }
    }
    return html;
  },
  
  usesMinQty: function P_usesMinQty() {  
    return (this.minQty > 1);
  },
  
  buildQtyDropdownHtml: function(curQty) {
    var items = [];
    var inc = this.bundleSize;
    var q = this.minQty;
    for(var i=0; i < 8; i++) {
      var ev = "";
      var clz = "";
      if(curQty != q) {
        ev = ' onmousemove="this.className=\'over\';" onmouseout="this.className=null;" ';
      } else {
        clz = ' class="alt"';
      }
      items.push('<li' + clz + ev + ' onmousedown="d.qtyDropDownSelected(' + q + ');">' + q + '</li>');
      q += inc;
      /*if(q == 25 || q == 50 || q == 100) {
        inc = q;
      } else {
        inc *= 2;
      }*/
    }
    return '<ul>' + items.join("\n") + '</ul>';
  },
  
  registerAvailableProcess: function P_registerAvailableProcess(processId) {
    this.availableProcesses[processId] = true;
  },
  
  canDecorate: function P_canDecorate(process) {
    return (this.availableProcesses[process.id] == true);
  },
  
  //get the disabled message.. for product it would be '%1s is not available on this %2s'
  //if we cant decorate because the product cant, use the products getDecProcDisabledMessage
  getDecProcDisabledMessage: function P_getDecProcDisabledMessage(process) {
    if(this.type.canDecorate(process)) {
      return process.name + " is not available on this " + this.name;
    } else {
      return this.type.getDecProcDisabledMessage(process);
    }
  }
});   

var ProductField = Class.create({
  CLASSDEF: {
      name: 'ProductField'
  },
  
  initialize: function PF_initialize(product, options) {
    this.product = product;
    this.valid = true;
    this.id = options.id; 
    this.sfid = options.sfid;
    this.fieldDef = this.product.type.fields.byId[this.id];
    if(this.fieldDef == null) return; //why bother continueing?
    
    this.used = options.u;
    this.priceDelta = options.d;
    this.usePriceDefaults = options.uDef;
    this.options = new MapList(this);
    
    this.multiOption = null;
    if(this.used) {
      if(this.fieldDef.typeOptions.list) {
        //log("loading options for " + this.fieldDef.name);
        for(var i=0; i < options.opts.length; i++) {
          //log("loading option " + options.opts[i].id);
          var opt = this.options.add(new ProductFieldChoice(this, options.opts[i]));
          if(opt.def == null) { //invalid data...
            log("ERROR: ProductFieldChoice " + opt.id + " has missing def on product " + product.name);
            this.options.remove(opt.id);
          } else if(opt.def.isMulti && this.product.usesMinQty()) {
            log("NOTE: ProductFieldChoice " + opt.id + " has is multi but product uses min qty " + product.name);
            this.options.remove(opt.id);
          } else {
            if(opt.selected) {
              this.defaultOption = opt;
              //log("found default")
            }
            this.product.optionsByProdChosenOptId[opt.pcoid] = opt;
            if(opt.def.isMulti) {
              this.multiOption = opt;
            }
          }
        }
        if((options.opts.length ==0)&&(this.fieldDef.typeOptions.list)) {
          log("Invalid field " + this.fieldDef.name + " has no options");
          this.valid = false;
        }
        if(this.valid) {
          if(this.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE  && !this.product.usesMinQty() && this.multiOption == null && this.fieldDef.productType.ms && this.fieldDef.multiOption!=null) { //there is no multi option def for this product but the type says only show in multi option mode...
            log("Adding Multi Choice because type has multi only defined");
            this.options.add(new ProductFieldChoice(this, {id:this.fieldDef.multiOption.id, s:true }));
          }
          
          if(this.defaultOption == null) {
            log("ERROR: no default option loaded... using first option");
            this.defaultOption = this.options.list[0];
          }
        }
      }
    } else {
      log("Field " + this.fieldDef.name + " is not used"); 
    }
    
  },
  
  //used when swapping products.. try and find a matching option....
  findOption: function(otherOption) {
    var foundOption = null;
    if(this.options.byId[otherOption.id] != null) {
      foundOption = this.options.byId[otherOption.id];
    } else {
      for(var i=0; i < this.options.list.length; i++) {
        var opt = this.options.list[i];
        if(opt.def.name == otherOption.def.name) {
          foundOption = opt;
          break;
        } else if(opt.def.value == otherOption.def.value) {
          foundOption = opt;
        }
      }
    }
    return foundOption;
  },
  
  //dynamically add multi option to support teamnames...
  addMultiOption: function PF_addMultiOption() {
    this.options.add(this.options.add(new ProductFieldChoice(this, {id:this.fieldDef.multiOption.id, s:true })));
  }
});

var ProductFieldChoice = Class.create({
  CLASSDEF: {
      name: 'ProductFieldChoice'
  },

  initialize: function PFC_initialize(field, options) {
    this.field = field;
    this.id = options.id; //product_option.id
    this.pcoid = options.pcoid; //product_chosen_option.id
    this.spcoId = options.spcoId;  //supplier_product_chosen_option.id
    this.def = field.product.type.optionsById[this.id];
    this.priceDelta = options.d;
    //this.sizeDelta = parseFloat(options.sd);
    //this.xOffset = parseInt(options.x, 10);
    //this.yOffset = parseInt(options.y, 10);
    this.selected = options.s;
    if(options.sub != null && options.sub.length > 0) {
      this.subs = {};
      var added = false;
      for(var i=0; i < options.sub.length; i++) {
        var pfsc = new ProductFieldSubChoice(this, options.sub[i]);
        if(pfsc.def != null) {
          this.subs[pfsc.id] = pfsc;
          added = true;
        } else {
          log("dropped missing product sub option " + pfsc.id);
        }
      }
      if(!added) this.subs = null;
    } else {
      this.subs = null;
    }
  },
  
  getSubOption: function PFC_getSubOption(id) {
    if(this.subs == null) return null;
    return this.subs[id];
  },
  
  //used when swapping products.. try and find a matching option....
  findOption: function(otherOption) {
    if(this.subs == null) {
      return null;
    }
    var foundOption = null;
    if(this.subs[otherOption.id] != null) {
      foundOption = this.subs[otherOption.id];
    } else {
      for(var i in this.subs) {
        var opt = this.subs[i];
        if(opt.def.name == otherOption.def.name) {
          foundOption = opt;
          break;
        } else if(opt.def.value == otherOption.def.value) {
          foundOption = opt;
        }
      }
    }
    return foundOption;
  }
});

var ProductFieldSubChoice = Class.create({
  CLASSDEF: {
      name: 'ProductFieldSubChoice'
  },

  initialize: function PFSC_initialize(pFieldChoice, options) {
    this.pFieldChoice = pFieldChoice;
    this.id = options.id; //product_option.id
    this.def = pFieldChoice.def.getSubOption(this.id);
    this.priceDelta = options.d;
   
    this.selected = options.s;
  }
});

var ProductView = Class.create({
  CLASSDEF: {
      name: 'ProductView'
  },
  
  initialize: function PV_initialize(product, options, viewIndex) {
    this.product = product;
    this.id = options.id;
    this.name = options.name;
    this.url = options.url;
    this.viewIndex = viewIndex;
  
    this.allowDesign = options.ad;
    this.allowView = options.av;
    
    this.offsetX = parseInt(options.x, 10);
    this.offsetY = parseInt(options.y, 10);
    
    this.areas = new MapList(this);

    this.availableProcesses = {};
    
    for(var i=0; i < options.a.size(); i++) {
      var area = new ProductViewArea(this, options.a[i], i)
      if (area.isValid()) this.areas.add(area);
    }
  },
  
  //allowStartUrl: a view can have a start url when loading existing configured items/custom products
  //allowScale: should we lookup and layout modifiers to see if the scale changes...
  getViewURL: function PV_getViewURL(size, colorId, scale) {
    var url = this.url.replace(d.layoutViewUrlCID, colorId);
    url = url.replace(d.layoutViewUrlS, size );
    return url.replace(d.layoutViewUrlSc, scale );
  },
  
  buildSelectorHtml: function PV_buildSelectorHtml(colorId, configuredView, customProduct, state) {
    if(!this.allowView) {
      return "";
    }
    var url = null;
    if(configuredView != null) {
      url = configuredView.getViewURL(11, true, false, state);
    } else if(customProduct!=null) {
      url = customProduct.getViewURL(this.id, 11, colorId, 1);
    } 
    if(url == null) {
      url = this.getViewURL(11, colorId, 1);
    }
    if((this.allowDesign)||(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      var clz = (this.id == d.selectedViewId) ? "d_layout_selected" : "d_layout_unselected";
      return '<li id="d_l_' + this.id + '" class="' + clz + '" onclick="d.viewClick(' + this.id + ');" onmousemove="d.viewMouseMove(this);" onmouseout="d.viewMouseOut(this);"><span></span><b id="d_l_s_' + this.id + '">&nbsp;</b><img id="d_l_i_' + this.id + '" src="' + url + '" onload="d.checkAreaHighlightPosition(' + this.id + ');"/><div id="d_l_ah_' + this.id + '" class="sel_area_highlight" style="display:none;">&nbsp;</div><label>'+this.name+'</label></li>';
    } else {
      return '<li id="d_l_' + this.id + '" class="unselectable"><img id="d_l_i_' + this.id + '" src="' + url + '"/><label>'+this.name+'</label></li>';
    }
  },
  
  updateAreaSelectorHtml: function PV_updateAreaSelectorHtml() {
    if(this.areas.list.size() > 1) {
      var html = "<h4>" + ml("Areas") + "</h4><ul class='areas'>";
      for(var i=0; i < this.areas.list.size(); i++) {
        var area = this.areas.list[i];
        html += '<li><input type="radio" name="selected_area" onclick="d.selectCurrentArea(' + area.id + ', true);" id="a_sel_' + area.id + '" value="' + area.id + '"/> <label for="a_sel_' + area.id + '">' + area.name + '</label></li>';
      }
      html += '</ul>';
      $("area_selector_container").innerHTML = html;
      $("area_selector_container").style.display="";
    } else {
      $("area_selector_container").style.display="none";
    }
  },
  
  //get the first area using the currently selected process...
  getDefaultPricingProcess: function PV_getDefaultPricingProcess(defaultProcessId) {
    if(defaultProcessId == null) {
      defaultProcessId = d.userSelectedProcessId;
    }
    for(var i=0; i < this.areas.list.size(); i++) {
      var area = this.areas.list[i];
      if(defaultProcessId == null) {
        return area.processes.list[0];
      }
      if(area.processes.byId[defaultProcessId] != null) {
        return area.processes.byId[defaultProcessId];
      }
    }
    return null;
  },
  
  registerAvailableProcess: function PV_registerAvailableProcess(processId) {
    this.availableProcesses[processId] = true;
    this.product.registerAvailableProcess(processId);
  },
  
  canDecorate: function PV_canDecorate(process) {
    return (this.availableProcesses[process.id] == true);
  },
  
  //get the disabled message.. for view it would be '%1s is not available on the %2s of the %3s'
  //if we cant decorate because the product cant, use the products getDecProcDisabledMessage
  getDecProcDisabledMessage: function PV_getDecProcDisabledMessage(process) {
    if(this.product.canDecorate(process)) {
      return process.name + " is not available on the " + this.name + " of the " + this.product.name;
    } else {
      return this.product.getDecProcDisabledMessage(process);
    }
  }
});

var ProductViewArea = Class.create({
  CLASSDEF: {
      name: 'ProductViewArea'
  },
  
  initialize: function PVA_initialize(view, options, areaIndex) {
    this.view = view;
    this.id = options.id;
    this.areaIndex = areaIndex;
    
    this.name = options.name;
    
    this.l = options.l;
    this.t = options.t;
    this.w = options.w;
    this.h = options.h;
    
    this.actualWidth = options.aw;
    this.actualHeight = options.ah;
    
    
    this.canSetBgColor = options.bgc;
    
    
    if(this.h > this.w) {
      this.reScale = 380.0 / parseFloat(this.h);
    } else {
      this.reScale = 380.0 / parseFloat(this.w);
    }
    
    this.dH = parseInt(this.h * this.reScale, 10);
    this.dW = parseInt(this.w * this.reScale, 10);
    this.dL = parseInt((400 - this.dW)/2, 10);
    this.dT = parseInt((400 - this.dH)/2, 10);
    
    
    var mask = options.m;
    this.maskId = mask.id;
    this.maskUrl = mask.url;
    
    this.processes = new MapList(this);
    for(var i=0; i < options.p.size(); i++) {
      var opts = options.p[i];
      var ptp = this.view.product.type.processes.byId[opts.id];
      if(ptp != null) { //check that the process is still supported....
        this.processes.add(new ProductViewAreaProcess(this, opts));
        this.view.registerAvailableProcess(ptp.id);
      }
    }
    this.processes.resort();
  },
  
  isValid: function PVA_isValid() {
    //check if any processes can be used
    for(var i=0; i < this.processes.list.size(); i++) {
      var p = this.processes.list[i];
      var process = processes.byId[p.id];
      if (process != null && process.isValid()) return true;
    }
    return false;
  },
  
  getName: function PVA_getName() {
    return this.view.name + "-" + this.name + " (" + this.view.product.name + ")";
  },
  
  canDecorate: function PVA_canDecorate(process) {
    return (this.processes.byId[process.id] != null);
  },
  
  //get the disabled message.. for area it would be '%1s is not available on the %2s'
  //if we cant decorate because the view cant, use the views getDecProcDisabledMessage
  getDecProcDisabledMessage: function PVA_getDecProcDisabledMessage(process) {
    if(this.view.canDecorate(process)) {
      return process.name + " is not available on the " + this.name;
    } else {
      return this.view.getDecProcDisabledMessage(process);
    }
  },
  
  isInArea: function PVA_isInArea(x,y) {
    if(x > this.l && x < (this.l + this.w) && y > this.t && y < (this.t + this.h)) {
      return true;
    } else {
      return false;
    } 
  }
});

var ProductViewAreaProcess = Class.create({
  CLASSDEF: {
      name: 'ProductViewAreaProcess'
  },
  
  initialize: function PVAP_initialize(viewArea, options) {
    this.viewArea = viewArea;
    this.id = options.id;
    
    this.productTypeProcess = this.viewArea.view.product.type.processes.byId[this.id];
    this.process = this.productTypeProcess.process;
    
    this.perfectDPI = options.pdpi;
    this.minDPI = options.mdpi;
    
    if((this.perfectDPI == null)||(this.process.isWilcomEMB())) { //this process does not use DPI... use web dpi..
      this.perfectDPI = 192;
      this.minDPI = 192;
    }
    
    this.fullWidth = this.viewArea.actualWidth * this.perfectDPI;
    this.fullHeight = this.viewArea.actualHeight * this.perfectDPI;
    
    this.layoutScale = parseFloat(this.viewArea.w) / parseFloat(this.fullWidth);
    this.designScale = parseFloat(this.viewArea.dW) / parseFloat(this.fullWidth);
    
    if(options.uddp) {
      this.useDefaultDecorationPricing = true;
    } else {
      this.useDefaultDecorationPricing = false;
      this.prices = options.d;
      if(this.productTypeProcess.priceMode==2) {//price table
        this.priceTable = d.priceTables[options.pt];
        if(this.priceTable == null) {
          this.priceTable = this.productTypeProcess.getPriceTable();
          log("ERROR: unable to get price table '" + options.pt + "' (for product process) defaulting to:" + (this.priceTable == null ? 'null' : this.priceTable.id));
        }
      }
    }
  },
  
  //sort processes by zindex descending
  compare: function PVAP_compare(other) {
    if (other.process === undefined) return -1
    return  other.process.zIndex - this.process.zIndex;
  },
  
  //we charge for 1 decoration even if no decorations are used...
  getDefaultDecorationPrice: function PVAP_getDefaultDecorationPrice(colorType) {
    var pricingData = this.getPricingData();
    
    if(this.productTypeProcess.priceMode == 0) {
      return pricingData.prices[colorType];
    } else if(this.productTypeProcess.priceMode == 1) {
      return pricingData.prices[0];
    } else {
      //get the price of the default stitch count....
      if(pricingData.priceTable == null) {
        log("ERROR: Unable to get default decoration price: priceTable is null");
      } else {
        var colorType = this.productTypeProcess.process.usesColorTypes ? colorType : 0;
        if(this.productTypeProcess.process.usesStitchCounts) {
          log("using price table to get price of default stitch count (" + this.productTypeProcess.defaultStitchCount + ")");
          return pricingData.priceTable.calcPrice(this.productTypeProcess.defaultStitchCount, d.currentCProduct.qty, colorType)[0];
        } else {
          log("using price table to get price of default area count (1)");
          return pricingData.priceTable.calcPrice(1, d.currentCProduct.qty, colorType)[0];
        }
      }
      return 0;
    }
  },
  
  canModifyDesign: function() {
    if(d.mode == DESIGNER_MODE_AMEND) {
      if(this.productTypeProcess.priceMode == 2) { //price table
        return false;
      }
    }
    return true;
  },
  
  getPricingData: function() {
    if(this.viewArea.view.product.useSingleDecorationPricing) {
      var pData = this.viewArea.view.product.singleDecPricing.byId[this.process.id];
      if(pData == null) {
        log("ERROR: unable to get single dec pricing for process " + this.process.id);
        return this.process;
      }
      if(pData.use_default) {
        if(this.productTypeProcess.useDefaultPricing) {
          return this.process;
        } else {
          return this.productTypeProcess;
        }
      }
      return this.viewArea.view.product.singleDecPricing.byId[this.process.id];
    } else if(this.useDefaultDecorationPricing) {
      if(this.productTypeProcess.useDefaultPricing) {
        return this.process;
      } else {
        return this.productTypeProcess;
      }
    }
    return this;
  }
});

var ProductColor = Class.create({
  CLASSDEF: {
      name: 'ProductColor'
  },
  
  initialize: function PClr_initialize(product, options) {
    this.product = product;
    this.id = options.id;
    this.productChosenOptionId = options.pc;
    this.colors = options.c;
    this.discontinued = options.dc;
    this.delta = parseFloat(options.d);
    this.color_type = parseInt(options.ct, 10);
  },
  
  getDelta: function PClr_getDelta(product, color_type) {
    return 0;
  },
  
  getFieldChoice: function PClr_getFieldChoice(product) {
    return null;
  },
  
  getName: function PClr_getName() {
    var colorName="";
    for(var k=0; k < this.colors.length; k++) {
      colorName+= this.colors[k][1] + ' ';
    }
    return colorName;
  },
  
  // only for OMS
  cellHtml: function PClr_cellHtml(allowPopup, showOverlay, cp, height, id, selected, disabled, comment) {
    if (!height) height = 25;
    var selHtml = (selected) ? " selected" : '';
    var idHtml = (id) ? ' id="' + id + '"' : '';
    var disabledHtml = (disabled) ? ' disabled' : '';
    if (!comment) comment = this.getName();
    var rolloverHtml = ' onmouseover="orderManager.popupManager.showRollover(this, \'' + escape('<p>' + comment + '</p>') + '\');" onmouseout="orderManager.popupManager.hideRollover(this);"';
    var h = parseInt(height / this.colors.length);
    
    var html = '<span' + idHtml + ' class="colorbox' + ((allowPopup) ? ' popup_button' : '') + selHtml + disabledHtml + '"' + ((allowPopup) ? ' onclick="orderManager.popupManager.togglePopup(\'item_color\', this, {cpid:' + cp.id + '}); return false;"' : '') + rolloverHtml + '>';
    for(var j=0; j < this.colors.length;j++) {
      html += '<span style="background-color: ' + this.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
    }
    if (showOverlay) html += '<b class="overlay" ' + rolloverHtml + '>&nbsp;</b></span>';
    
    return html;
  }
});


var CustomProduct = Class.create({
  CLASSDEF: {
      name: 'CustomProduct'
  },
  
  initialize: function CProd_initialize(id, options) {
    this.id = id;
    this.name = options.n;
    this.viewOptions = options.v;
    this.productId = options.p;
    this.markup = options.m;
    this.useDefaultPricing = options.udp;
    this.defaultColor = options.color;
    this.allColors = options.allColors;
    if(!this.allColors) {
      this.colors = options.colors;
    }
    this.defaultProcessId = options.def_proc;
    this.configuredPrice = options.cp;
    this.viewUrls = {}; //this contains a map of strings with urls to the views in /xxx[CID]/[SIZE]/xxx format
    for(var i=0; i < options.u.length;i++) {
      var urlInfo = options.u[i];
      this.viewUrls[urlInfo.id] = urlInfo.u;
    }
  },
  
  //get the view url for the size and color....
  getViewURL: function CProd_getViewURL(viewId, size, colorId, scale) {
    var viewUrl = this.viewUrls[viewId];
    if(viewUrl == null) {
      return null;
    }
    var url = viewUrl.replace(d.layoutViewUrlCID, colorId);
    url = url.replace(d.layoutViewUrlS, size );
    return url.replace(d.layoutViewUrlSc, scale );
  },
  
  getColors: function CProd_getColors() {
    if(this.allColors) {
      return d.productsById[this.productId].colors;
    } else {
      if(this.availableColors == null) {
        var prod = d.productsById[this.productId];
        this.availableColors = new MapList();
        for(var i=0; i < this.colors.length;i++) {
          var c = prod.colors.byId[this.colors[i]];
          if(c != null) {
            this.availableColors.add(c);
          }
        }
      }
      return this.availableColors;
    }
  }
});

var ProductType = Class.create({
  CLASSDEF: {
      name: 'ProductType'
  },
  
  initialize: function PT_initialize(id, options) {
    this.id = id;
    this.options = options;
    this.optionsById = new Hash();
    this.cats = [];
    this.catsById = new Hash();
    this.catsLoaded = false;
    this.name = options.name;
    this.ms = options.ms;
    this.hidden = options.hidden == null ? false : options.hidden;
    
    //this.viewsAreDerived = options.vad; moved to product.hasDerivedViews
    this.noCategores = options.ddp;
    
    if(this.noCategores == true) { //no categories.....
      var notCat = new Category({id:-1,n:"No Category"}, this);
      this.cats.push(notCat);
      this.catsById[-1] = notCat;
      this.catsLoaded = true;
    }
    
    this.pricingType = options.pt;
    this.flags = options.flags;
    this.hasSizes = options.hs;
    
    
    this.fields = new MapList(this);
    this.disFields = new MapList(this);
    this.fieldsByCode = {};
    this.fieldsByName = {};
    this.colorField = null;
    this.sizeField = null;
    this.customFields = new MapList(this);
    
    this.minQty = options.mq;
    this.bundleSize = options.bs;
    
    this.discountId = options.discid;
    this.discountToBase = options.datb;
    
    
    
    var fl = options.f;
    for(var i=0;i<fl.length;i++) {
      var f = new ProductTypeField(fl[i].id, fl[i], this);
      if(f.fieldType != FIELD_TYPE_PRODUCT_SIZE || this.hasSizes) {
        this.fields.add(f);
        if(f.code != null && f.code != '') {
          this.fieldsByCode[f.code] = f;
        }
        if(f.name != null && f.name != '') {
          this.fieldsByName[f.name] = f;
        }
        
        if(f.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
          this.sizeField = f;
        } else if(f.fieldType == FIELD_TYPE_PRODUCT_COLOR) {
          this.colorField = f;
        } else {
          this.customFields.add(f);
        }
      }
    }
    this.currentCategory = null;
    this.currentSubCategory = null;
    this.boundElements = false;
    this.taxExempt = options.te;
    
    this.processes = new MapList(this);
  },
  
  addCategories: function PT_addCategories(cats) {
    this.cats = [];
    this.catsById = new Hash();
    if (cats) {
      for(var i=0;i<cats.length;i++) {
        var cat = new Category(cats[i], this);
        this.cats.push(cat);
        this.catsById[cat.id] = cat;
      }
      this.catsLoaded = true;
    }
  },
  
  addProcess: function PT_addProcess(options) {
    return this.processes.add(new ProductTypeProcess(this,options));
  },
  
  addProcessMix: function PT_addProcessMix(from, to) {
    var proc = this.processes.byId[from];
    if(proc != null) {
      proc.addMix(to);
    }
  },
  
  loadCategory:function(catId, products) {
    var cat = this.catsById[catId];
    if(cat!=null) {
      cat.load(products);
    }
    return cat;
  },
  
  getColorPanel: function PT_getColorPanel() {
    if(this.colorPanel!=null) {
      return this.colorPanel;
    }
    if(this.noColorPanel) {
      return null;
    }
    this.colorPanel = $("pt_col_" + this.id);
	
    if(this.colorPanel==null) {
      this.noColorPanel = true;
    } else {
      this.colorPanel.className="color_panel";
    }
    return this.colorPanel;
  },
  
  buildColorPanelForOMS: function PT_buildColorPanelForOMS(cProduct, cp) {
    var html = '';
    
    if(cp != null) {
      var unavailableColors = [];
      var colors = cProduct.getAvailableColorList(unavailableColors);
      
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        var colorName="";
        for(var k=0; k < color.colors.length; k++) {
          colorName+= color.colors[k][1] + ' ';
        }
        var cellHtml = '<li><span id="oms_color_' + color.id + '" class="colorbox" onmouseover="orderManager.popupManager.showRollover(this, \'<p>' + colorName + '</p>\');" onmouseout="orderManager.popupManager.hideRollover(this);">';
        var h = parseInt(18 / color.colors.length);
        for(var j=0; j < color.colors.length;j++) {
          cellHtml += '<span style="background-color: ' + color.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
        }
        cellHtml += '<b class="overlay">&nbsp;</b></span></li>';
        html += cellHtml;
      }
      
      for(var i = 0; i < unavailableColors.length;i++) {
        var color = unavailableColors[i]["color"];
        var comment="";
        for(var k=0; k < color.colors.length; k++) {
          comment+= color.colors[k][1] + ' ';
        }
        comment += "is not available in ";
        var sizes = unavailableColors[i]["conflict_sizes"];
        for(var k=0; k < sizes.length; k++) {
          comment+= sizes[k];
          if (k != sizes.length - 1) comment += ", ";
          else comment += ".";
        }
        var cellHtml = '<li><span id="oms_color_' + color.id + '" class="colorbox disabled" onmouseover="orderManager.popupManager.showRollover(this, \'<p>' + comment + '</p>\');" onmouseout="orderManager.popupManager.hideRollover(this);">';
        var h = parseInt(18 / color.colors.length);
        for(var j=0; j < color.colors.length;j++) {
          cellHtml += '<span style="background-color: ' + color.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
        }
        cellHtml += '<b class="overlay">&nbsp;</b></span></li>';
        html += cellHtml;
      }
      
      Element.update(cp, html);
      
      //choose selected colorcolor_cell
      var f = this.colorField;
      if(f != null) {
        var selectedColorId = cProduct.getSelectedColorId();
        var cpc = $("oms_color_" + selectedColorId);
        if(cpc==null) {
          log("Unable to get selected color cell for color " + selectedColorId);
        } else {
          cpc.className += " selected";
          this.selectedColorCellForOMS = cpc;
        }
      }
      //bind the color cells
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        var cpc = $("oms_color_" + color.id);
        this.bindColorCell(cpc, color);
      }
    }
  },
  
  buildColorPanel: function PT_buildColorPanel(cProduct) {
    var cp = this.getColorPanel();
    
    var html = '';
    
    if(cp != null) {
      var count = 0;
      var colors = cProduct.getAvailableColorList();
      
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        //log(color);
        var int_title="";
        for(var k=0; k < color.colors.length; k++) {
          int_title+= color.colors[k][1] + ' ';
        }
        var cellHtml = '<li><a href="#" class="color_panel_cell" title="'+int_title+'" id="cpc_' + color.id + '" onmousemove="d.colorMouseMove(this, ' + color.id + ');" onmouseout="d.colorMouseOut(this, ' + color.id + ');">';
        var h = parseInt(20 / color.colors.length, 10);
        for(var j=0; j < color.colors.length;j++) {
          cellHtml += '<span style="background-color: ' + color.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
        }
        cellHtml += "<b>&nbsp;</b></a></li>";
        html += cellHtml;
        count += 1;
      }
     // html += "<!--</ul>-->";
      
      log("setting color panel");
      //log(html);
      Element.update(cp, html);
      log("setting color panel- DONE");
      
      //choose selected colorcolor_cell
      var f = this.colorField;
      if(f != null) {
        var c = null;
        var selectedColorId = cProduct.getSelectedColorId();
        var cpc = $("cpc_" + selectedColorId);
        if(cpc==null) {
          log("Unable to get selected color cell for color " + selectedColorId);
        } else {
          cpc.className += " selected";
          this.selectedColorCell = cpc;
        }
      }
      //bind the color cells
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        var cpc = $("cpc_" + color.id);
        this.bindColorCell(cpc, color);
      }
    }
  },
  
  bindColorCell: function PT_bindColorCell(el, color) {
    var self = this;
    el.onclick = function() {
      if(self.selectedColorCell!=null) {
        self.selectedColorCell.className = self.selectedColorCell.className.replace(" selected", "");
      }
      d.track("change-product-color");
      self.selectedColorCell = el;
      self.selectedColorCell.className = self.selectedColorCell.className+ " selected";
      d.currentCProduct.selectColor(color);
      //self.updateProductViews(true, true);
      d.itemChanged();
      return false;
    };
  },
  
 
  
  //select this product type
  //cProduct: the current configured product..  if different type than this then set product to first product of this type
  //doUpdate: update the designer view
  //doSelect: select the first item 
  selectConfiguredProduct: function PT_selectConfiguredProduct(cProduct, doUpdate, doSelect) {
    log("Selecting Configured Product", true);
    d.deSelectCurrentItem();
    var rebuildImages = false;
    if(cProduct.product.type != this) {
      //swapped product types.... we dont want to select 'product', we just pass it to flag product type change
      log("swapping products type..");
      var cat = this.getFirstLoadedCat();
      /*if(cat == null) {
        log("product type does not have a loaded cat... getting now");
        var self = this;
        this.cats[0].getProducts(function() {
          self.selectConfiguredProduct(cProduct, doUpdate, doSelect);
          } , "m_apparel_pane");
        return;
      }*/
      var new_prod = d.getProduct(cat.products[0].id, null, null);
      if(new_prod == null) {
        log("product " + cat.products[0].id + " not cached.. getting now..");
        var self = this;
        d.getProduct(cat.products[0].id, function() {
          self.selectConfiguredProduct(cProduct, doUpdate, doSelect);
        } , "m_apparel_pane");
        return;
      }
      var moveResult = cProduct.productChangeWillMatch(new_prod);
      if(moveResult != 0) {
        var warning = moveResult==2 ? ml("Some of the artwork you have done cannot be used on the product you have selected. Are you sure you want to change the product?") : ml("None of the artwork you have done can be used on the product you have selected. Are you sure you want to change the product?") ;
        
        if(!confirm(warning)) {
          this.showProductAsSelected( cProduct.product);
          return;
        }
      }
      d.deSelectCurrentItem();
      cProduct.setProduct(new_prod);
      rebuildImages = true;
      cProduct.price = null;
      
    }
    
    var productCaption = $("left_title");
    if(productCaption!= null) {
      if (cProduct.usingCustomProduct()) {
        productCaption.innerHTML = cProduct.customProduct.name;
      } else {
        productCaption.innerHTML = cProduct.product.name;
      }
    }
    
    // update breadcrumb
    /*
    if (d.mode == DESIGNER_MODE_SHOP) {
      if (cProduct.usingCustomProduct()) {
        d.updateBreadCrumb(null, cProduct.customProduct.id);
      } else {
        d.updateBreadCrumb(cProduct.product.id);
      }
    }
    */
    
    /*
    DNC-4007 - we now want this back all the time...
    var previewEl = $("show_preview");
    if(previewEl != null) {
      if(cProduct.product.hasDerivedViews) {
        previewEl.show();
      } else {
        previewEl.hide();
      }
    }
    */
    
    cProduct.updateViewListHtml();
    this.buildColorPanel(cProduct);
    log("this.buildColorPanel");
    this.setFields(cProduct);
    log("this.setFields");
    //select the category etc...
    var cat = this.catsById[cProduct.product.categoryId];
    if((cat != this.currentCategory)&&(cat!=null)&&(this.currentCategory!=null)&&(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      this.markCategorySelected(0); //move to 'all'
      this.selectCategory(0, cProduct.product.id); //render 'all' listing
      this.selectProductInListing(cProduct.product.id); //visually mark as selected
      if(doUpdate) {
        d.selectCurrentView(cProduct.getFirstSelectedViewId(), null);
        if(doSelect) {
          if(!d.currentCViewArea.findAndSelectItem()) {
            d.selectTab('m','customize');
          }
        }
        this.showEditing();
      }
      this.doShow();
    } else if(doUpdate) {
      this.selectProductInListing(cProduct.product.id); //visually mark as selected
      d.selectCurrentView(cProduct.getFirstSelectedViewId(), null);
      if(doSelect) {
        if(!d.currentCViewArea.findAndSelectItem()) {
          d.selectTab('m','customize');
        }
      }
      this.showEditing();
      this.doShow();
    } else {
      this.doShow();
    }
    if(cProduct.product.usesMinQty()) {
      $("c_bun_info").show();
      $("c_min_qty").innerHTML=cProduct.product.minQty;
      $("c_bun").innerHTML=cProduct.product.bundleSize;
      $("qty_container").className = "qty_dropdown";
    } else {
      $("c_bun_info").hide();
      $("qty_container").className = "";
    }
    cProduct.qty = cProduct.checkQty(cProduct.qty, true, true);
    $("qty").value = cProduct.qty; //the qty can change when multiple sizes option is selected and some sized are not available in new product
    
    this.updatePrice();
    
    
    
  },
  
  setFields: function PTF_setFields(cProduct) {
    for(var i=0;i< this.fields.list.length;i++) {
      if(this.fields.list[i].fieldType != FIELD_TYPE_PRODUCT_COLOR) {
        this.fields.list[i].setField(cProduct);
        //this.setField(this.fields.list[i], cProduct);
      }
    }
  },
  
  setFieldsForOMS: function PTF_setFieldsForOMS(cProduct, optionsContainer, popupId) {
    
    html = '';
        
    if (this.customFields.list.length > 0) {
      html += '<div class="custom_fields">';
      for(var i=0;i< this.customFields.list.length;i++) {
        var field = this.customFields.list[i];
        html +=
        '<div class="custom_field" id="oms_pt_fc_' + field.id + '_' + popupId + '" style="display:none;">' +
          '<label>' +
            '<span class="help" id="oms_cf_lbl_' + field.id + '_' + popupId + '">' + field.name + '</span>' +
            '<b class="tip" id="oms_cf_tip_' + field.id + '_' + popupId + '">' +
              '<b>' + field.description + '</b>' +
            '</b>' +
          '</label>' +
          '<div id="oms_pt_foc_' + field.id + '_' + popupId + '" class="custom_option"></div>' +
        '</div>';
      }
      html += '</div>';
      optionsContainer.update(html);
      
      for(var i=0;i< this.customFields.list.length;i++) {
        this.customFields.list[i].setFieldForOMS(cProduct, popupId);
      }
    }
  },
  
  getFirstLoadedCat: function PT_getFirstLoadedCat() {
    for(var i=0; i < this.cats.length; i++) {
      if(this.cats[i].products != null) {
        return this.cats[i];
      }
    }
    return null;
  },
  
  //the user has selected a different product from the product list
  changeSelectedProduct: function PT_changeSelectedProduct(productId) {
    d.deSelectCurrentItem();
    var p = d.currentCProduct;
    var self = this;
    d.track("change-selected-product");
    d.getProduct(productId, function(product) {
        
      
      log("changeSelectedProduct callback");
      var moveResult = p.productChangeWillMatch(product);
      if(moveResult != 0) {
        var warning = moveResult==2 ? ml("Some of the artwork you have done cannot be used on the product you have selected. Are you sure you want to change the product?") : ml("None of the artwork you have done can be used on the product you have selected. Are you sure you want to change the product?") ;
        
        if(!confirm(warning)) {
          //cat.selectProduct(cProduct.product.id);
          self.showProductAsSelected( p.product);
          return;
        }
      }
      d.deSelectCurrentItem();
      self.showProductAsSelected(product);
      p.setProduct(product);
      log("p.setProduct(product);");
      self.selectConfiguredProduct(p,false, false);
      p.afterProductChanged();
      log("self.selectConfiguredProduct(p,false, false);");
      self.updateProductDetails(true);
      
      d.selectCurrentView(p.getFirstSelectedViewId(), null);
      //self.updateProductViews(true);
      log("changeSelectedProduct callback DONE");
    }, $("pt_info_" + this.id));
  },
  
  //show a product as selected on product selector.. assumes the current product type/cat is the same...if not do nothing
  showProductAsSelected: function PT_showProductAsSelected(product) {
    if(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) return; //viewing custom product...
    if(this.id != product.type.id) {
      return;
    }
    this.selectProductInListing(product.id);
  },
  
  updateProductDetails: function PT_updateProductDetails() {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    var p = d.currentCProduct.product;
    var img = $("pt_i_" + this.id);
    if(img != null) setImageUrl(img, img, p.getDisplayImageURL(), null);
    //$("pt_i_" + this.id).src = p.getViewURL(p.product.defaultView.id, 2);
    var el = $("pt_c_" + this.id);
    if(el != null) el.innerHTML = p.name;
    var el = $("pt_d_" + this.id);
    if(el != null) el.innerHTML = p.description;
  },
  
  
  deselect: function PT_deselect() {
    if (this.container != null) this.container.hide();
    //this.views.style.display = "none"; 
    if (this.dataContainer != null) this.dataContainer.hide();
    if (this.fieldsContainer != null) this.fieldsContainer.hide();
    if (this.catSelectorContainer != null) this.catSelectorContainer.hide();
    var cp = this.getColorPanel();
    if(cp != null) cp.hide();
  },
  
  //show the product type information, calling the server if we dont have it locally yet...
  select: function PT_select(callback, selectedCategoryId, productId) {
    if (!this.typeSelector) this.typeSelector = $('sel_prod_type');
    if (this.typeSelector) this.typeSelector.value = this.id;
    
    if(this.boundElements) {
      this.container.show();
      //this.views.style.display = ""; 
      this.dataContainer.show(); 
      if((d.mode == DESIGNER_MODE_CONFIGURE)&&(!d.configuringNewProduct)) {
        $("m_apparel").hide();
      } else {
        if(d.mode != DESIGNER_MODE_CONFIGURE) {
          this.fieldsContainer.show();
        } else {
          this.fieldsContainer.hide();
        }
        if (this.catSelectorContainer != null) this.catSelectorContainer.show();
        if(this.noCategores) {
          $("product_category_container").hide();
        } else {
          $("product_category_container").show();
        }
      }
      var cp = this.getColorPanel();
      if(cp != null) cp.show();
      callback();
      return true;
    } else {
      var self = this;
      var aKey = asyncStart("designer_container");
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/get_product_type_configuration_panel?id=" + this.id + "&pid=" + productId + "&cats_loaded=" + this.catsLoaded), {asynchronous:true, evalScripts:true, 
        onComplete: function PT_onComplete() { 
          asyncFinish(aKey); 
          self.bindElements();
          self.selectCategory(0);
          callback();
        }
      });
    }
  },
  
  bindElements: function PT_bindElements() {
    this.boundElements = true;
    var self = this;
    this.container = $("pt_" + this.id);
    //this.views = $("product_views_" + this.id);
    this.dataContainer = $("product_type_data_" + this.id);
    this.fieldsContainer = $("product_type_cf_" + this.id);
    this.catSelectorContainer = $("category_selector_" + this.id);
    if(d.mode != DESIGNER_MODE_CONFIGURE) {
      this.fieldsContainer.show();
    } else {
      this.fieldsContainer.hide();
    }
   
    var ddel = $("ddpc_" + this.id);
    if(ddel != null) {
      this.bindCatSelector($("ddpc_" + this.id));
    }
    if((d.mode == DESIGNER_MODE_CONFIGURE)&&(!d.configuringNewProduct)) {
      $("m_apparel").hide();
    } else {
      if (this.catSelectorContainer != null) this.catSelectorContainer.show();
      if(this.noCategores) {
        $("product_category_container").hide();
      } else {
        $("product_category_container").show();
      }
    }
  },

  bindCatSelector: function PT_bindCatSelector(el) {
    this.catSelectorEl = el;
    var self = this;
    log("bindCatSelector");
    log(el);
    el.onchange = function() {
      log("Cat Changed:" + el.value);
      var catId = el.value;
      self.selectCategory(parseInt(catId, 10));
      self.selectProductInListing(d.currentCProduct.product.id);
    };
  },
  
  selectCategory: function(categoryId) {
    if(this.noCategores != true) {
      if(categoryId == 0) {
        var html = '';
        for(var i=0; i < this.cats.length; i++) {
          var cat = this.cats[i];
          if(cat.products.length > 0) {
            var cHtml = '<h3>' + cat.name + '</h3><ul>';
            cHtml += cat.generateHtml();
            cHtml += '</ul>';
            html += cHtml;
          }
        }
        html += '';
        Element.update("product_list_" + this.id, html);
        for(var i=0; i < this.cats.length; i++) {
          var cat = this.cats[i];
          cat.bindElements();
        }
      } else {
        var cat = this.catsById[categoryId];
        cat.displayProducts();
      }
    } else {
      var cat = this.catsById[-1];
      cat.displayProducts();
    }
  },
  
  //called when loading a product to make sure it at least appears in product listing when loading pre-dec of product type that is turned off in designer
  checkProductAvailable: function(productId, options) {
    if(this.catsLoaded) {
      var firstCat = null;
      for(var i=0; i < this.cats.length; i++) {
        if(firstCat == null) firstCat = this.cats[i];
        if(this.cats[i].findProduct(productId) != null) {
          return; //already available.. 
          log("Product " + productId + " is already available");
        }
      }
      if(firstCat != null) {
        log("late adding product " + productId);
         options.id = productId;
         firstCat.products.push(options);
      }
    }
  },
  
  markCategorySelected: function PT_markCategorySelected(categoryId) {
    if(this.catSelectorEl != null) {
      this.catSelectorEl.value = categoryId;
    } 
  },
  
  selectProductInListing: function(productId) {
    if(this.currentCategory!=null) {
      this.currentCategory.selectProduct(productId);
    } else { //all selected..
      for(var i=0; i < this.cats.length; i++) {
        this.cats[i].selectProduct(productId);
      }                                     
    }
  },
  
  //depricated
  scrollCat: function PT_scrollCat(amount) {
    if(this.currentCategory!=null) {
      this.currentCategory.scrollCat(amount);
    }
  },
  
  updatePrice: function PT_updatePrice() {
    var up = d.currentCProduct.getUnitPrice();
    var price = d.currentCProduct.getPrice();
    var discount = d.currentCProduct.calculatedDiscountAmount;
    var rrp = d.currentCProduct.getRRP();
    var qty = d.currentCProduct.qty;
    log('up=' + up + ', price=' + price + ', discount=' + discount + ', qty=' + qty + ", rrp=" + rrp);
    //$("qty").value = qty; (this was stopping user from backspacing the qty before changing it)
    $('price').innerHTML = d.formatPrice(price, "price_currency_code", true, rrp);
    var disc = $("discount_container");
    if(disc != null) {
      if(discount > 0) {
        disc.style.display="";
         $("discount").innerHTML = d.formatPrice(discount,"discount_currency_code");
      } else {
        disc.style.display="none";
      }
    }
    if(d.pricesToUpdate){
      this.updatePrintingProcessPrices();
    }
  },
  
  updatePrintingProcessPrices: function PT_updatePrintingProcessPrices(){
    var originalPP = d.currentCProduct.defaultProcessId ;
    var originalUserPP = d.userSelectedProcessId;
    d.userSelectedProcessId = null;
    var pps = d.pricesToUpdate,
      length = pps.length,
      i = length ;
    while (--i >= 0) {
      var pp = pps[i],
        el = $(pp.ele) ;
      d.currentCProduct.defaultProcessId = pp.id ;
      d.currentCProduct.getUnitPrice() ;
      el.innerHTML = d.formatPrice(d.currentCProduct.getPrice(), "price_currency_code", true, d.currentCProduct.getRRP()) ;
    }
    d.currentCProduct.defaultProcessId = originalPP ;
    d.userSelectedProcessId = originalUserPP;
  },
  
  showEditing: function PT_showEditing(fromSave) {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    if(fromSave) {
      $("add_cart_container").style.display = "none";
      $("update_cart_container").style.display = "none";
    } else {
      log("showEditing", true);
      if(d.currentCProduct.addedToCart) {
        $("add_cart_container").style.display = "none";
        $("update_cart_container").style.display = "";
      } else {
        $("add_cart_container").style.display = "";
        $("update_cart_container").style.display = "none";
      }
      $("qty").value = d.currentCProduct.qty;
      $("qty").disabled =(d.currentCProduct.multiOptionField() != null || d.defaultQtyDisabled);
    }
  },
  
  itemChanged: function PT_itemChanged() {
    $("qty").disabled = (d.currentCProduct.multiOptionField() != null || d.defaultQtyDisabled);
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    //log("item changed!", true);
    if(d.currentCProduct.addedToCart) {
      $("update_cart_container").style.display = "";
      //$("cancel_button").style.display = "";
    } else {
      $("add_cart_container").style.display = "";
      
    }
  },
  
  itemSaved: function PT_itemSaved() {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    $("update_cart_container").style.display = "none";
    //$("cancel_button").style.display = "none";
    
  },
   //called to make sure any state of the current product is displayed in the designer
  doShow: function PT_doShow() {
    $("qty").disabled = (d.currentCProduct.multiOptionField() != null || d.defaultQtyDisabled);
  },
  
  canDecorate: function PT_canDecorate(process) {
    return (this.processes.byId[process.id] != null);
  },
  
  //get the disabled message.. for product type it would be 'You cannot add %1s to a %2s'
  getDecProcDisabledMessage: function PT_getDecProcDisabledMessage(process) {
    return "You cannot add " + process.name + " to a " + this.name;
  }
});

var ProductTypeField = Class.create({
  CLASSDEF: {
      name: 'ProductTypeField'
  },
  
  initialize: function PTF_initialize(id, options, productType) {
    this.id = id;
    this.name = options.n;
    this.code = options.c;
    this.fieldType = options.ft;
    this.productType = productType;
    
    this.discontinued = options.dis == null ? false : options.dis;
    
    
    this.description = options.d;
		this.pos = options.p;
		
    this.typeOptions = FIELD_TYPES_OPTIONS[this.fieldType];
		this.pricingType = options.pt;
		this.priceModifierType = options.pm;
    this.priceModifier = options.pta;
    
    this.percentOf = null;
    if(options.pco != null) {
      this.percentOf = options.pco;
    }
    this.defaultOn = options.don;

    this.isRequired = options.r;
    this.hasSubOptions = options.sub;
    this.customOptions = (options.co == null) ? {} : options.co;
    
    this.hasMulti = options.multi;

        
    if(this.hasMulti) {
      this.multiOptions = {};
      this.multiRow = $("multi_" + this.id);
    }
    
    this.options = new MapList();
    var lo = options.o;
    for(var i=0;i<lo.length;i++) {
      var o = new ProductTypeOption(lo[i].id, lo[i], this);
      this.options.add(o);
  
      this.productType.optionsById[o.id] = o;
      
      if((this.hasMulti) && (!o.isMulti)) {
        var optQty = $("mq_" + o.id);
        var optTd = $("m_" + o.id);
        this.multiOptions[o.id] = {td: optTd, q: optQty};
      } else if(o.isMulti) {
        this.multiOption = o;
      }
      
    }
  },
  
  //because the mutirow may not exist when the init contsructor is called be need late binding
  
  toggleMultiContainer: function PTF_toggleMultiContainer(show) {
    var el = $("multi_container_" + this.id);
    if(el != null) {
      if(show) {
        el.show();
      } else {
        el.hide();
      }
    }
    if(d.currentLayoutManager != null) {
      d.currentLayoutManager.recalibrate();
    }
  },
  
  //show the tr with the multiple qty options
  showMultiQtyOptions: function PTF_showMultiQtyOptions(cProduct) {
    this.toggleMultiContainer(true);
  },
  
  hideMultiQtyOptions: function PTF_hideMultiQtyOptions(cProduct) {
    this.toggleMultiContainer(false);
  },
  
  //called when loading options popup in OMS: render/insert the html for this field....
  setFieldForOMS: function PTF_setFieldForOMS(cProduct, popupId) {
    omsFieldContainer = $("oms_pt_fc_" + this.id + '_' + popupId);
    omsFieldOptionsContainer = $("oms_pt_foc_" + this.id + '_' + popupId);
    
    var lblEl = $("oms_cf_lbl_" + this.id + '_' + popupId);
    var toolEl = $("oms_cf_tip_" + this.id + '_' + popupId);
    if((lblEl!=null)&&(toolEl!=null)) {
      new Tooltip(lblEl, toolEl);
    }
    
    var productField = cProduct.product.fields.byId[this.id];
    if((productField == null)||(!productField.valid)||(!productField.used)) { //this product does not use the field...
      if (omsFieldContainer != null) omsFieldContainer.hide(); 
      log("Hiding field " + this.name + ": productField follows:");
      log(productField);
    } else {
      var cField = cProduct.cFields[this.id];
      if(cField != null) {
        if (omsFieldOptionsContainer != null) omsFieldOptionsContainer.update(this.renderHtml(cProduct, productField, cField, true));
        if(d.mode != DESIGNER_MODE_CONFIGURE) {
          if (omsFieldContainer != null) omsFieldContainer.show();
        } else {
          if (omsFieldContainer != null) omsFieldContainer.hide();
        }
      }
    }
  },
  
  //called when changing/loading configured product: render/insert the html for this field....
  setField: function PTF_setField(cProduct) {
    if(this.fieldContainer == null) {
      this.fieldContainer = $("pt_fc_" + this.id);
      this.fieldOptionsContainer = $("pt_foc_" + this.id);
      if(this.fieldType != FIELD_TYPE_PRODUCT_SIZE) {
        var lblEl = $("cf_lbl_" + this.id);
        var toolEl = $("cf_tip_" + this.id);
        if((lblEl!=null)&&(toolEl!=null)) {
          new Tooltip(lblEl, toolEl);
        }
      }
    }
    var productField = cProduct.product.fields.byId[this.id];
    if((productField == null)||(!productField.valid)||(!productField.used)) { //this product does not use the field...
      if (this.fieldContainer != null) this.fieldContainer.hide(); 
      log("Hiding field " + this.name + ": productField follows:");
      log(productField);
    } else {
      var cField = cProduct.cFields[this.id];
      if(cField != null) {
        if (this.fieldOptionsContainer != null) this.fieldOptionsContainer.update(this.renderHtml(cProduct, productField, cField));
        if(d.mode != DESIGNER_MODE_CONFIGURE) {
          if (this.fieldContainer != null) this.fieldContainer.show();
        } else {
          if (this.fieldContainer != null) this.fieldContainer.hide();
        }
      }
    }
  },
  
  customOption: function PTF_customOption(name, defaultValue) {
    var opt = this.customOptions[name];
    if(opt==null) opt = defaultValue;
    return opt;
  },
  
  renderHtml: function PTF_renderHtml(cProduct, pField, cField, forOMS) {
    var setValueParams = (forOMS) ? ', true' : '';
    if(this.typeOptions.list) {
      return this.renderListHtml(cProduct, pField, cField, forOMS);
    } else if(this.fieldType == FIELD_TYPE_TEXT_BOX) {
      var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE && cField.getValue(null) == null) ? ' disabled="true"' : '';
      return '<input type="text" size="' + this.customOption("length", 30) + '" value="' + cField.getValue('') + '"' + cField.iId('value', true) + ' onblur="' + cField.objectRef(forOMS) + '.setValue(this.value, null' + setValueParams + ');"' + disCode + '/>';
    } else if(this.fieldType == FIELD_TYPE_TEXT_AREA) {
      var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE && cField.getValue(null) == null) ? ' disabled="true"' : '';
      return '<textarea ' + disCode + ' rows="' + this.customOption("rows", 5) + '" cols="' + this.customOption("cols", 30) + '"' + cField.iId('value', true) + ' onblur="' + cField.objectRef(forOMS) + '.setValue(this.value, null' + setValueParams + ');">' + cField.getValue('') + '</textarea>';
    } else if(this.fieldType == FIELD_TYPE_FILE) {
      var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE && cField.getValue(null) == null) ? ' disabled="true"' : '';
      var existing = "";
      if(cField.getValue(null) != null) {
        var fileData = cField.getValue(null);
        existing = ' <a href="' + fileData.url + '" target="_BLANK">' + fileData.name + '</a> (' + fileData.file_size + ') <a href="#" onclick="' + cField.objectRef(forOMS) + '.removeFile(); return false;" class="file_remove">' + ml("Remove") + '</a>';
      }
      return '<form id="field_upload_form' + this.id + '" method="post" enctype="multipart/form-data" action="/designer/upload_file?field_id=' + this.id + '&c_product_id=' + cProduct.id + '&s_product_id=' + cProduct.product.id + '" target="UploadTarget">' +
        '<input size="5" name="field_file" type="file" ' + cField.iId('value', true) + ' onchange="' + cField.objectRef(forOMS) + '.setValue(this.value, null' + setValueParams + ');" value="' + cField.getValue('') + '"' + disCode + '/>' + existing +
       '</form>';
    } else if(this.fieldType == FIELD_TYPE_IMAGE) {
      var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE && cField.getValue(null) == null) ? ' disabled="true"' : '';
      var existing = "";
      if(cField.getValue(null) != null) {
        var fileData = cField.getValue(null);
        existing = ' <img src="' + fileData.url_thumb + '" >' + ' (' + fileData.width + "x" + fileData.height + " " + fileData.file_size + ') <a href="#" onclick="' + cField.objectRef(forOMS) + '.removeFile(); return false;" class="file_remove">' + ml("Remove") + '</a>';
      }
      return '<form id="field_upload_form' + this.id + '" method="post" enctype="multipart/form-data" action="/designer/upload_file?field_id=' + this.id + '&c_product_id=' + cProduct.id + '" target="UploadTarget">' +
        '<input size="5" name="field_file" type="file" ' + cField.iId('value', true) + ' onchange="' + cField.objectRef(forOMS) + '.setValue(this.value, null' + setValueParams + ');" value="' + cField.getValue('') + '"' + disCode + '/>' + existing +
       '</form>';
    } else if((this.fieldType == FIELD_TYPE_DATE)||(this.fieldType == FIELD_TYPE_DATE_TIME)) {
      var disabled = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE && cField.getValue(null) == null);
      if(disabled) {
        return "Cannot Set Date When Ammending Order";
      }
      var format = parseInt(this.customOption("date_format", '0'), 10);
      var type = parseInt(this.customOption("date_input_type", '0'), 10);
      var includeTime = (this.fieldType == FIELD_TYPE_DATE_TIME);
      var timeFormat = includeTime? parseInt(this.customOption("time_format", '0'), 10) : -1;
      var defValue = parseInt(this.customOption("default_value", '0'));
      var curDate = null;
      if (defValue != null && (defValue > 0 || defValue <= 0)) {
        curDate = new Date();
        if (includeTime) {
          curDate.setTime(curDate.getTime() + defValue * 3600000);
        } else {
          curDate.setTime(curDate.getTime() + defValue * 3600000 * 24);
        }
      }
      
      if(type == 0) { //date picker...
        var currentDate = this.formatDate(format, curDate, includeTime, "Select Date");
        var dataDate = (curDate == null) ? "" :  curDate.toString();
        
        return '<a href="#" onclick="' + cField.objectRef(forOMS) + '.selectDate(' + includeTime + '); return false;" class="field_date_picker"><input type="hidden" id="field_date_value_' + this.id + '" value="' + dataDate + '"/><span id="field_date_' + this.id + '">' + currentDate + '</span></a> <a href="#" onclick="' + cField.objectRef(forOMS) + '.selectDate(' + includeTime + '); return false;" class="field_date_picker"><img src="/images/calendar_date_select/calendar.gif" border="0"/></a>'
      } else { //input fields
        var str = this.renderDateInputs(format, curDate, cField.objectRef(forOMS), cField, forOMS);
        if(includeTime) {
          str += this.renderTimeInputs(timeFormat, curDate, cField.objectRef(forOMS), cField,forOMS);
        }
        return str;
      }
    } else if(this.fieldType == FIELD_TYPE_TIME) {
      var disabled = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE && cField.getValue(null) == null);
      if(disabled) {
        return "Cannot Set Date When Ammending Order";
      }
      var timeFormat = parseInt(this.customOption("time_format", '0'), 10);
      var defValue = parseInt(this.customOption("default_value", '0'));
      var curDate = null;
      if (defValue != null && (defValue > 0 || defValue <= 0)) {
        curDate = new Date();
        curDate.setTime(curDate.getTime() + defValue * 3600000);
      }
      return this.renderTimeInputs(timeFormat, curDate, cField.objectRef(forOMS), cField);
    }
  },
  
  renderDateInputs: function PTF_renderDateDropDowns(format, date, objectRef, cField, forOMS) {
    var dateFieldOrder = {
      0: ["m","/", "d", "/", "y"],
      1: ["d","/", "m", "/", "y"],
      2: ["sm"," ", "d", ", ", "y"],
      3: ["lm"," ", "d", ", ", "y"]
    };
    var fields = dateFieldOrder[format];
    var str = "";
    var setValueParams = (forOMS) ? ', null, true' : '';
    
    for(var i=0; i < fields.length; i++) {
      var fieldCode = fields[i];
      switch(fieldCode) {
      case "m": 
        var selVal = (date == null)? -1 : date.getMonth();
        str += '<select ' + cField.iId("date_1", true) + ' onchange="' + objectRef + '.setValue(this.value, 1' + setValueParams + ');">' + selectOptionHtml([["",-1],["01-Jan",0],["02-Feb",1],["03-Mar",2],["04-Apr",3],["05-May",4],["06-Jun",5],["07-Jul",6],["08-Aug",7],["09-Sep",8],["10-Oct",9],["11-Nov",10],["12-Dec",11]], selVal) + '</select>'; 
        break;
      case "d": 
        var selVal = (date == null)? "" : date.getDate();
        str += '<input ' + cField.iId("date_0", true) + ' type="text" size="2" onblur="' + objectRef + '.setValue(this.value, 0' + setValueParams + ');" value="' + selVal + '"/>'; 
        break;
      case "y": 
        var selVal = (date == null)? -1 : date.getFullYear();
        var y = new Date().getFullYear();
        var years = $R(y - 10, y + 10);
        str += '<select ' + cField.iId("date_2", true) + ' onchange="' + objectRef + '.setValue(this.value, 2' + setValueParams + ');">' + selectOptionHtml([""].concat(years.toArray()), selVal) + '</select>'; 
        break;
      case "sm": 
        var selVal = (date == null)? -1 : date.getMonth();
        str += '<select ' + cField.iId("date_1", true) + ' onchange="' + objectRef + '.setValue(this.value, 1' + setValueParams + ');">' + selectOptionHtml([["",-1],["Jan",0],["Feb",1],["Mar",2],["Apr",3],["May",4],["Jun",5],["Jul",6],["Aug",7],["Sep",8],["Oct",9],["Nov",10],["Dec",11]], selVal) + '</select>'; 
        break;
      case "lm": 
        var selVal = (date == null)? -1 : date.getMonth();
        str += '<select ' + cField.iId("date_1", true) + ' onchange="' + objectRef + '.setValue(this.value, 1' + setValueParams + ');">' + selectOptionHtml([["",-1],["January",0],["February",1],["March",2],["April",3],["May",4],["June",5],["July",6],["August",7],["September",8],["October",9],["November",10],["December",11]], selVal) + '</select>'; 
        break;
      default:
        str += fieldCode;
      }
    }
    return str;
  },
  
  renderTimeInputs: function PTF_renderTimeInputs(format, date, objectRef, cField, forOMS) {
    var str = "";
    var selVal = null;
    var setValueParams = (forOMS) ? ', null, true' : '';
    if(format == 0) { //ampm
      selVal = (date == null)? "" : date.getAMPMHour();
      str += '<input ' + cField.iId("date_3", true) + ' type="text" size="2" onblur="' + objectRef + '.setValue(this.value, 3' + setValueParams + ');" value="' + selVal + '"/>';
    } else {
      selVal = (date == null)? "" : date.getHour();
      str += '<input ' + cField.iId("date_6", true) + ' type="text" size="2" onblur="' + objectRef + '.setValue(this.value, 6' + setValueParams + ');" value="' + selVal + '"/>';
    }
    selVal = (date == null)? "" : date.getMinutes();
    str += '<input ' + cField.iId("date_4", true) + ' type="text" size="2" onblur="' + objectRef + '.setValue(this.value, 4' + setValueParams + ');" value="' + selVal + '"/>';
    if(format == 0) { //ampm
      selVal = (date==null)? '' : date.getAMPM();
      str += '<select ' + cField.iId("date_5", true) + ' onchange="' + objectRef + '.setValue(this.value, 5' + setValueParams + ');">' + selectOptionHtml(["AM","PM"], selVal) + '</select>'; 
    }
    return str;
  },
  
  formatDate: function PTF_formatDate(format, date, includeTime, defaultValue) {
    if(date == null) {
      return defaultValue;
    }
    return date.toFormat(format, includeTime);
  },
  
  updateDate: function(cField) {
    var format = parseInt(this.customOption("date_format", '0'), 10);
    if(this.fieldType == FIELD_TYPE_DATE) {
      var currentDate = this.formatDate(format, cField.fieldValue, false, "Select Date");
      $('field_date_' + this.id).update(currentDate);
    } else {
      var currentDate = this.formatDate(format, cField.fieldValue, true, "Select Date");
      $('field_date_' + this.id).update(currentDate);
    }
  },
  
  renderListHtml: function PTF_renderListHtml(cProduct, pField, cField, forOMS) {
    var sizeColorCombinations = null;
    if(this.fieldType == FIELD_TYPE_PRODUCT_SIZE && cProduct.product.limitSizeColors) {
      sizeColorCombinations = cProduct.product.sizeColorCombinations;
    }
    var html = '';
    if((this.fieldType == FIELD_TYPE_PRODUCT_SIZE && !this.productType.ms) || (this.fieldType == FIELD_TYPE_PRODUCT_SIZE && (cProduct.usingTeamnames || cProduct.product.usesMinQty())) || this.fieldType ==FIELD_TYPE_LIST_MULTISELECT_QTY || this.fieldType ==FIELD_TYPE_LIST_MULTISELECT || this.fieldType == FIELD_TYPE_LIST_DROPDOWN) {
      html = this.renderSelect(cProduct, pField, cField, sizeColorCombinations, forOMS);
    }
    if(this.fieldType == FIELD_TYPE_LIST_CHECKBOX || this.fieldType ==FIELD_TYPE_LIST_RADIO) {
      html = this.renderCheckRadio(cProduct, pField, cField, forOMS);
    }
    if(this.fieldType == FIELD_TYPE_PRODUCT_SIZE || this.fieldType ==FIELD_TYPE_LIST_MULTISELECT_QTY) {
      log("renderMultiQty");
      html += this.renderMultiQty(cProduct, pField, cField, sizeColorCombinations);
    }
    
    return html;
  },
  
  checkSizeColorCombination: function PTF_checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, subOptionId) {
    if((sizeColorCombinations != null)&&(selectedColor != null)&&(!ptOpt.isMulti)) {
      var filter = sizeColorCombinations[ptOpt.id];
      if(filter != null) {
        if(filter[subOptionId] == null) {
            subOptionId = 0; //use the size defaults
        }
        if(filter[subOptionId] != null && filter[subOptionId][selectedColor.productChosenOptionId] != true) {
          log("Filtering out " + ptOpt.name + " subOptionId=" + subOptionId);
          return false;
        }
      } else {
        log("product has no available colors for size " + ptOpt.name);
        return false;
      }
    }
    return true;
  },
  
  renderSelect: function PTF_renderSelect(cProduct, pField, cField, sizeColorCombinations, forOMS) {
    
    var setValueParams = (forOMS) ? ', null, true' : '';
    
    if(this.fieldType == FIELD_TYPE_PRODUCT_SIZE && cProduct.usingTeamnames) {
      return '<a href="#" onclick="d.currentCProduct.getTeamNames().editTeamNames(); return false;">Manage Teamnames</a>';
    }
    log("renderSelect");
    var multiMode = false;
    var size = 0;
    var multi = false;
    
    if(this.fieldType == FIELD_TYPE_PRODUCT_SIZE || this.fieldType ==FIELD_TYPE_LIST_MULTISELECT_QTY || this.fieldType ==FIELD_TYPE_LIST_DROPDOWN) { //"multi select" may be chosen
      if(cField.multiSelect) {
        multiMode = true;
      }
    } else {
      size = 5;
      multi = true;
    }
    
    var opts = this.getOptionList(cProduct, pField, cField, sizeColorCombinations, true, true);
    
    var selOpt = cField.getMainOption(true);
      
    var optionHtml = selectOptionHtml(opts, selOpt.optionId);
    
       
       
       
    var extraSelOpts = "";
    if(size > 1) {
      extraSelOpts += ' size="' + size + '"';
    }
    if(multi) {
      extraSelOpts += ' multiple="multiple"';
    }
    var selCode = multi ? 'null' : 'this.value';
    var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE) ? ' disabled="true"' : '';
    return '<select ' + cField.iId('value', true) + ' onchange="' + cField.objectRef(forOMS) + '.setValue(' + selCode + setValueParams + ');" ' +  extraSelOpts + disCode + '>' + optionHtml + '</select>' + 
    '<span ' + cField.iId('sub_value_container', true) + '>' + this.renderSubSelect(cProduct, pField, cField, sizeColorCombinations, forOMS) + '</span>';
  },
  
  renderSubSelect: function PTF_renderSubSelect(cProduct, pField, cField, sizeColorCombinations, forOMS) {
    var selOpt = cField.getMainOption(true);
    var pOpt = pField.options.byId[selOpt.optionId];
    if(pOpt == null) {
      log("Unable to get product def of selected option " + selOpt.optionId + " when rendering sub select");
      return "";
    }
    if(pOpt.subs == null) { //no sub options..
      return "";
    }
    var selectedColor = cProduct.getSelectedColor();
    if(selectedColor == null) log("Unable to filter out sub sizes as selected color is null");
    
    var ptOpt = this.options.byId[selOpt.optionId];
    
    var subOpts = [];
    for(var j=0; j < ptOpt.subs.list.length; j++) {
      var ptSubOpt = ptOpt.subs.list[j];
      var pSubOpt = pOpt.getSubOption(ptSubOpt.id);
      if(pSubOpt != null) {
        if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, ptSubOpt.id)) {
          delete selOpt.subOptions[ptSubOpt.id];//just make sure its not selected..
          continue;
        }
        subOpts.push([ptSubOpt.name, ptOpt.id + '-' + ptSubOpt.id]);
      }
    }
    var optionHtml = selectOptionHtml(subOpts, selOpt.getId());
    var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE) ? ' disabled="true"' : '';
    var setValueParams = (forOMS) ? ', null, true' : '';
    return '<select ' + cField.iId('sub_value', true) + ' onchange="' + cField.objectRef(forOMS) + '.setValue(this.value' + setValueParams + ');" '  + disCode + '>' + optionHtml + '</select>';
  },
  
  getOptionList: function PTF_getOptionList(cProduct, pField, cField, sizeColorCombinations, includeMulti, excludeSubs) {
    if(excludeSubs == null) excludeSubs = false;
    var selectedColor = cProduct.getSelectedColor();
    if(selectedColor == null) log("Unable to filter out sizes as selected color is null");
    var opts = [];
    for(var i=0; i < this.options.list.length; i++) {
      var ptOpt = this.options.list[i];
      var pOpt = pField.options.byId[ptOpt.id];
      if(pOpt != null) {
        
        if(pOpt.subs != null) {
          var cfieldOption = cField.options[ptOpt.id];
          var subOpts = [];
          for(var j=0; j < ptOpt.subs.list.length; j++) {
            var ptSubOpt = ptOpt.subs.list[j];
            var pSubOpt = pOpt.getSubOption(ptSubOpt.id);
            if(pSubOpt != null) {
              if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, ptSubOpt.id)) {
                if(cfieldOption != null) {
                  delete cfieldOption.subOptions[ptSubOpt.id];//just make sure its not selected..
                }
                continue;
              }
              subOpts.push([ptSubOpt.name,ptOpt.id + '-' + ptSubOpt.id]);
            }
          }
          if(subOpts.length > 0) {
            if(excludeSubs) {
              opts.push([ptOpt.name,pOpt.id]);
            } else {
              opts.push({ c:ptOpt.name, v:subOpts});
            }
          }
        } else if(!ptOpt.isMulti || includeMulti) {
          if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, 0)) {
            delete cField.options[ptOpt.id]; //just make sure its not selected..
            continue;
          }
          opts.push([ptOpt.name,pOpt.id]);
        }
      }
    }
    return opts;
  },
  
  renderMultiQtyTextForOMS: function PTF_renderMultiQtyTextForOMS(cp) {
    if (cp.product) {
      var pField = cp.product.fields.byId[this.id];
      var cField = cp.cFields[this.id];
      var sizeColorCombinations = null;
      if (cp.product.limitSizeColors) {
        sizeColorCombinations = cp.product.sizeColorCombinations;
      }
      
      var selectedColor = cp.getSelectedColor();
      if(selectedColor == null) log("Unable to filter out sizes as selected color is null");
      var opts = [];
      
      for(var i=0; i < this.options.list.length; i++) {
        var ptOpt = this.options.list[i];
        if(ptOpt.isMulti) continue;
        var pOpt = pField.options.byId[ptOpt.id];
        if(pOpt != null) {
          if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt)) {
            delete cField.options[ptOpt.id]; //just make sure its not selected..
            continue;
          }
          
          if(pOpt.subs != null) {
            var cOpt = cField.options[pOpt.id];
            
            var subOpts = [];
            var availSubOpts = [];
            for(var j=0; j < ptOpt.subs.list.length; j++) {
              var ptSubOpt = ptOpt.subs.list[j];
              var pSubOpt = pOpt.getSubOption(ptSubOpt.id);
              if(pSubOpt != null) {
                availSubOpts.push(ptSubOpt);
              }
            }
            availSubOpts.each(function(ptSubOpt) {
                var qty = cField.selectedOptionQty(pOpt.id, ptSubOpt.id);
                if (qty > 0) subOpts.push(ptSubOpt.value + ' x ' + qty);
            });
            if(subOpts.length > 0) {
              opts.push(ptOpt.value + '(' + subOpts.join(", ") + ')');
            }
          } else {
            var qty = cField.selectedOptionQty(pOpt.id);
            if (qty > 0) opts.push(ptOpt.value + ' x ' + qty);
          }
        }
      }
      return opts.join(", ");
    }
    return '';
  },
  
  getMultiQtyData: function PTF_getMultiQtyData(cp) {
    if (cp.product) {
      var pField = cp.product.fields.byId[this.id];
      var cField = cp.cFields[this.id];
      var sizeColorCombinations = null;
      if (cp.product.limitSizeColors) {
        sizeColorCombinations = cp.product.sizeColorCombinations;
      }
      
      var selectedColor = cp.getSelectedColor();
      if(selectedColor == null) log("Unable to filter out sizes as selected color is null");
      var opts = [];
      
      for(var i=0; i < this.options.list.length; i++) {
        var ptOpt = this.options.list[i];
        if(ptOpt.isMulti) continue;
        var pOpt = pField.options.byId[ptOpt.id];
        if(pOpt != null) {
          if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt)) {
            delete cField.options[ptOpt.id]; //just make sure its not selected..
            continue;
          }
          
          if(pOpt.subs != null) {
            var cOpt = cField.options[pOpt.id];
            
            var subOpts = [];
            var availSubOpts = [];
            for(var j=0; j < ptOpt.subs.list.length; j++) {
              var ptSubOpt = ptOpt.subs.list[j];
              var pSubOpt = pOpt.getSubOption(ptSubOpt.id);
              if(pSubOpt != null) {
                availSubOpts.push(ptSubOpt);
              }
            }
            availSubOpts.each(function(ptSubOpt) {
                var qty = cField.selectedOptionQty(pOpt.id, ptSubOpt.id);
                var cSubOpt = (cOpt) ? cOpt.subOptions[ptSubOpt.id] : null;
                subOpts.push({
                    'id' : ptSubOpt.id,
                    'name' : ptSubOpt.name,
                    'value' : ptSubOpt.value,
                    'rrps' : ptSubOpt.defaultPrices,
                    'qty' : qty,
                    'unitPrice' : (cSubOpt) ? cSubOpt.unitPrice : null
                });
            });
            
            if(subOpts.length > 0) {
              opts.push({
                'id' : ptOpt.id,
                'name' : ptOpt.name,
                'value' :ptOpt.value,
                'subOpts' : subOpts
              });
            }
          } else {
            var qty = cField.selectedOptionQty(pOpt.id);
            var cOpt = cField.options[pOpt.id];
            
            opts.push({
                'id' : ptOpt.id,
                'name' : ptOpt.name,
                'value' :ptOpt.value,
                'rrps' : ptOpt.defaultPrices,
                'qty' : qty,
                'unitPrice' : (cOpt) ? cOpt.unitPrice : null
            });
          }
        }
      }
      
      this.multiQtyData = opts;
    } else {
      this.multiQtyData = [];
    }
    
    return this.multiQtyData;
  },
  
  renderMultiQty: function PTF_renderMultiQty(cProduct, pField, cField, sizeColorCombinations, idPrefix, renderEvents, forceVisible) {
    log("renderMultiQty");
    if(idPrefix == null) idPrefix = "";
    if(renderEvents==null) renderEvents = true;
    var dis = (cField.multiSelect || forceVisible) ? '' : ' style="display:none;"' ;
    var en = ((d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE)||(this.fieldType == FIELD_TYPE_PRODUCT_SIZE && cProduct.usingTeamnames && renderEvents)) ? ' disabled="true"' : '';
    var selectedColor = cProduct.getSelectedColor();
    if(selectedColor == null) log("Unable to filter out sizes as selected color is null");
    var opts = [];
    for(var i=0; i < this.options.list.length; i++) {
      var ptOpt = this.options.list[i];
      if(ptOpt.isMulti) continue;
      var pOpt = pField.options.byId[ptOpt.id];
      if(pOpt != null) {
        
        if(pOpt.subs != null) {
          var cOpt = cField.options[pOpt.id];
          
          var subOpts = [];
          for(var j=0; j < ptOpt.subs.list.length; j++) {
            var ptSubOpt = ptOpt.subs.list[j];
            var pSubOpt = pOpt.getSubOption(ptSubOpt.id);
            if(pSubOpt != null) {
              if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, ptSubOpt.id)) {
                if(cField.options[ptOpt.id] != null) {
                  delete cField.options[ptOpt.id].subOptions[ptSubOpt.id]; //just make sure its not selected..
                }
                continue; 
              }
             var qty = cField.selectedOptionQty(pOpt.id, ptSubOpt.id);
             subOpts.push('<li id="' + idPrefix + 'ms_' + ptSubOpt.id + '">' +
               '<label>' + ptSubOpt.value + '</label><input id="' + idPrefix + 'mqs_' + ptSubOpt.id + '" style="width: 25px;" type="text" ' + (renderEvents ? 'onkeyup="d.multiQtyChange(this, ' + this.id + ', ' + ptOpt.id + ',' + ptSubOpt.id + ');"' : '') + ' value="' + qty + '"' + en + '/>' +
                '</li>');
            }
          }
          if(subOpts.length > 0) {
            opts.push('<li id="' + idPrefix + 'm_' + ptOpt.id + '" class="block">' +
                  '<label>' + ptOpt.value + '</label><ul>' + subOpts.join("\n") + '</ul>' +
                '</li>');
          }
        } else {
          if(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, 0)) {
            delete cField.options[ptOpt.id]; //just make sure its not selected..
            continue;
          }
          var qty = cField.selectedOptionQty(pOpt.id);
          opts.push('<li id="' + idPrefix + 'm_' + ptOpt.id + '">' +
            '<label>' + ptOpt.value + '</label><input id="' + idPrefix + 'mq_' + ptOpt.id + '" style="width: 25px;" type="text" ' + (renderEvents ? 'onkeyup="d.multiQtyChange(this, ' + this.id + ', ' + ptOpt.id + ');"' : '') + ' value="' + qty + '"' + en + '/>' +
                '</li>');
        }
      }
      
    }
    
    return '<div id="' + idPrefix + 'multi_container_' + this.id + '" ' + dis + ' class="multi_qty_tr">' +
            '<ul>' + opts.join("\n") + '</ul>' +
          '</div>';
  },
  
  renderCheckRadio: function PTF_renderCheckRadio(cProduct, pField, cField, forOMS) {
    log("renderCheckRadio");
    var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE) ? ' disabled="true"' : '';
    var oRef = cField.objectRef(forOMS);
    var opts = [];
    for(var i=0; i < this.options.list.length; i++) {
      var ptOpt = this.options.list[i];
      if(!ptOpt.isMulti) {
        var pOpt = pField.options.byId[ptOpt.id];
        if(pOpt != null) {
          
          var subHtml = '';
          if(pOpt.subs != null) {
            var pSel = cField.selectedOptionQty(pOpt.id) > 0;
            var subDis = pSel ? '' : ' style="display:none;"';
        
            var subOpts = [];
            for(var j=0; j < ptOpt.subs.list.length; j++) {
              var ptSubOpt = ptOpt.subs.list[j];
              var pSubOpt = pOpt.getSubOption(ptSubOpt.id);
              if(pSubOpt != null) {
                var subSel = cField.selectedOptionQty(pOpt.id, ptSubOpt.id) > 0;
                var selHtml = subSel ? ' checked="true"' : '';
                var elHtml = null;
                if(this.fieldType == FIELD_TYPE_LIST_CHECKBOX) {
                  elHtml='<input type="checkbox" ' + selHtml + ' id="so_' + ptSubOpt.id + '" onclick="' + oRef +'.selectValue(this.checked, ' + ptOpt.id + ',' + ptSubOpt.id + ');"/>';
                } else {
                  elHtml='<input type="radio" ' + selHtml + ' name="sub_' + ptOpt.id + '" id="so_' + ptSubOpt.id + '" onclick="' + oRef +'.selectValue(this.checked, ' + ptOpt.id + ',' + ptSubOpt.id + ');"/>';
                }
                subOpts.push('<li>' + elHtml + '<label>' + ptSubOpt.name + '</label></li>');
              }
            }
            if(subOpts.length > 0) {
              subHtml = '<ul id="subs_' + ptOpt.id + '"' + subDis + '>' + subOpts.join("\n") + '</ul>';
            }
          }
          var sel = cField.selectedOptionQty(pOpt.id) > 0;
          var selHtml = sel ? ' checked="true"' : '';
          var elHtml = null;
          if(this.fieldType == FIELD_TYPE_LIST_CHECKBOX) {
            elHtml='<input type="checkbox" ' + selHtml + ' id="o_' + ptOpt.id + '" onclick="' + oRef +'.selectValue(this.checked, ' + ptOpt.id  + ');"/>';
          } else {
            elHtml='<input type="radio" ' + selHtml + ' name="fo_' + this.id + '" id="o_' + ptOpt.id + '" onclick="' + oRef +'.selectValue(this.checked, ' + ptOpt.id + ');"/>';
          }
              
          var qty = cField.selectedOptionQty(pOpt.id);
          opts.push('<li id="m_' + ptOpt.id + '">' + elHtml + '<label>' + ptOpt.name + '</label>' + subHtml + '</li>');
        }
      }
    }
    return '<ul>' + opts.join("\n") + '</ul>';
    
    
  },
  
  //when the product does not have a default on field we need to init the options from what is default on...
  getProductDefaultOptions: function PTF_getProductDefaultOptions() {
    var opts = [];
    if(this.typeOptions.list) {
      for(var i=0; i < this.options.list.length; i++) {
        var ptOpt = this.options.list[i];
        if(ptOpt.defaultOn) {
          opts.push({id:ptOpt.id, d: ptOpt.defaultPrices, s: ptOpt.defaultSelected, sub: ptOpt.getDefaultSubOptions()});
        }
      }
    }
    return opts;
  }
  
});

var ProductTypeOption = Class.create({
    CLASSDEF: {
        name: 'ProductTypeOption'
    },
  
  initialize: function PTO_initialize(id, options, field) {
    this.id = id;
    this.name = options.n;
    this.value = options.c;
    this.isMulti = options.m;
    this.defaultPrices = options.pta;
    this.field = field;
    this.defaultOn = options.defo;
    this.defaultSelected = options.defs;
    if(options.sub != null) {
      this.subs = new MapList(this);
      for(var i=0; i < options.sub.length; i++) {
        this.subs.add(new ProductTypeSubOption(options.sub[i], this));
      }
    } else {
      this.subs = null;
    }
  },
  
  getDelta: function PTO_getDelta(product, color_type) {
    var fc = product.optionsByPoId[this.id];
    if(fc != null) {
      return fc.delta[color_type];
    }
    return 0;
  },
  
  getFieldChoice: function PTO_getFieldChoice(product) {
    return product.optionsByPoId[this.id];
  },
  
  getSubOption: function PTO_getSubOption(subOptId) {
    if(this.subs == null) return null;
    return this.subs.byId[subOptId];
  },
  
  getDefaultSubOptions: function PTO_getDefaultSubOptions() {
    if(this.subs == null) return null;
    var opts = [];
    for(var i=0; i < this.subs.list.length; i++) {
      var ptSub = this.subs.list[i];
      if(ptSub.defaultOn) {
        opts.push({ id: ptSub.id, s: ptSub.defaultSelected, d: ptSub.defaultPrices});
      }
    }
    return opts;
  }
});

var ProductTypeSubOption = Class.create({
    CLASSDEF: {
        name: 'ProductTypeSubOption'
    },
  
  initialize: function PTSO_initialize(options, option) {
    this.id = options.id;
    this.name = options.n;
    this.value = options.c;
    this.defaultPrices = options.pta;
    this.option = option;
    this.defaultOn = options.defo;
    this.defaultSelected = options.defs;
  }/*,
  
  getDelta: function PTSO_getDelta(product, color_type) {
    var fc = product.optionsByPoId[this.id];
    if(fc != null) {
      return fc.delta[color_type];
    }
    return 0;
  },
  
  getFieldChoice: function PTSO_getFieldChoice(product) {
    return product.optionsByPoId[this.id];
  }*/
});

var ProductTypeProcess = Class.create({
  CLASSDEF: {
      name: 'ProductTypeProcess'
  },
  
  initialize: function PTP_initialize(productType, options) {
    this.id = options.id;
    this.process = processes.byId[options.id];
    this.priceMode = options.pm;
    this.defaultStitchCount = options.sc;
    
    this.prices = options.p;
    this.priceTableId = options.pt;
    this.useDefaultPricing = options.use_default_pricing;
    if(this.priceTableId != null && this.priceMode == 2) {
      this.priceTable = d.priceTables[this.priceTableId];
      if(this.priceTable == null) {
        log("ERROR: unable to get price table '" + this.priceTableId + "' (for product type process)");
      }
    }
    this.mixing = {};
  },
  
  addMix: function PTP_addMix(otherProcessId) {
    this.mixing[otherProcessId] = processes.byId[otherProcessId];
  },
  
  getAllowedProcesses: function PTP_getAllowedProcesses(existingProcesses) {
    var result = {};
    result[this.id] = true;
    if(existingProcesses == null) {
      existingProcesses = this.mixing;
    }
    for(var k in this.mixing) {
      if(existingProcesses[k] != null) {
        result[k] = true;
      }
    }
    return result;
  },
  
  getPriceTable: function() {
    if((this.useDefaultPricing) || (this.priceTable == null)) {
      return this.process.priceTable;
    } else {
      return this.priceTable;  
    }
    
  }
});

var Process = Class.create({
  CLASSDEF: {
      name: 'Process'
  },
  
  initialize: function(options) {
    this.id = options.id;
    this.abbr = options.abbr;
    this.name = options.name;
    this.zIndex = options.z;
    this.usesDPI = options.dpi;
    this.flags = options.flags;
    
    this.allowImageUpload = options.allow_image_upload;
    this.allow = {};
    this.allow["image"] = this.allowImages = options.allow_images;
    this.allow["text"] = this.allowText = options.allow_text;
    this.allow["teamname"] = this.allowTeamNames = options.allow_teamnames;
    this.allow["placeholder"] = this.allowPlaceHolder = this.allowImages || this.allowText;
    
    //this.allowDigitization = options.dig;
    this.digitizationFee = options.dig_fee;
    this.digitizationFeePerInch = options.dig_fee_per_inch;
    this.stitchesPerInch = options.s_per_inch;
    this.maxColors = options.max_colors;
    this.autoSplit = options.auto_split;
    this.defaultTextColorId = options.def_text_color;
    this.usesStitchCounts = options.uses_stitch_counts;
    this.usesColorTypes = options.uses_color_types;
    
    this.prices = options.p;
    this.priceTableId = options.pt;
    if(this.priceTableId != null) {
      this.priceTable = d.priceTables[this.priceTableId];
      if(this.priceTable == null) {
        log("ERROR: unable to get price table '" + this.priceTableId + "' (for process)");
      }
    }
  },
  
  isValid: function() {
    var valid = false;
    $H(this.allow).each(function(pair) {
        if (pair.value) valid = true;
    });
    return valid;
  },
  
  isWilcomEMB: function() {
    return ((this.flags & 1) == 1);
  }
});

var PriceTable  = Class.create({
  CLASSDEF: {
      name: 'PriceTable'
  },
  
  initialize: function(options) {
    this.id = options.id;
    this.name = options.n;
    this.rows = options.rows;
    //remove repeater....
    this.rowRepeater = this.rows.splice(this.rows.length-1,1)[0];
    this.cols = options.cols;
    this.prices = options.p;
    this.priceMode = options.pm;
    this.incremental = options.inc;
  },
  
  calcPrice: function(rowValue, colValue, colorType) {
		log("PriceTable.calcPrice:" + rowValue + "," + colValue + ":" + colorType + " (" + this.name + ")");
		if(this.priceMode == 1) {
		  colorType = 0; //this table only supports flat pricing
		}
		var rowIndex = -1; 
		var colIndex = -1;
		
		for(var i=0; i < this.cols.length; i++) {
			var col = this.cols[i];
			if((colValue >= col[0])&&((colValue <= col[1])||(col[1] == -1))) {
				colIndex = i;
				break;
			}
		}
		if(colIndex == -1) {
			log("PriceTable.calcPrice:Unable to get the price column for " + colValue);
			return [0, 0, 0];
		}
		for(var i=0; i < this.rows.length; i++) { //skip first row (the end repeater..)
			var row = this.rows[i];
			if((rowValue >= row[0])&&(rowValue <= row[1])) {
				rowIndex = i;
				break;
			}
			
		}
		
		if(rowIndex == -1) { //we are above largest defined range... use repeater...
			log(this.rowRepeater);
			if(this.incremental) {
			  log("incremental");
			  var price = this.prices[colIndex][this.rows.length][colorType];
			  return [price, rowValue, rowValue+1];
			} else {
        var price = (this.rows.length == 0) ? 0 : this.prices[colIndex][this.rows.length-1][colorType];
        var basePrice = price;
        //TODO: deduct stitch counts then mod to get repeat count...
        var left = rowValue - ((this.rows.length == 0) ? 0 : this.rows[this.rows.length - 1][1]);
        var blocks = parseFloat(left) / parseFloat(this.rowRepeater[1]);
        //round blocks up
        blocks = Math.ceil(blocks);
        price += blocks * this.prices[colIndex][this.rows.length][colorType];
        log("PriceTable.calcPrice: Row Index using repeater, basePrice=" + basePrice + " price=" + price + " left=" + left + " blocks=" + blocks);
        var from = ((this.rows.length == 0) ? 0 : this.prices[colIndex][this.rows.length-1][0]) + ((blocks -1) * this.rowRepeater[1]);
        var to = from + this.rowRepeater[1] ;
        log("PriceTable.calcPrice: Price=" + price);
        return [price, from, to];
      }
    } else {
			log("Price Table price (" + colIndex + "," + rowIndex +") =" + this.prices[colIndex][rowIndex][colorType] );
			return [this.prices[colIndex][rowIndex][colorType], this.rows[rowIndex][0], this.rows[rowIndex][1]];
		}
	}/*,
	
	not used because prices are now incremental...
	calcSingleAreaPrice: function(rowValue, colValue, colorType, currentCount) {
	  var priceData = this.calcPrice(currentCount, colValue, colorType); //use current count.....
	  
	  var priceData = this.calcPrice(rowValue, colValue, colorType);
	  var normalSinglePrice = priceData[0] / parseFloat(rowValue);
	  //round to full cents...
	  normalSinglePrice = parseFloat(parseInt(normalSinglePrice * 100, 10)) / 100.0;
	  if(rowValue == currentCount) {
	    //this is the last price... do rounding adjustment....  
	    var lPrice = normalSinglePrice * (rowValue-1);
	    var adjSinglePrice = priceData[0] - lPrice;
	    log("adjusting final dec pricetable price from " + normalSinglePrice + " to " + adjSinglePrice + " to make total (" + rowValue + " items) match " + priceData[0]);
	    normalSinglePrice = adjSinglePrice;
	  }
	  priceData[0] = normalSinglePrice;
	  return priceData;
	}*/
});

var Category = Class.create({
    CLASSDEF: {
        name: 'Category'
    },
  
  initialize: function(options, productType) {
    this.id = options.id;
    this.name = options.n;
    this.productType = productType;
    this.load(options.p);
    this.infoUrl = options.i;
    this.currentStartIdx = 0;
  },
  
  load: function(products) {
    this.products = products;
    this.loaded = true;
  },
  
  getProducts: function(callback, asyncContainer) {
    if(this.loaded) {
      if(callback) {
        callback();
      }
      return true;
    }
    if(callback) {
      var self = this;
      d.getData({c:this.id, cat_ptid:this.productType.id}, function(response) {
        if(!self.loaded) {
          alert("Error loading category " + self.id);
          return;
        }
        callback();
      }, asyncContainer);
    }
    return false;
  },
  
  displayProducts: function(visibleProductId) {
      log("displayProducts();");
      
      var html = "<ul>"+this.generateHtml()+"</ul>";
      
      
      Element.update("product_list_" + this.productType.id, html);
      
      this.bindElements();
      this.productType.currentCategory = this;
      this.selectProduct(visibleProductId);

  },
  
  generateHtml: function() {
    var html = "";
    for(var i=0;i< this.products.length;i++) {
      var product = this.products[i];
      if(product.u != "") {//filter out broken products
        html += '<li style="display:inline;" id="ps_' + this.id + '_' + this.products[i].id + '"><img src="' + this.products[i].u + '" id="psi_' + this.id + '_'+ this.products[i].id + '" alt="' + this.products[i].n + '" class="product_cell" onmousemove="d.piMouseMove(this);" onmouseout="d.piMouseOut(this);"/></li>';
      }
    }
    return html;
  },
  
  bindElements: function() {
    for(var i=0;i< this.products.length;i++) {
      var product = this.products[i] ;
      if(product.u != "") {
        var img = $('psi_' + this.id + '_' + this.products[i].id);
        this.bindProductCell(img, this.products[i]);
      }
    }
  },
  
  bindProductCell: function(el, product) {
    var self = this;
    el.onclick = function() {
      //self.selectProduct(id);
      self.productType.changeSelectedProduct(product.id);
      d.itemChanged();
    };
    new Tooltip(el, product.n);
  },
  
  selectProduct: function(id) {
    this.deselectProduct();
    var img = $('psi_' + this.id + '_'+ id);
    if(img!=null) {
      this.selectedProductCell = img;
      this.selectedProductCell.className = "product_cell_selected";
    }
  },
  
  deselectProduct: function() {
    if(this.selectedProductCell!=null) {
      this.selectedProductCell.className = "product_cell";
    }
    this.selectedProductCell = null;
  },
  
  findProduct: function(productId) {
    for(var i=0;i< this.products.length;i++) {
      var product = this.products[i];
      if(product.id == productId) {//filter out broken products
        return product;
      }
    }
    return null;
  },
  
  loadSubCats: function(selectedCatId) {
    
  }, 
  
  selectSubCat: function(el, id) {
   
  },
  
  bindSubCat: function(el, cat) {
   
  }
  
});

