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 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); };
}




var debug = null;

function log(msg, trace) {
  //if(BrowserDetect.browser == "Firefox") {
    try {
      console.debug(msg);
      if(trace) {
        console.trace();
      }
    } catch(e) {}
  //}
  if((debug!=null)&&(debug.style.display != 'none')) {
    debug.value = msg + "\n" + debug.value;
  }
  /* else if(debug==null) {
    alert("no debug: " + msg);
  } else {
    alert(debug.style.display);
  }
  */
}

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='/ppr/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="";
  
  if(options.positionCallback == null) {
    if( ((((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop()) <10 ) {
      div.style.top = "10px";
    } else {
      div.style.top = (((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop() + "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);
  }
  
  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;
  }
  
  if( ((((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop()) <10 ) {
    div.style.top = "10px";
  } else {
    div.style.top = (((f_clientHeight() / 2) - (div.clientHeight/2))) + f_scrollTop() + "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 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;
}

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_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};

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_DIGITIZATION = 11;

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) {
    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.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.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.affiliateDiscounting = true;
    this.declibMarkup = declibMarkup;
    
    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;
    
    
    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.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;
  },
  
  setDesignerOptions: function D_setDesignerOptions(flags) {
    this.designerOptions = new DesignerOptions(flags);
  },
  
  setDesignElements: function D_setDesignElements(sizer, designPane) {
    this.sizer = sizer;
    this.designPane = designPane;
    
    this.roSizers = new Hash();
    
    this.roSizers["TR"] = $("ts_tr");
    this.roSizers["TL"] = $("ts_tl");
    this.roSizers["BR"] = $("ts_br");
    this.roSizers["BL"] = $("ts_bl");
    
  },
  
  setManagePaneContainer: function D_setManagePaneContainer(el) {
    this.managePaneContainer = el;
  },
  
  setDefaultProcess: function D_setDefaultProcess(processId) {
    this.userSelectedProcessId = processId; 
    this.defaultProcess = processes.byId[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]);
    }, "product_config");
  },
  
  //called from the cart
  selectConfiguredProduct: function D_selectConfiguredProduct(id, doUpdate, doSelect) {
    log("d.selectConfiguredProduct()");
    this.currentCProduct = this.cart.getProduct(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 {
      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.getProduct(oldId);
    cp.id = newId;
    delete this.cart.productsById[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);
    }
  },
  
  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) {
    if(processId == null) {
      //select process
      processId = this.showSelectProcess("image");
      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;
    
    
    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;
        } 
        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), {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(":");
                  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), {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;
                      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" ;
            }
          }
        }
        this.addNewItemByAsset(this.newProcessId, newAsset, opts);
    }.bind(this));
    d.track("add-image");
    return;
  },
  
  addText: function D_addText(processId) {
    if(processId == null) {
      processId = this.showSelectProcess("text");
      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;
    $("new_text").value = "";
    popup("new_text_popup");
    $("new_text").focus();
  },
  
  addTeamName: function D_addText(processId) {
    if(processId == null) {
      processId = this.showSelectProcess("text");
      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"));
    }
  },
  
  //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}
    };
    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;
      }
    }
    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();
    }
  },
  
  showSelectProcess: function D_showSelectProcess(type) {
    var types = ["text","teamname","image"];
    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) {
    //  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 {
      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....
          $("add_" + type + "_type_" + process.id).className="";
        } 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(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));
          }
          
        }
      }
      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";
    }
    opts.tc = pwColorPicker.getDefaultColor(this.newProcessId, "#FF3333", proc.defaultTextColorId);
    this.addNewItemByAsset(this.newProcessId, new TextAsset(), opts);
  },
  
  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) {
    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) {
    this.currentCProduct.updateMultiQty(el.value, fieldId, optionId, subOptionId);
    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(is_layout_change) {
      this.currentCProduct.onLayoutChanged();
      this.currentCView.queueViewUpdate();
      $("save_div").style.display="";
    }
    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;
      
    }
  },
  
  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.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;
    }
    return price;
  },
  
  formatPrice: function D_formatPrice(price, clazz, applyTax) {
    if(applyTax==null) {
      applyTax = true;
    }
    var taxWarn = "";
      if((this.tMod != 1)&&(applyTax) && !d.currentCProduct.product.taxExempt) {
      log("Applying tax mod of " + this.tMod);
      taxWarn = '<span class="tax_warning">*</span>';
      price *= this.tMod;
    }
    price *= this.cMod;
    var dc = price.toFixed(2);
    var codeHtml = this.curCode;
    if(clazz!=null) {
      codeHtml = '<span class="' + clazz + '">' + this.curCode + "</span>";
    }
    
    switch(this.priceFormat) {
      case 1: return this.curGlyph + dc + " " + taxWarn;
      case 2: return dc + " " + codeHtml + taxWarn;
      case 3: return dc + " " + taxWarn;
      case 4: return dc + this.curGlyph + " " + taxWarn;
      default: return 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 = "/ppr/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 .popup")[0].style.width="650px";
      },
      onSuccess: function D_onSuccess() {
        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 = "/ppr/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";
      },
      onSuccess: function D_onSuccess() {
        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(discounts) {
    this.discounts = discounts;
  },
  
  setAffiliateDiscounting: function D_setAffiliateDiscounting(affiliateDiscounting) {
    this.affiliateDiscounting = affiliateDiscounting;
  },
  
  getDiscount: function D_getDiscount(qty, price, cost) {
    var dis = 0;
    for(var i=0; i < this.discounts.size(); i++) {
      if(qty > this.discounts[i].m) {
        log("found discount level at " + this.discounts[i].m + " discount=" + this.discounts[i].d);
        dis = this.discounts[i].d;
      }
    }
    if(dis == 0) {
      return 0;
    }
    log("applying discount of " + dis);
    if(this.affiliateDiscounting) {
      return Math.round(price * dis) / 100.0 * qty;
    } else {
      return Math.round(cost * dis) / 100.0 * qty;
    }
  },
  
  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) {
           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 + "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);
    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));
      }
    }
  }
});


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) {
      $("declib_loading").show();
      var aKey = asyncStart("decoration_libraries_container");
      this.registerLibraryCallback = function() {
        asyncFinish(aKey);
        $("declib_loading").hide();
        decorationLibrary = this.libraries[processId];
        callback(decorationLibrary);
      }.bind(this);
      var modeStr = "";
      if(d.mode == DESIGNER_MODE_CONFIGURE) {
        modeStr="&for_product=1";
      }
      var ajax = new Ajax.Updater("decoration_libraries_container", d.ajaxUrl("/designer/get_library?process=" + processId + modeStr), {asynchronous:true, evalScripts:true, insertion: Insertion.Bottom}); 
    } else {
      decorationLibrary = this.libraries[processId];
      callback(decorationLibrary);
    }
  },
  
  registerLibrary: function DLM_registerLibrary(library, processId) {
    log("registerLibrary: " + processId);
    this.libraries[processId] = 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);
      queueImageLoading($("image_thumb_" + this.currentProcessId),image.url,dims[0],dims[1], function() {});
      $("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;
          var fixedMarkup = 0;
          if(d.currentCProduct.usingCustomProduct()) {
            // leave markup as %0
          } else if(d.defaultMarkupType == 0) {
            percentMarkup =  1 + d.defaultMarkupAmount / 100;
          }
          
          $("decoration_price_field_" + this.currentProcessId).innerHTML = "$" +( (dec_price.range1price + (d.declibMarkup / 100) * dec_price.range1price) * percentMarkup).toFixed(2);
          Element.show("decoration_price_" + this.currentProcessId);
        }
      }
	  						
	  						var int_y_pos=get_page_ypos($("dec_lib_li_"+image.uId));
							var int_x_pos=get_page_pos($("dec_lib_li_"+image.uId));
							
							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($("dec_lib_li_"+image.uId))+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];
      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);
  }
});


// 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) {
    this.container = container;
    this.rotationWidget = rotationWidget;
    this.l = l;
    this.r = r;
    this.t = t;
    this.callback = callback;
    
    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);
    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; 
     
  },
  
  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);
  },
  
  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);
    //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);
    }
  }
  
  
});


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");
    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.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()) { //the derived object doesnt have special start positon logic (Text)
      var w =  this.cViewProcess.productProcess.fullWidth;
      var h = this.cViewProcess.productProcess.fullHeight;
      if(this.options.w == null) {
        if((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);
      }
      this.top = (h - this.height) / 2;
      this.left = (w - this.width) / 2;
    }
    
    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.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() {
    
  },
  
  initialiseManagePane: function Item_initialiseManagePane() {
    var self = this;
    log("Item.initialiseManagePane()");
    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
      this.setPosition();
    } 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.el.onclick = function(ev) {
      if (ev==null) {
        ev = window.event;
      }
      self.select(false, ev);
    };
    */
    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();
    }
  },
  
  //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";
  },
  
  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;
  },
  
  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);
  },
  
  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="";
    
    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 {
        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) {
    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) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    var o = this.serializeToOptions();
    
    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;
  },
  
  updateTitleSize: function Item_updateTitleSize() {
    if (this.titleSize != null && 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;
      }
      this.titleSize.innerHTML = d.convertLengthFromInches(w).toFixed(2) + d.getLengthUnit() + " x " + d.convertLengthFromInches(h).toFixed(2) + 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();
      //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);
    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 = {};
    } else {
      for(var i=0;i < imageEffects.size; i++) {
        if(imageEffects[i][3].single) {
          if(this.effects[imageEffects[i][0]] != null) {
            this.currentSingleEffect = imageEffects[i];
            break;
          }
        }
      }
    }
    this.effectsToBeApplied = false;
    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); });
    
    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>';
      for(var i=0;i< imageEffects.length;i++) {
        var effect = imageEffects[i];
       /* if((i>0)&&(i%2==0)) {
          cell = "</tr><tr>";
        } else {
          cell = "";
        }*/
        cell = '<li class="et_effect" 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;
    
      this.selectDefaultEffect(this.effects);
    
    }
  },
  
  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() {
    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(deg==0) {
      setTransPng(this.el, this.asset.getUrl());
    } else {
      this.el.src = "/images/trans.gif"; //it looks crap when the image appears unrotated/oversize...
    }
    /*
    if(useAlphaHack) {
      this.el.src = d.pathPrefix + "/images/trans.gif";
      this.el.style.fontSize = "1px";
      //el.style.display="inline-block";
      this.el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.asset.getUrl() + '", sizingMethod="scale")';
      //this.el.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(sizingMethod="auto expand")\nprogid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.asset.url + '", sizingMethod="scale")';
    } else {
      this.el.src = this.asset.getUrl();
    }
    */
    this.cViewArea.canvas.appendChild(this.el);
    
    //var self = this;
    
    if((deg==0)&&(this.colors == null)) {
      this.asset.loadWorkingAsset(function() { 
        this.setSrc(this.asset.getWUrl());
        /*if(this.thumb != null) {
          setTransPng(this.thumb, this.asset.getWUrl());
          //self.thumb.src = self.asset.getWUrl();
        }*/
        log("loaded working asset");
      }.bind(this));
    } else {
      this.updateImageSrc();
      this.asset.loadWorkingAsset(function() { 
        /*if(this.thumb != null) {
          setTransPng(this.thumb, this.asset.getWUrl());
          //self.thumb.src = self.asset.getWUrl();
        }*/
        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==null) return;
    setTransparentImage(this.el, this.el, url, function() {
        if(self.itemType == 0) {
          self.translateRotated(rotCount, rotRef);
          self.checkPosition();
        }
        if(updateInfo != null) {
          if((useCroppedImageHack)&&(updateInfo.checkCropIssue == true)) {
            window.setTimeout( function() {
                var refreshJoin = "?";
                if(self.currentSrc.indexOf("?") != -1) {
                  refreshJoin = "&";
                }
                log("Resetting Src Because of useCroppedImageHack to " + self.currentSrc + refreshJoin + "refresh");
              $(self.el.id).src = self.currentSrc + refreshJoin + "refresh";
            }, 1000);
            
          }
        }
    });
  },
  
  getSrc: function ImgItem_getSrc() {
    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.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.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();
    }
  },
  
  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();

    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();
  },
  
  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 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)
  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});;
  },
  
  updateImageSrc: function ImgItem_updateImageSrc() {
    var deg = this.getRotation();
    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", this.getWorkingState()).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();
    }
    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);
        this.el.style.height = height + "px";
        
        var width = parseInt(height * this.rotatedRef.a, 10);
        this.el.style.width = width + "px";
        
        return false;
      }
      this.el.style.height = height + "px";
      var width = parseInt(height * this.rotatedRef.a, 10);
      this.el.style.width = width + "px";
      return true;
    } else {
      this.el.style.height = height + "px";
      if((this.maintainAspect)||(this.rotated)) {
        var width = parseInt(height * this.rotatedRef.a, 10);
        this.el.style.width = width + "px";
      }
      return true;
    }
    
  },
  
  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";
    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) {
    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(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.colorCount != null) {
        o.cc = this.colorCount;  
      }
      
      if (this.colorWay != null) {
        o.cw = this.colorWay ;
      }
      
      //effects
      var eo = {};
      hasEffects = 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) {
            hasEffects = true;
            eo[k] = eoo;
          }
        }
      }
      if(hasEffects) {
        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]", this.effects, 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;
      }
      
      if(this.oldTabName == "effects") {
        if(this.effectsToBeApplied) {
          if(confirm(ml("Do you want to apply the selected effects?"))) {
            this.effectChanged(true);
          } else {
            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 = '<table cellpadding="0" cellspacing="0">';
	  var html = '<ul>';
      for(var i=0; i < borderEffects.size(); i++) {
		/*if(i%5 == 0) {
			if(i==0) {
				html += "<tr>";
			} else {
				html += "</tr><tr>";
			}
		}*/
		
        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 += "</tr></table>";
	  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 effect = imageEffects[eIdx];
    var eCode = effect[0];
    var workingState = this.getWorkingState();
    var hasChanged = false;
    if(workingState[eCode] == null) {
      workingState[eCode] = {};
      hasChanged = true;
    }
    this.loadEffectPanel(workingState[eCode], effect);
    if(hasChanged) {
      this.effectChanged();
      this.updateEffectSelection(this.getWorkingState());
    }
  },
  
  loadEffectPanel: function ImgItem_loadEffectPanel(state, effect) {
    if((effect!= null)&&(this.currentEffect == effect)) {
      return;
    }
    var html = "<div class='effect_controls'>";
	html+='<div class="effect_header">';
	
    if(effect!=null) {
      html += '<h4>' + effect[1] + '</h4><a href="#" id="e_rem_' + this.elId + '">Remove</a>';
	  html+='</div>';
    } else {
      html += '<h4>No Effects Applied</h4>';
	  html+='</div>';
	  //html+='<p>Click on an effect to apply it to your image.</p>';
    }
    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_preview">'+
			'<div class="title"><input type="checkbox" id="e_p_c_' + this.elId + '"/><label>Preview</label></div>'+
			'<img id="e_p_i_' + this.elId + '" style="display:none;"/>'+
		'</div>'+
		'<div class="et_right">'+
			'<ul>'+lineHtml+'</ul>'+
			'<div class="et_submit">'+
			'<a href="#" id="e_p_a_' + this.elId + '" class="okay">apply</a>'+
			'<a href="#" id="e_p_ac_' + this.elId + '" class="cancel">cancel</a>'+
			'</div>'+
			
			//'<img src="' + d.pathPrefix + '/images/mp/tick.gif" alt="Apply" title="Apply" style="display:none;cursor:pointer;" id="e_p_a_' + this.elId + '"/>'+
			//'<img src="' + d.pathPrefix + '/images/mp/cross.gif" alt="Cancel" title="Cancel" style="display:none;cursor:pointer;" id="e_p_ac_' + this.elId + '"/>'+
		'</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;
    
    if(this.showEffectPreview == null) {
      this.showEffectPreview = d.showEffectPreview;
    }
    var cb = $("e_p_c_" + this.elId);
    if(this.showEffectPreview) {
      cb.checked = true;
    }
    cb.onclick = function() {
      d.showEffectPreview = cb.checked;
      self.showEffectPreview = cb.checked;
      self.effectChanged();
    };
    
    var button = $("e_p_a_" + this.elId);
    if(this.effectsToBeApplied) {
      button.style.display='';
    }
    button.onclick = function() {
      self.effectChanged(true);
	  return false;
    };
    
    var button = $("e_p_ac_" + this.elId);
    if(this.effectsToBeApplied) {
      button.style.display='';
    }
    button.onclick = function() {
      //self.effectChanged(true);
      self.workingState = null;
      self.effectsToBeApplied = false;
      self.selectDefaultEffect(self.effects);
	  return false;
    };
    
    
    if(effect != null) {
      var rem = $("e_rem_" + this.elId);
      rem.onclick = function() {
        self.removeEffect(effect);
        return false;
      };
      if(effect[3].single) {
        if((this.currentSingleEffect != null)&&(this.currentSingleEffect !=effect))  {
          this.removeEffect(this.currentSingleEffect, true);
        }
        this.currentSingleEffect = effect;
      }
    }
    
  },
  
  removeEffect: function ImgItem_removeEffect(effect, noBubble) {
    this.currentEffect = null;
    var eCode = effect[0];
    log("removeEffect:" + eCode);
    state = this.getWorkingState();
    state[eCode] = null;
    if(noBubble!=true) {
      this.effectChanged();
      this.selectDefaultEffect(state);
      this.effectChanged();
    }
  },
  
  selectDefaultEffect: function ImgItem_selectDefaultEffect(state) {
    this.updateEffectSelection(state);
    var effectLeft = false;
    for(var i = 0; i < imageEffects.size(); i++) {
      var effect = imageEffects[i];
      var td = $('i_ef_' + this.elId + '_' + effect[0]);
      var eCode = effect[0];
      if(state[eCode] != null) {
        this.loadEffectPanel(state[eCode], effect);
        effectLeft = true;
        break;
      }
    }
    if(!effectLeft) {
      this.loadEffectPanel(null, null);
    }
  },
  
  updateEffectSelection: function ImgItem_updateEffectSelection(state) {
    for(var i = 0; i < imageEffects.size(); i++) {
      var effect = imageEffects[i];
      var td = $('i_ef_' + this.elId + '_' + effect[0]);
      if(state[effect[0]] != null) {
        log(effect[0] + " is on");
        td.className = "et_effect select";
      } else {
        td.className = "et_effect";
      }
    }
  },
  
  effectChanged: function ImgItem_effectChanged(skipPreview) {
    if(this.settingEffectsPanel){
      return;
    }
    this.updateWorkingState();
    if( (skipPreview == true) || (!this.showEffectPreview)) {
      this.effects = this.workingState;
      this.workingState = null;
      log("effectChanged: update image");
      this.effectsToBeApplied = false;
       $("e_p_a_" + this.elId).style.display="none";
       $("e_p_ac_" + this.elId).style.display="none";
       this.updateImageSrc();
       this.setDirty();
       //d.itemChanged(true);
    } else {
      this.effectsToBeApplied = true;
      $("e_p_a_" + this.elId).style.display="";
      $("e_p_ac_" + this.elId).style.display="";
      var bgColorObj = d.currentCProduct.getSelectedColor();
      var bgColor = "#FFFFFF";
      if(bgColorObj != null) {
        bgColor = bgColorObj.colors[0][0];
      }
      
      url = d.pathPrefix + "/designer/get_image?id=" + this.asset.id + this.getColorParam() + "&preview=1&bg_color=" + encodeURIComponent(bgColor) + "&" + this.serialiseState("es", this.getWorkingState()).join('&');
      
      var previewImg = $("e_p_i_" + this.elId);
      previewImg.style.display="";
      setImageUrl(previewImg, previewImg, url);
      log("effectChanged: update preview");
    }
    
  },
  
  updateWorkingState: function ImgItem_updateWorkingState() {
    if(this.currentEffect == null) {
      return;
    }
    var eCode = this.currentEffect[0];
    var state = this.getWorkingState();
    var effectState = state[eCode];
    if(effectState == null) {
      effectState = {};
      state[eCode] = effectState;
    }
    for(k in this.currentControls) {
      var control = this.currentControls[k];
      control.saveToState(effectState);
    }
  },
  
  serialiseState: function ImgItem_serialiseState(prefix, state, queryComponents) {
    log(state);
    if(prefix==null) {
      prefix = "t[" + this.id + "][eff]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }

    for(k in state) {
      var e = state[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() {
    for(k in this.effects) {
      return true;
    }
    return false;
  },
  
  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.digitizationFee = process.digitizationFeePerInch ? process.digitizationFee * this.digitizationArea : process.digitizationFee;
    this.digitizationFee *= this.cView.configuredProduct.getPercentMarkup(true);
    
    if(this.digitizedAsset.primaryItem() == this) {
      log("Primary Digitization Found");
    } else {
      log("Secondary Digitization Found");
    }
    this.stitchCount = 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;
      if(this.digitizedAsset.primaryItem() == this) {
        if(!this.splitColors) {
          notice = ml('Digitization 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(this.digitizationFee), "fee_currency_code"), this.digitizationArea.toFixed(2), this.cViewProcess.productProcess.process.maxColors]);
        } else {
          notice = ml('Digitization Fee = %1s<br />Size =%2ssq inches<br/>Colors = Approximated. Final thread colors may vary.',
          [d.formatPrice(d.roundPrice(this.digitizationFee), "fee_currency_code"), 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 ? "Alice" : "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.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 ? "Alice" : "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(el) { 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(el) {  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 = dopt(this.options, "g", "0");
    this.effectType.value = 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.showImageAdvancedOpen())) {
      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() {
    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() {
    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() {
    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);
    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);
    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) {
    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() {
    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.el.style.fontSize = "1px";
      //el.style.display="inline-block";
      this.el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.asset.url + '", sizingMethod="scale")';
      //this.el.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(sizingMethod="auto expand")\nprogid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.asset.url + '", sizingMethod="scale")';
    } else {
      this.el.src = this.asset.url;
    }*/
    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);
    }
    this.rotDelay = window.setTimeout( function() { 
      self.setDirty();
      self.setText(false); 
      //d.itemChanged(true);
    }, 100);
    return;
  },
  
  serializeToOptions: function Item_serializeToOptions(o) {
    o = TextItem.parentClass.method("serializeToOptions").call(this,o);
    
    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.e = this.effectType.value;
    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;
      
      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")}
      });
    }
    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);
    }
    /*
    } else {
      url =  d.pathPrefix + "/text/transform_text_bling?" + this.serialize([],"a");
      var _el = this.el;
      this.el.onload = function() {
        _el.style.visibility = "hidden";
        _el.removeAttribute("width");
        _el.removeAttribute("height");
        _el.style.width = null;
        _el.style.height = null;
        self.width = _el.width / self.cViewProcess.productProcess.designScale;
        self.height = _el.height  / self.cViewProcess.productProcess.designScale;
        if(self.doCenter) {
          log("centering");
          self.top = (self.cViewProcess.productProcess.fullHeight - self.height) / 2;
          self.left = (self.cViewProcess.productProcess.fullWidth - self.width) / 2;
          self.doCenter = false;
        } else {
          log("NOT centering");
        }
        log(self.width);                                                   
        self.setPosition();
        _el.style.visibility = "";
        if(self.selected) {
          self.autoRepositionHandles();
        }
        finishAsyncAction();
      };
      this.el.src = url;
    }*/
  },
  
  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.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) {
    this.el.style.height = height + "px";
    return true;
  },
  
  setWidth: function TxtItem_setWidth(width) {
    this.el.style.width = width + "px";
    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 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) * 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;
  }
});



/* 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]">' +
						//'<table cellspacing="0">' +
						  //'<tr>' +
							//'<td width="160">' +
							  '<div class="et_list">' +
								'<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 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>';

      
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
];


var Cart = Class.create({
    CLASSDEF: {
        name: 'Cart'
    },
  
  initialize: function Cart_initialize() {
    this.products = [];
    this.productsById = new Hash();
    this.derivedProductsByLinkId = {};
    //this.allProducts = {}; //includes derived products
    this.id = -1;
    this.digitizedAssets = {};
    //this.editing = false;
  },
  
  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;
    } //else {
      this.products.push(product);
      this.productsById[product.id] = product;
    //}
    //this.allProducts[product.id] = product;
  },
  
  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;
  },
  
  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)) {
        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)) {
        return this.products[i];
      }
    }
    return null;
  },
  
  copyProduct: function Cart_copyProduct(id) {
    var product = this.productsById[id];
    var cp = this.getUnsavedProduct();
    if(cp != null) {
      cp.copy(product);
      cp.save(true);
    } 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");
          }
        }
       });
    }
  },
  
  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) {
        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="";
      }
    }
    log("updateCartPrice Done");
  },
  
  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) {
          if(p == cProduct) {
            $("cart_" + p.id).className="cp_details selected";
			$("cart_options_" + p.id).className="cp_options selected";
          } else {
            $("cart_" + p.id).className="cp_details";
			$("cart_options_" + p.id).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) {
    this.digitizedAssets = {};
    for(var i=0; i < this.products.length; i++) {
      var p = this.products[i];
      if(p == configuredProduct) {
        p.doRollBack();
      } 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;
  }
});


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...
      delete d.cart.digitizedAssets[this.asset.id];
      //remove the cart item
      //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) {
    this.id = id;
    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.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;
    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;
      
    if(this.cp_type == CP_DIGITIZATION) {
      this.asset_id = options.asset_id;
      this.price = options.price;
//      this.primaryItemId = options.cpa;
    } else {
    
      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);
            if(cOpt != null) {
              for(var k=0; k < optData.sub.length; k++) {
                var subData = optData.sub[k];
                cOpt.addSelectedOption(subData.id, subData.qty);
              }
            }
          }
        } 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) { //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;
      }
    }
  },
  
  toString: function() {
    return this.product.name;
  },
  
  initFields: function CP_initFields() {
    this.cFields = {};
    //load from configured product options first
    var fieldOpts = this.options.f;
    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) { //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) {
        var pField = this.product.fields.byId[field.id];
        if(pField!=null) { //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 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);
    }
    $("product_views").innerHTML = html;
  },
  
  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;
  },
  
  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) {
      var c = (this.selectedColorId==null) ? null :  this.product.colors.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() {
    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;
        for(var sizeId in sizeField.options) {
          var sizeChoice = sizeField.options[sizeId];
          if((sizeChoice.qty > 0)&&(!sizeChoice.def.isMulti)) {
            var sizeColors = this.product.sizeColorCombinations[sizeId];
            if(sizeColors != null) { 
              if(sizeColors[pColor.productChosenOptionId] != true) {
                allAvailable = false;
                log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId); 
                break;
              }
            } else {
              log("Error: size " + sizeId + " has no scc entries");
            }
          }
        }
        if(allAvailable) {
          result.push(pColor);
        }
      }
    } 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 != 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;
    }
    
  },
  
  //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.usingCustomProduct() && this.markupMode == 0)) {
      percentMarkup =  1 + this.markupAmount / 100;
      log("Percent Markup=" + percentMarkup);
    }
    return percentMarkup;
  },
  
  getFixedMarkup: function CP_getFixedMarkup() {
    var fixedMarkup = 0;
    if(this.usingCustomProduct()) {
      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;
  },
  
  getUnitPrice: function CP_getUnitPrice() {
    var percentMarkup = this.getPercentMarkup();
    var fixedMarkup = this.getFixedMarkup();
    log("percentMarkup= " + percentMarkup + " fixedMarkup=" + fixedMarkup);
    log("Branding is :"+this.brandId);
    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]);
		}
    
    var basePrice = cost * percentMarkup + fixedMarkup;
    
    var baseColorDelta = (cost - this.product.price[0]) * percentMarkup;
    
    
    var basePriceWithDecoration = basePrice;
    var baseCostWithDecoration = cost;
    
    
    log("basePrice=" + basePrice + " cost=" + cost);
    
    var pricingData = {
      basePrice: basePrice,
      baseColorDelta: baseColorDelta,
      basePriceWithDecoration: basePrice,
      baseCostWithDecoration: cost,
      unUsedDecorationCost: 0.0,
      baseCost: cost,
      extras: [],
      decorationPrice: 0.0,
      decorationCost: 0.0
    };
    
    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 && d.userSelectedProcessId != -1) { //as soon as we have started decorating we no longer use default prices...
      log("No Customising Done: Using default decoration prices from process " + this.defaultProcessId);
      for(var i =0; i < this.product.views.list.size(); i++) {
        var pView = this.product.views.list[i];
        var noUsageProcess = pView.getDefaultPricingProcess(this.defaultProcessId);
        if(noUsageProcess != null) {
          var cost = noUsageProcess.getDefaultDecorationPrice(colorType);
          log("Default Decoration Cost:" + cost);
          pricingData.basePriceWithDecoration += (cost * percentMarkup);
          pricingData.baseCostWithDecoration += cost;
          //keep track of this amount so we can take it from any decoration pricing....
          pricingData.unUsedDecorationCost = cost;
          fieldDecorationCost = cost;
          break;
        } else {
          log("noUsageProcess not found");
        }
      }
    } else {
      //update the decoration cost
      var decorationPrice = 0;
      var decorationCost = 0;
      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())) {
          decorationCost += cView.calculatePrice(colorType, pricingData, percentMarkup);
        } 
      }
      fieldDecorationCost = decorationCost
      decorationPrice = decorationCost * percentMarkup;
      log("decorationCost=" +decorationCost + ", decorationPrice=" + decorationPrice);
      pricingData.decorationPrice = decorationPrice;
      pricingData.decorationCost = decorationCost;
    }
    
    var unitPrice = pricingData.basePriceWithDecoration + pricingData.decorationPrice;
    var unitCost = pricingData.baseCostWithDecoration + pricingData.decorationCost;
    
    
    //get the prices for field choices
    
    var fieldCost = this.fieldPrice(colorType, fieldDecorationCost, pricingData.baseCost, pricingData); //we use costs becuase the markup will be applied to the results
    unitCost += fieldCost;
    var fieldPrice = fieldCost * percentMarkup;
    unitPrice += fieldPrice;
    log("field cost=" + fieldCost + ", fieldPrice=" + fieldPrice);
    //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.price = d.roundPrice(unitPrice);
    this.cost = unitCost;
    log("final rounded price=" + this.price);
    
    
    log(pricingData);
    return this.price;
  },
  
  fieldPrice: function(colorIdx, decorationPrice, basePrice, pricingData) {
    log("fieldPrice for colorIdx=" + colorIdx);
    var fixedMarkups = {};
    var percentMarkups = {};
    var finalMarkups = {};
    //sum defaults for the fields...
   
    
    var fieldPricingData = {};
    
    //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.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
        var fieldDef = pField.fieldDef;
        var cField = this.cFields[fieldDef.id];
        var priceModifierType = fieldDef.priceModifierType;
        if (priceModifierType != PRICE_MODIFY_NONE) {
          var fieldPriceData = {
            type: 4,
            field: cField,
            extras: []
          };
          fieldPricingData[fieldDef.id] = fieldPriceData;
          pricingData.extras.push(fieldPriceData);
          var fieldMarkup = 0.0;
          var fieldQty = 0;
          if(fieldDef.typeOptions.list) {
            var selectedOptions = cField.getSelectedOptions(true);
            var fieldColorIdx = (fieldDef.pricingType == PRICE_TYPE_FLAT) ? 0 : colorIdx;
            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 optPriceData = {
                type: 5,
                option: cChoice,
                qty: null,
                total: choicePrice
              };
              if(cField.multiSelect) {
                fieldMarkup += choicePrice * cChoice.qty;
                fieldQty += cChoice.qty;
                optPriceData.qty = cChoice.qty;
              } else {
                fieldMarkup += choicePrice;
              }
              fieldPriceData.extras.push(optPriceData);
            }
            if(cField.multiSelect) {
              fieldMarkup = fieldMarkup / fieldQty;
            }
          } else if(cField.isUsed()) {
            var priceModifierToUse = pField.usePriceDefaults ? fieldDef.priceModifier : pField.priceDelta;
            var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
            fieldMarkup += parseFloat(choicePrice);
          }
          if(priceModifierType == PRICE_MODIFY_FIXED) {
            fixedMarkups[fieldDef.id] = fieldMarkup;
            log(fieldDef.name + " has a fixed markup of " + fieldMarkup);
          } else {
            percentMarkups[fieldDef.id] = fieldMarkup;
            log(fieldDef.name + " has a percent markup of " + fieldMarkup + "%");
          }
        }
      }
    }
    fixedMarkups[-1] = basePrice;   
    fixedMarkups[-2] = decorationPrice;  

    var totalFieldMarkup = 0.0;
    
    for(var i=0; i < this.product.fields.list.size(); i++) {
      var pField = this.product.fields.list[i];
      if(pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
        var fieldDef = pField.fieldDef;
        var cField = this.cFields[fieldDef.id];
        var priceModifierType = fieldDef.priceModifierType;
        if (priceModifierType != PRICE_MODIFY_NONE) {
          fieldPriceData = fieldPricingData[fieldDef.id];
          var fieldMarkup = 0.0
          if(priceModifierType == PRICE_MODIFY_FIXED) {
             fieldMarkup = fixedMarkups[fieldDef.id];
          } else {
            var thisPercent = percentMarkups[fieldDef.id];
            //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 fixedMarkup = fixedMarkups[percentOfId];
              if(fixedMarkup == null) {
                log("Unable to get fixed markup for field " + percentOfId);
              } else {
                log("Adding " + thisPercent + "% of " + fixedMarkup + " for field " + percentOfId);
                fieldMarkup += (fixedMarkup * thisPercent / 100.0);
              }
            }
          }
          log("Calcing Field Markup, " + fieldDef.name + " adds $" + fieldMarkup);
          fieldPriceData.total = parseFloat(fieldMarkup);
          totalFieldMarkup += fieldPriceData.total;
        }
      }
    }
    return totalFieldMarkup;
  },
  
  getPrice: function CP_getPrice() {
    if(this.cp_type == CP_DIGITIZATION) {
      return this.price;
    } else {
      if(this.price == null) {
        this.getUnitPrice();
      }
      
      this.discount = d.getDiscount(this.qty, this.price, this.cost);
      log("discount=" + this.discount);
      return (this.price * this.qty) - this.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 + "[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()) {
      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.fieldDef.code != "C") { //ignore color field
        var cField = this.cFields[pField.id];
        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) {
    if(this.qty <= 0) {
      alert(ml("You must specify a valid quantity to add this product to the cart"));
      return;
    }
    var self = this;
    
    this.checkForAlerts(false, function(result) {
      if(result) {
        var self = this;
        var aKey = null;
        var cont = null;
        this.product.type.itemSaved();
        
        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",
              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.notifyCartChanged();
                  d.cart.selectCartItem(d.currentCProduct); 
                  processToolTips($(cont));
                }
              }
          });
          
        } else {
          d.track("save-new-product");
          cont = "m_cart_pane";
          
          ajaxQueueManager.queueRequest("save_product/" + this.id, 1, {
              mode: 0,
              url: d.pathPrefix + "/designer/save_product?is_new=1",
              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.cart.selectCartItem(d.currentCProduct);
                  processToolTips($("cart_body"));
                }
              }
          });
        }
        var cartTab = $("m_cart");
        if(cartTab.className == "unselected_tab_hidden") {
          cartTab.className = "unselected_tab";
        }
        d.selectTab('m','cart');
        aKey = asyncStart($(cont));
      }
    }.bind(this));
  },
  
  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";
        }
      }
      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 != null)&&(this.customProduct.productId == this.product.id)) {
      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;
    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) {
    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(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) { 
    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);
    f.cleanEmptyOptions();
    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() {
    for(var k in this.views) {
      var view = this.views[k];
      view.remove();
    }
  },
  
  //restore state from options
  rollBack: function CP_rollBack() {
    //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() {
    this.remove();
    this.initialize(this.id, 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); 
            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);
          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)});
  }
});

//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) {
    var opt = this.options[optionId];
    var subQty = 0;
    if(subOptionId != null) {
      subQty = qty;
      qty = 1;
    }
    if(opt == null) {
      opt = new ConfiguredFieldOption(this, optionId, qty);
      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(opt.def.isMulti) {
      this.multiSelect = true;
      this.multiOption = opt;
    } else if(subOptionId != null) {
      opt.addSelectedOption(subOptionId, subQty);
    }
    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.def.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, valueType) {
    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, valueType);
      }
      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);
    }
    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...
  },
  
  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 
    }
  },
  
  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, valueType) {
    
    var ids = this.extractOptIds(value);
    var optionId = ids[0];
    var subOptionId = ids[1];
    
    var oldValue = this.getMainOption(true);

    this.setSelectedOption(optionId, 1, subOptionId);
    
    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.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 = {};
          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);
              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);
              }
            }
          }
        }
      } 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() {
    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.fieldValue == null) {
      return false;
    }
    return true;
  },
  
  refreshHtml: function() {
    this.fieldDef.setField(this.configuredProduct);
  }
});


var ConfiguredFieldOption = Class.create({
  CLASSDEF: {
      name: 'ConfiguredFieldOption'
  },
  
  initialize: function CPFO_initialize(field, optionId, qty) {
    this.field = field;
    this.optionId = optionId;
    this.qty = qty;
    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){
            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) {
    var opt = this.subOptions[subOptionId];
    if(opt == null) {
      opt = new ConfiguredFieldSubOption(this, subOptionId, qty);
      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;
    }
    return opt;
  },
  
  serialize: function CPFO_serialize(queryComponents, prefix) {
    queryComponents.push(encodeURIComponent(prefix + "[q]") + "=" + encodeURIComponent(this.qty));
    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);
  }
    
});

var ConfiguredFieldSubOption = Class.create({
  CLASSDEF: {
      name: 'ConfiguredFieldSubOption'
  },
  
  initialize: function CPFSO_initialize(option, subOptionId, qty) {
    this.option = option;
    this.field = option.field;
    this.subOptionId = subOptionId;
    this.qty = qty;
    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));
  }
});


//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 = this.renderVersion;
    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);
          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_checkBgImage() {
    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.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) {
    
    //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 );
    }
    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, this.getDesignerBackgroundURL());
      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() {
    for(var k in this.areas) {
      var area = this.areas[k];
      if(area.isUsed()) {
        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) {
    var viewTotal = 0;
    var viewPriceData = {
      type: 1,
      view: this,
      extras: []
    };
    for(var j=0; j < this.productView.areas.list.size(); j++) {
      var pArea =  this.productView.areas.list[j];
      var cArea = this.areas[pArea.id];
      if((cArea != null) && (cArea.isUsed())) {
        viewTotal += cArea.calculatePrice(colorType, priceData, viewPriceData, percentMarkup);
      }
    }
    if(viewTotal > 0) {
      viewPriceData.total = viewTotal * percentMarkup;
      priceData.extras.push(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;
  },
  
  //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);
        }
        cProcess.addItem(item);
      }
    }  
  },
  
  isUsed: function CPVA_isUsed() {
    return (this.visibleItemCount > 0);
  },
  
  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.backgroundColor = configuration.bgColor;
    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);
    }
    
  },
  
  //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) {
    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");
    }
    return this.insertNewItem(newItem);
  },
  
  insertNewItem: function CPVA_insertNewItem(newItem) {
    var wasUsing = newItem.cViewProcess.isUsed();
    newItem.cViewProcess.addNewItem(newItem);
    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.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")) {
        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.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);
    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) {
    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) {
    var areaTotal = 0;
    var areaPriceData = {
      type: 2,
      area: this,
      extras: []
    };
    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()) {
        areaTotal += cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup);
      }
    }
    if(areaTotal > 0) {
      areaPriceData.total = areaTotal * percentMarkup;
      priceData.extras.push(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;
  }
      
});

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;
    
  },
  
  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();
  },
  
  isUsed: function CPVAP_isUsed() {
    return (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) {
    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.length;i++) {
      this.items[i].remove();
    }
    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) {
    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 total this views costs to decorate
  calculatePrice: function CPVAP_calculatePrice(colorType, fullPriceData, priceData, percentMarkup) {
    
    var processPriceData = {
      type: 3,
      process: this,
      priceType: 0,
      extras: []
    };
    var price = 0.0;
    var stitchCount = 0;
    var incompleteStitchCount = false; //do we have a stitch count for all the items?
    var digitizationFees = 0;
    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;
            }
          }
          //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;
        }
      }
      
      
    }
    
    if(this.productProcess.productTypeProcess.priceMode == 0) { //lwd
      price = this.productProcess.prices[colorType];
      processPriceData.priceType = colorType;
      processPriceData.priceDelta = this.productProcess.prices[colorType] - this.productProcess.prices[0];
      
    } else if(this.productProcess.productTypeProcess.priceMode == 1) { //single price
      price = this.productProcess.prices[0];
      processPriceData.priceType = -1;
      processPriceData.priceDelta = 0;
    } else { //lookup tables
      processPriceData.priceType = -2;
      
      var priceTable = this.productProcess.priceTable;
      var embPriceData = priceTable.calcPrice(stitchCount, this.configuredViewArea.configuredProduct.qty);
      
      price = embPriceData[0];
      processPriceData.threadCountLower = embPriceData[1];
      processPriceData.threadCountUpper = embPriceData[2];
      processPriceData.priceDelta = 0;
      
      
      log(priceData);
      //TODO: get digitisation costs
    }
    if(digitizationFees>0) {
        var digitizationPriceData = {
          type: 4,
          process: this,
          total: digitizationFees * percentMarkup,
          extras: []
        };
        processPriceData.extras.push(digitizationPriceData);
        //split the price accross the qty to give an item cost...
        itemDigPrice = digitizationFees / this.configuredViewArea.configuredProduct.qty;
        price += itemDigPrice;
    }
    
    if(fullPriceData.unUsedDecorationCost > 0) {
      if(fullPriceData.unUsedDecorationCost > price) {
        fullPriceData.unUsedDecorationCost -= price;
        price = 0;
      } else {
        price -= fullPriceData.unUsedDecorationCost;
        fullPriceData.unUsedDecorationCost = 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.decorationLibraryId != 0) {
        log('trying to calculate cost from process');
        var itemcost = item.asset.itemCost();
        log("item cost " + itemcost);
        price = price + itemcost;
        log('price is now ' + price);
      }
    };
    
    if(price > 0) {
      processPriceData.total = price * percentMarkup;
      priceData.extras.push(processPriceData);
    }
    return price;
  }
});

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.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.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.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);
    
    
    var pc = options.c;
    for(var i=0;i<pc.length;i++) {
      var l = new ProductColor(this, pc[i]);
      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;
    }
    return this.fields.byId[this.type.sizeField.id];
  },
  
  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.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];
        }
      }
    }
    
  },
  
  //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++) {
      this.areas.add(new ProductViewArea(this, options.a[i], i));
    }
  },
  
  //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) {
    if(!this.allowView) {
      return "";
    }
    var url = null;
    if(configuredView != null) {
      url = configuredView.getViewURL(11, true, false);
    } 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();
  },
  
  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);
    
    this.prices = options.d;
    
    if(this.productTypeProcess.priceMode==2) {//price table
      this.priceTable = d.priceTables[options.pt];
      if(this.priceTable == null) {
        log("ERROR: unable to get price table '" + options.pt + "'");
      }
    }
  },
  
  //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) {
    if(this.productTypeProcess.priceMode == 0) {
      return this.prices[colorType];
    } else if(this.productTypeProcess.priceMode == 1) {
      return this.prices[0];
    } else {
      //get the price of the default stitch count....
      if(this.priceTable == null) {
        log("ERROR: Unable to get price of default stitch count: priceTable is null");
      } else {
        log("using price table to get price of default stitch count (" + this.productTypeProcess.defaultStitchCount + ")");
        return this.priceTable.calcPrice(this.productTypeProcess.defaultStitchCount, d.currentCProduct.qty)[0];
      }
      return 0;
    }
  },
  
  canModifyDesign: function() {
    if(d.mode == DESIGNER_MODE_AMEND) {
      if(this.productTypeProcess.priceMode == 2) { //price table
        return false;
      }
    }
    return true;
  }
});

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.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;
  }
});


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.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.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.fieldsByCode = {};
    this.fieldsByName = {};
    this.colorField = null;
    this.sizeField = null;
    this.customFields = new MapList(this);
    
    this.minQty = options.mq;
    this.bundleSize = options.bs;
    
    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) {
    //if(this.noCategores != true) {
      this.cats = [];
      this.catsById = new Hash();
      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;
  },
  
  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;
      }
    }
    
    /*
    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);
      }
    }
  },
  
  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.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];
          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();
    }
  },
  
  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.discount;
    var qty = d.currentCProduct.qty;
    //$("qty").value = qty; (this was stopping user from backspacing the qty before changing it)
    $('price').innerHTML = d.formatPrice(price, "price_currency_code");
    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") ;
    }
    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.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 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 (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) {
    
    if(this.typeOptions.list) {
      return this.renderListHtml(cProduct, pField, cField);
    } 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() + '.setValue(this.value);"' + 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() + '.setValue(this.value);">' + 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() + '.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() + '.setValue(this.value);" 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() + '.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() + '.setValue(this.value);" 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() + '.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() + '.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(), cField);
        if(includeTime) {
          str += this.renderTimeInputs(timeFormat, curDate, cField.objectRef(), cField);
        }
        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(), cField);
    }
  },
  
  renderDateInputs: function PTF_renderDateDropDowns(format, date, objectRef, cField) {
    var dateFieldOrder = {
      0: ["m","/", "d", "/", "y"],
      1: ["d","/", "m", "/", "y"],
      2: ["sm"," ", "d", ", ", "y"],
      3: ["lm"," ", "d", ", ", "y"]
    };
    var fields = dateFieldOrder[format];
    var str = "";
    
    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);">' + 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);" 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);">' + 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);">' + 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);">' + 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) {
    var str = "";
    var selVal = null;
    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);" 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);" value="' + selVal + '"/>';
    }
    selVal = (date == null)? "" : date.getMinutes();
    str += '<input ' + cField.iId("date_4", true) + ' type="text" size="2" onblur="' + objectRef + '.setValue(this.value, 4);" value="' + selVal + '"/>';
    if(format == 0) { //ampm
      selVal = (date==null)? '' : date.getAMPM();
      str += '<select ' + cField.iId("date_5", true) + ' onchange="' + objectRef + '.setValue(this.value, 5);">' + 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) {
    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);
    }
    if(this.fieldType == FIELD_TYPE_LIST_CHECKBOX || this.fieldType ==FIELD_TYPE_LIST_RADIO) {
      html = this.renderCheckRadio(cProduct, pField, cField);
    }
    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) {
    if((sizeColorCombinations != null)&&(selectedColor != null)&&(!ptOpt.isMulti)) {
      var filter = sizeColorCombinations[ptOpt.id];
      if(filter != null) {
        if(filter[selectedColor.productChosenOptionId] != true) {
          log("Filtering out " + ptOpt.name);
          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) {
    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);
    
    var selOpt = cField.getMainOption(true);
      
    var optionHtml = selectOptionHtml(opts, selOpt.getId());
    
       
       
       
    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() + '.setValue(' + selCode + ');" ' +  extraSelOpts + disCode + '>' + optionHtml + '</select>';
  },
  
  getOptionList: function PTF_getOptionList(cProduct, pField, cField, sizeColorCombinations, includeMulti) {
    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(!this.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt)) {
          delete cField.options[ptOpt.id]; //just make sure its not selected..
          continue;
        }
        if(pOpt.subs != null) {
          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) {
              subOpts.push([ptSubOpt.name,ptOpt.id + '-' + ptSubOpt.id]);
            }
          }
          if(subOpts.length > 0) {
            opts.push({ c:ptOpt.name, v:subOpts});
          }
        } else if(!ptOpt.isMulti || includeMulti) {
          
          opts.push([ptOpt.name,pOpt.id]);
        }
      }
    }
    return opts;
  },
  
  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(!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 = [];
          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 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 {
          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) {
    log("renderCheckRadio");
    var disCode = (d.mode == DESIGNER_MODE_AMEND && this.priceModifierType != PRICE_MODIFY_NONE) ? ' disabled="true"' : '';
    var oRef = cField.objectRef();
    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.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;
  }
});

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.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;
  },
  
  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;
  },
  
  calcPrice: function(rowValue, colValue) {
		log("PriceTable.calcPrice:" + rowValue + "," + colValue);
		var rowIndex = -1; 
		var colIndex = -1;
		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;
			}
		}
		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;
		}
		if(rowIndex == -1) { //we are above largest defined range... use repeater...
			log(this.rowRepeater);
			
			var price = (this.rows.length == 0) ? 0 : this.prices[colIndex][this.rows.length-1];
			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];
			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=" + this.prices[colIndex][rowIndex] );
			return [this.prices[colIndex][rowIndex], this.rows[rowIndex][0], this.rows[rowIndex][1]];
		}
	}
});

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.products[i].id + '"><img src="' + this.products[i].u + '" id="psi_' + this.products[i].id + '" alt="' + this.products[i].n + '" width="50" height="50" 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.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_' + 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;
  },
  
  loadSubCats: function(selectedCatId) {
    
  }, 
  
  selectSubCat: function(el, id) {
   
  },
  
  bindSubCat: function(el, cat) {
   
  }
  
});
