///////////////////////////////////////////////////////////////
//  Big.js
//  v0.9.0 (beta)
///////////////////////////////////////////////////////////////
//  http://github.com/whatgoodisaroad/Big-js
//  By Wyatt Allen
//  MIT Licensed
//  2011-09-19 (Monday, 19 September 2011)
///////////////////////////////////////////////////////////////
var Big;
(function(){function p(a,b){a=a.clone();b=b.clone();if(a.sign!=b.sign)return a.sign==k?n:g;else if(a.exponent!=b.exponent)return a.sign==k?a.exponent>b.exponent?n:g:a.exponent>b.exponent?g:n;else{var c=r(a,b);return a.sign==k?u(c.l.mantissa,c.r.mantissa):u(c.r.mantissa,c.l.mantissa)}}function u(a,b){a=a.slice();b=b.slice();if(b.length){if(!a.length)return h(b)?o:g}else return h(a)?o:n;if(a[0]>b[0])return n;else if(a[0]<b[0])return g;return u(a.splice(1),b.splice(1))}function h(a){return/^0*$/.test(a.join(""))}function v(a){return B(a.join("").replace(/0+$/,
""))}function w(a,b){return b.length<a?b.concat(s(a-b.length,0)):b}function s(a,b){if(a<=0)return[];for(var c=Array(a),d=0;d<a;++d)c[d]=b;return c}function E(a,b){for(var c=Array(b.length),d=0;d<b.length;++d)c[d]=a(b[d]);return c}function B(a){return E(function(a){return parseInt(a,10)},a.split(""))}function m(a){if(h(a.mantissa))return x;var b;b=(b=a.mantissa.join("").match(/^(0+)[1-9]/))?b[1].length:0;var c;c=(c=a.mantissa.join("").match(/(0+)$/))?c[1].length:0;return a.mantissa.length?b?new Big(a.sign,
a.exponent-b,v(b<1?a.mantissa.slice():a.mantissa.slice(b))):c?new Big(a.sign,a.exponent,v(a.mantissa)):a:new Big(a.sign,a.exponent,[])}function r(a,b){return a.exponent==b.exponent?{l:a,r:b}:a.exponent<b.exponent?{l:new Big(a.sign,b.exponent,a.mantissa.length<b.exponent-a.exponent+a.mantissa.length?s(b.exponent-a.exponent+a.mantissa.length-a.mantissa.length,0).concat(a.mantissa):a.mantissa),r:b}:{l:a,r:new Big(b.sign,a.exponent,b.mantissa.length<a.exponent-b.exponent+b.mantissa.length?s(a.exponent-
b.exponent+b.mantissa.length-b.mantissa.length,0).concat(b.mantissa):b.mantissa)}}function y(a,b){var c=a.mantissa.length,d=b.mantissa.length;return c==d?{l:a,r:b}:c<d?{l:new Big(a.sign,a.exponent,w(d,a.mantissa)),r:b}:{l:a,r:new Big(b.sign,b.exponent,w(c,b.mantissa))}}function l(a){return new Big(!a.sign,a.exponent,a.mantissa)}function z(a,b){var c=r(a,b),c=y(c.l,c.r);if(a.sign==b.sign){var d=Big,f=a.sign,e=c.l.exponent+1,i=c.l.mantissa,c=c.r.mantissa,g=Array(i.length+1),j;j=0;for(var h=i.length-
1;h>=0;--h)j=j+i[h]+c[h],g[h+1]=j%10,j=j>9?1:0;g[0]=j;return new d(f,e,g)}else return u(c.l.mantissa,c.r.mantissa)==o?x:a.sign==k?t(c.l,l(c.r)):t(c.r,l(c.l))}function t(a,b){a=m(a);b=m(b);if(a.isZero())return l(a);if(b.isZero())return a.clone();var c=p(a,b);if(c==o)return x;if(a.sign==b.sign){if(a.sign==q)return t(l(b),l(a));if(c==g)return l(t(b,a));c=r(a,b);c=y(c.l,c.r);return new Big(a.sign,c.l.exponent,A(c.l.mantissa,c.r.mantissa))}else return a.sign==k?z(a,l(b)):l(z(l(a),b))}function A(a,b){if(a.length==
0)return[];var c=a.slice(0,a.length-1),d=b.slice(0,b.length-1),f=a[a.length-1],e=b[b.length-1];return f<e?A(C(c),d).concat([f+10-e]):A(c,d).concat([f-e])}function C(a){return a[a.length-1]==0?C(a.slice(0,a.length-1)).concat([9]):a.slice(0,a.length-1).concat([a[a.length-1]-1])}function D(a,b,c,d,f){if(d>=Big.precision)return f.DIVISION_ROUNDOFF=!0,f.DIVISION_REMAINDER=c.toString(),[];if(!a.length&&h(c.mantissa))return f.DIVISION_ROUNDOFF=!1,[];var e;if(a.length){var i={x:a[0],xs:a.slice(1)},a=i.x;
e=i.xs}else a=0,e=[];for(var g,j,i=9;i>=0;--i)if(j=b.times(new Big(i)),i==0||j.lessThanOrEqualTo(c))break;g={q:i,prod_bn:j};j=g.q;i=D;c=c.minus(g.prod_bn);c=c.times(new Big(10)).plus(new Big(a));b=i(e,b,c,d==0&&j==0?0:d+1,f);return[j].concat(b)}var k=!1,q=!0,n=0,g=1,o=2;Big=function(a,b,c,d){typeof a=="number"&&(a=""+a);if(a.substring)return a=a.replace(/(^\s+)|(\s+$)/g,""),b=/^[+-]/.test(a),c=/^[+-]?\d*\.\d*$/.test(a),d=b?a.slice(1):a,m(new Big(b&&a[0]=="-"?q:k,c?d.indexOf(".")-1:d.length-1,B(d.replace(".",
""))));else this.sign=a,this.exponent=b,this.mantissa=c,this.flags=d||{},this.expr=this.toString()};Big.POSITIVE=k;Big.NEGATIVE=q;Big.parse=function(a){return new Big(a)};Big.precision=20;Big.prototype.toString=function(){if(h(this.mantissa))return"0";var a=m(this),b=(this.sign==q?"-":"")+(a.exponent<0?"0":w(a.exponent+1,a.mantissa.slice(0,a.exponent+1)).join(""));a=a.exponent>=0?a.mantissa.length==a.exponent?"":"."+v(a.exponent+1<1?a.mantissa.slice():a.mantissa.slice(a.exponent+1)).join(""):"."+
s(-a.exponent-1,"0").join("")+a.mantissa.join("");return b+a};Big.prototype.lessThan=function(a){return p(this,a)==g};Big.prototype.greaterThan=function(a){return p(this,a)==n};Big.prototype.equals=function(a){return p(this,a)==o};Big.prototype.lessThanOrEqualTo=function(a){a=p(this,a);return a==g||a==o};Big.prototype.greaterThanOrEqualTo=function(a){a=p(this,a);return a==n||a==o};Big.prototype.plus=function(a){return m(z(this,a))};Big.prototype.minus=function(a){return m(t(this,a))};Big.prototype.times=
function(a){var b;b=r(this,a);b=y(b.l,b.r);for(var c=b.l.mantissa.length,d=s(2*c,0),f=c-1;f>=0;--f)for(var e=c-1;e>=0;--e)d[f+e+1]+=b.r.mantissa[f]*b.l.mantissa[e];c=d.slice();for(f=c.length-1;f>0;--f)e=c[f],e>9&&(d=e%10,e=(e-d)/10,c[f-1]+=e,c[f]=d);a=new Big(this.sign==a.sign?k:q,2*b.l.exponent+1,c);return m(a)};Big.prototype.over=function(a){var b,c;b=r(this,a);var d;d=b.l.mantissa.length-b.r.mantissa.length>0?new Big(Math.pow(10,b.l.mantissa.length-b.l.exponent-1)):new Big(Math.pow(10,b.r.mantissa.length-
b.r.exponent-1));c={l:b.l.times(d),r:b.r.times(d)};b={};d=Big;var a=this.sign==a.sign?k:q,f=c.l.exponent;var e=c.l;c=c.r;if(h(e.mantissa))c=[];else if(h(c.mantissa))throw"Division by zero";else e={x:e.mantissa[0],xs:e.mantissa.slice(1)},c=D(e.xs,new Big(k,c.exponent,c.mantissa),new Big(e.x),0,b);b=new d(a,f,c,b);return m(b)};Big.prototype.isZero=function(){return h(this.mantissa)};Big.prototype.clone=function(){return new Big(this.sign,this.exponent,this.mantissa.slice())};Big.prototype.negate=function(){var a=
this.clone();a.sign=!a.sign;return a};var x=new Big(k,0,[])})();

var cDisc = 0.0;
var cCustomerId='';
var cCustomerEdited = false;

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

var ignoreDirtyProducts = false; //used by OMS to stop any asking save dirty

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 ;
  if(typeof Big == 'function') {
    //use bigdecimal library to stop rounding errors
    var placeModifier = new Big(Math.pow(10,places));
    var bigThis = new Big("" + this);
    var bigUp = bigThis.times(placeModifier);
    return Math.round(parseFloat(bigUp.toString()))/Math.pow(10,places);
  } else {
    return Math.round(this*Math.pow(10,places))/Math.pow(10,places) ;
  }
} ;


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

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

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

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

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

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

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

var debug = null;

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

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

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


function backgroundClicked(event) {
  if (typeof(orderManager) != 'undefined' && !orderManager.isDesignerShown()) return true; 
  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 = {};

function asyncStart(container, zIndex) {
  var containerEl = $(container);
  if (!containerEl || containerEl.visible() == false) return;
  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});
  }
  
  if(zIndex == null) zIndex = 3000;
  
  var divB = document.createElement("DIV");
  divB.style.display = "none";
  divB.style.zIndex = zIndex;
  divB.className = "async_action_background";
  
  var div = document.createElement("DIV");
  div.style.display = "none";
  div.style.zIndex = zIndex;
  div.className = "async_action";
  
  div.innerHTML="<img src='/images/spinner_no_bg.gif' alt='[]' />";
  var oldPositioning = null;
  

  var dims = Element.getDimensions(container);
  var offset = Position.cumulativeOffset(containerEl);
  log(offset);
  log(dims);
  
  divB.style.position = "absolute";
  divB.style.top = offset[1] + "px";
  divB.style.left = offset[0] + "px";
  divB.style.height = dims.height + "px";
  divB.style.width = dims.width + "px";
  divB.style.display="none";
  document.body.appendChild(divB);
  divB.id = "aa_" + containerId + "_bg";
  
  div.style.position = "absolute";
  div.style.top = offset[1] + "px";
  div.style.left = offset[0] + "px";
  div.style.height = dims.height + "px";
  div.style.width = dims.width + "px";
  div.backgroundColor="#000000";
  div.style.display="none";
  document.body.appendChild(div);
  //Effect.Appear(div, { duration: 0.2 });
  
  div.id = "aa_" + containerId;
  
  $(divB).show();
  $(div).show();
  var v = getNextId();
  asyncActions[containerId] = {d:div, db:divB, c:container, p:oldPositioning, v:v, a: new Date().getTime()};
  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 {
    //check that the container is still in the document and that it is the same element as the async action...
    var docEl = $(containerId);
    var forceFinish = false;
    if((docEl == null) || (docEl != aa.c)) {
      if(docEl == null) {
        log("container has been removed, stoping async action");
      } else {
        log("container element has changed, stoping async action");  
      }
      forceFinish = true;
    }
    if(!forceFinish && (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) {}
    }
  }
}

function checkAsyncActions() {
  var toFinish = [];
  for(var k in asyncActions) {
    var now = new Date().getTime();
    var aa = asyncActions[k];
    if(now - aa.a > 3000) { //only check actions that have been around longer than 3 seconds
      var docEl = $(k); 
      if((docEl == null) || (docEl != aa.c)) {
        log("checkAsyncActions found invalid action container:" + k);
        toFinish.push({id:k, v:aa.v});
      }
    }
  }
  for(var i=0; i < toFinish.length; i++) {
    asyncFinish(toFinish[i]);  
  }
  window.setTimeout( function() { checkAsyncActions(); }, 1000);
}

checkAsyncActions();


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

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

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

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

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

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


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


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


var hiddenSelects = [];

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

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

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

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


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

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

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

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

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

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

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

  

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

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


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




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


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

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

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

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

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




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

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


var msgBoxCallback = null;
var msgboxId = null;

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

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


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

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

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


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

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

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

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


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

var DESIGNER_SUBMODE_DEFAULT = 0;
var DESIGNER_SUBMODE_VIEW = 1;
var DESIGNER_SUBMODE_QUOTE = 2;

var TEMPLATE_MODE_DESIGN = 1;
var TEMPLATE_MODE_CP = 2;
var TEMPLATE_MODE_BLANK = 3;


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

var DIG_EVENT_ALL = DIG_EVENT_COLOR + DIG_EVENT_PRIMARY + DIG_EVENT_STARTUP;

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

var ORDER_CACHE_COUNT = 10;

var ORDER_INTERNET = 0;
var ORDER_QUOTE = 2;
var ORDER_OMS = 3;
var ORDER_KIOSK = 4;
var ORDER_KIOSK_INCOMPLETE = 5;

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

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

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

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

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

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

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

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

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

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

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

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

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

var ORDER_PROGRESS = {
  '1' : ml("Added To Cart"),
  '2' : ml("Cart View"),
  '3' : ml("Address"),
  '4' : ml("Address Updated"),
  '5' : ml("Shipping"),
  '6' : ml("Billing"),
  '7' : ml("Billing Updated"),
  '8' : ml("Confirmation"),
  '9' : ml("Confirmed")
};

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

var AMOUNT_TYPE_PERCENT = 1;
var AMOUNT_TYPE_FIXED = 2;

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

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

var MODE_EDIT = 0;
var MODE_VIEW = 1;

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

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

var OMS_STATUS_LOADING = 0;
var OMS_STATUS_LIST = 1;
var OMS_STATUS_ORDER = 2;
var OMS_STATUS_DASHBOARD = 3;
var OMS_STATUS_NEWSFEED = 4;
var OMS_STATUS_ABANDON_CARTS = 5;
var OMS_STATUS_MANUAL_PAYMENTS = 6;
var OMS_STATUS_CUSTOMERS = 7;
var OMS_STATUS_ARTWORKS = 8;
var OMS_STATUS_RECORD_PAYMENTS = 9;

var OMS_STATUS_BY_CODE = {
  dashboard: OMS_STATUS_DASHBOARD,
  newsfeed: OMS_STATUS_NEWSFEED,
  abandon_carts: OMS_STATUS_ABANDON_CARTS,
  manual_payments: OMS_STATUS_MANUAL_PAYMENTS,
  artworks: OMS_STATUS_ARTWORKS
};

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


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

var Designer = Class.create({
  CLASSDEF: {
      name: 'Designer'
  },
  
  initialize: function D_initialize(pathPrefix, designMode, inPopup, curGlyph, curCode, priceRounding, price99, useAnalytics, cMod, tMod, declibMarkup, lengthUnit, priceFormat, commissionRate, appLicMode) {
    this.selected_tabs = {"d":"layout","m":"customize","p":"mens", "i":"user"};
    this.cart = new Cart();
    this.decorationLibraryManager = new DecorationLibraryManager(this);
    this.teamNameTemplates = new TeamNameTemplates();
    this.chargeDecorationLibraries = {};
    this.assets = {};
    this.designerOptions = new DesignerOptions(DFLAG_DEFAULTS);
    this.appLicMode = appLicMode;
    this.debugAjax = 0;
    
    this.currentCProduct = null;
    this.productTypes = new Hash();
    this.currentProductType = null;
    this.nextId = 1;
    this.nextItemId = 1;
    this.pathPrefix = pathPrefix;
    this.sizer = null;
    this.rotator = null;
    this.productsById = new Hash();
    this.inPopup = inPopup;
    this.extraExternalParams = "";
    this.hideQualityWarning = false;
    this.useAnalytics = useAnalytics;
    this.showGrid = true;
    this.autoCheckout = true;
    this.extraCallbackParams = "";
    
    this.defaultMarkupType = 0;
    this.defaultMarkupAmount = 20;
    
    this.discounts = {};
    this.discountNames = {};
    this.defaultDiscountId = null;
    this.affiliateDiscounting = true;
    this.declibMarkup = declibMarkup;
    this.commissionRate = commissionRate;
    this.aMarkupRate = commissionRate / (100.0 - commissionRate);
    
    this.lengthUnit = lengthUnit;
    
    
    this.priceRounding = priceRounding;
    this.price99 = price99;
    this.priceFormat = priceFormat;
    
    this.imagePopupLoaded = false;
    
    this.allowForms = true;
    
    this.defaultQtyDisabled = false;
    this.subMode = DESIGNER_SUBMODE_DEFAULT;
    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 if(designMode=="template") {
      this.mode = DESIGNER_MODE_TEMPLATE;
      this.templateMode = TEMPLATE_MODE_DESIGN;
    } else {
      this.mode = DESIGNER_MODE_VIEW_CUSTOM_PRODUCT;
      if(designMode == "quote") {
        this.subMode = DESIGNER_SUBMODE_QUOTE;
      } else {
        this.subMode = DESIGNER_SUBMODE_VIEW;
      }
    }
    
    this.allowPersonalization = true;
    
    this.currentCanvasType = 0; //LAYOUT
    this.zoom = 1; //400x400 (LAYOUT)
    
    var re = new RegExp("\\[PATH_PREFIX\\]", "g");
  
    for(var i=0; i < types.length; i++) {
      types[i] = types[i].replace(re,pathPrefix);
    }
    
    type_min = type_min.replace(re,pathPrefix);
    
    this.layoutViewUrlCID = new RegExp("\\[CID\\]", "g");
    this.layoutViewUrlS = new RegExp("\\[S\\]", "g");
    this.layoutViewUrlSc = new RegExp("\\[SC\\]", "g");
    
    this.allowedItemTypes = null;
    
    if (typeof(orderManager) == "undefined") {
      Ajax.Responders.register({
        onException: function D_onException(aj, ex) {
          try {
            log("Exception Occured\nError name: " + ex.name + "\nError message: " + ex.message, true);
            alert("Exception Occured\nError name: " + ex.name + "\nError message: " + ex.message);
            log(ex); //this was commented out.. why?
          } catch(e) {}
          throw ex;
        }
      });
    }
    
    
    
    this.onKeyUpEvent = this.onKeyUp.bindAsEventListener(this);
    Event.observe(document, "keyup", this.onKeyUpEvent);
    
    Event.observe(document.body, "mousedown", backgroundClicked);
    
    this.onQtyDDBgEvent = this.onQtyDDBg.bindAsEventListener(this);
    
    this.onCopyBgEvent = this.onCopyBg.bindAsEventListener(this);
    
    this.showEffectPreview = true;
    
    this.effectIconsLoaded = false;
    this.borderIconsLoaded = false;
    this.preloadEffectIndex = -1;
    this.preloadBorderIndex = -1;
    
    //currency information
    this.curGlyph = (curGlyph==null) ? "$" : curGlyph; 
    this.curCode = (curCode==null) ? "USD" : curCode;
    //currency modifier
    if(cMod==null) {
      this.cMod = 1;
    } else {
      this.cMod = cMod;
    }
    //tax modifier
    if(tMod==null) {
      this.tMod = 1;
    } else {
      this.tMod = tMod;
    }
    
    
    if(pwCurCur!=null) {
      this.curGlyph = pwCurCur[2];
      this.curCode = pwCurCur[1];
      this.cMod = pwCurModifier;
      var self = this;
      pwCurRegisterArea(0, null, null, null, null, null, function(pwCurCur, pwCurBCur, pwCurModifier) {
          self.curGlyph = pwCurCur[2];
          self.curCode = pwCurCur[1];
          self.cMod = pwCurModifier;
          //redisplay prices here..
          self.cart.updateCartPrice(true);
          self.currentCProduct.updateCartPrice(false);
          self.currentProductType.updatePrice();
          d.cart.products.each(function(product){
            for (var viewId in product.views) {
              for (var areaId in product.views[viewId].areas) {
                for (var itemId in product.views[viewId].areas[areaId].allItems) {
                  var item = product.views[viewId].areas[areaId].allItems[itemId];
                  if(item.updateDigitizationNotice) item.updateDigitizationNotice(true, true);
                }
              }
            }
          });
      });
    } else {
      log("pwCurCur is null");
    }
    
    
    this.defaultProcess = null;
    this.defaultProcessId = null;
    
    this.priceTables = {};
    if(priceTableData != null) {
      for(var i=0; i < priceTableData.length; i++) {
        var pt = new PriceTable(priceTableData[i]);
        this.priceTables[pt.id] = pt;
      }
    }
    
    this.customProducts = {};
    
    this.contentTopEl = $("top_of_content");
    this.translateParams = null;
    this.useLegacyPrices = false;
    this.legacyPriceChanges = {};
    this.inOrderManager = false;
    this.doPriceRounding = true;
    this.nonEmbFormats = ["jpg", "jpeg", "png" , "gif", "svg", "eps"];
  },
  
  configureForProductSelector: function D_configureForProductSelector(options, withoutBlankProduct) {
    this.brandMarkups = {};
    /*
    this.inOrderManager = true;
    //this.setManagePaneContainer($("manage_pane_container"));
    //this.setDesignElements($('sizer'), $('d_pane'));
    //this.bindElements();
    this.defaultMarkupType = options.markup_mode;
    this.defaultMarkupAmount = options.markup_amount;
    this.brandMarkups = options.brand_markups;
    
    this.wsRate = 1.0 + (options.wholesale_rate / 100.0);
    //this.setAffiliateDiscounting(true);
    var self = this;
    options.discount_structures.each(function(dc) {
        self.setDiscountStructure(dc.id, dc.discounts, false, dc.name);
    });
    
    log(Object.toJSON(options.declib_prices));
    if (options.declib_prices) {
      options.declib_prices.each(function(dp) {
          self.addChargeDecorationLibrary(dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7]);
      });
    }
    */
    var self = this;
    //log(Object.toJSON(options.processes));
    if (options.processes) {
      options.processes.each(function(p) {
          processes.add(new Process(p));
      });
    }
    
    var firstPtid;
    //log(Object.toJSON(options.product_types.product_type));
    if (options.product_types) {
      for (var ptid in options.product_types.product_type) {
        log('Adding product type ' + ptid);
        if (!firstPtid) firstPtid = ptid;
        var pt = this.addProductType(ptid, options.product_types.product_type[ptid]);
        //log(Object.toJSON(options.product_types.product_type_processes[ptid]));
        if (options.product_types.product_type_processes[ptid]) {
          options.product_types.product_type_processes[ptid].each(function(p) {
              pt.addProcess(p);
          });
        }
        for (var mixable in options.product_types.mixed_processes[ptid]) {
          pt.addProcessMix(mixable[0], mixable[1]);
        }
        //log(Object.toJSON(options.product_types.categories[ptid]));
        if (options.product_types.categories[ptid]) {
          pt.addCategories(options.product_types.categories[ptid]);
        } else {
          pt.addCategories([]);
        }
      }
    }
    this.setDefaultProcess(-1);
    /*
    this.checkAddAvailableOptions();
    
    if (firstPtid) {
      this.initProductTypeConfiguration();
      this.selectCurrentProductType(firstPtid, false);
      
      var pt = this.productTypes[firstPtid];
      pt.bindElements();
    }
    */
    
    if (!withoutBlankProduct) {
      var cp = new ConfiguredProduct(-1);
      this.cart.add(cp);
      this.currentCProduct = cp;
    }
  },
  
  //if the designer has to appear within a form, we need to make any dynamic forms get generated outside the parent form or they will not work
  //needed for file/image custom fields
  setFormContainer: function(container) {
    this.allowForms = false;
    this.formContainer = container;
  },
  
  initProductTypeConfiguration: function D_initProductTypeConfiguration() {
    var html;
    
    // generate product type selector
    var productTypeSelector = $("product_type_selector");
    if (!$("sel_prod_type") && productTypeSelector) {
      html = '<select onchange="d.changeProductType(this);" id="sel_prod_type">';
      
      var self = this;
      $H(this.productTypes).each(function(pair){
          if(!pair.value.hidden) {
            var ptid = pair.key;
            
            var selHtml = "";
            if (!self.currentProductType) {
              self.currentProductType = self.productTypes[pair.key];
              selHtml = 'selected="selected"';
            }
            html += '<option '+ selHtml +' value="' + pair.key + '">' + pair.value.name + '</option>';
          }
      });
      
      html += '</select>';
      
      new Insertion.Bottom(productTypeSelector, html);
    }
    
    // generate product category containers
    var productCategoryContainer = $("product_category_container");
    if (productCategoryContainer) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        if (productType.cats && productType.cats.length > 0) {
          var styleHtml = 'style="display: none;"';
          if (self.currentProductType == productType) styleHtml = '';
          html = '<span id="category_selector_' + pair.key + '" ' + styleHtml + '>' +
                    '<label>' + ml("Select Category") + '</label>' +
                    '<select id="ddpc_' + pair.key + '">' +
                      '<option selected="selected" value="0">' + ml("All") + '</option>';
          productType.cats.each(function(cat) {
            html += '<option value="' + cat.id + '">' + cat.name + '</option>';
          });
          html += '</select></span>';
          new Insertion.Bottom(productCategoryContainer, html);
        }
      });
    }
    
    var productConfig = $("product_config");
    if (productConfig) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        var styleHtml = 'style="display: none;"';
        if (self.currentProductType == productType) styleHtml = '';
        html = '<div id="pt_' + pair.key + '" ' + styleHtml + '>' +
        '<div id="product_list_' + pair.key + '" class="product_list">' +
        '</div></div>';
        new Insertion.Bottom(productConfig, html);
      });
    }
    
    var typeColorsContainer = $("type_colors_container");
    if (typeColorsContainer) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        var style = ' style="display: none;"';
        if (productType == d.currentProductType) style = '';
        html = '<ul id="pt_col_' + pair.key + '"' + style + '></ul>';
        new Insertion.Bottom(typeColorsContainer, html);
      });
    }
    
    var qtyLi = $("qty_li");
    if (qtyLi) {
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        html = '<li id="product_type_data_' + pair.key + '">';
        if (productType.sizeField) {
          html += 
          '<div class="sizing" id="pt_fc_' + productType.sizeField.id + '" style="display:none;">' +
            '<a href="#" onclick="d.showProductInfo(); return false;" class="size_chart">' + ml('size chart') + '</a>' +
            '<label>' +
              ml('Product Sizing') + '<br />' +
            '</label>' +
            '<div id="pt_foc_' + productType.sizeField.id + '" class="size_input"></div>' +
          '</div>';
        }
        html += '</li>';
        new Insertion.Before(qtyLi, html);
      });
    }
    
    var cfContainer = $("product_type_cf_container");
    if (cfContainer) {
      cfContainer.hide();
      
      var self = this;
      $H(this.productTypes).each(function(pair){
        var productType = pair.value;
        html = '<li id="product_type_cf_' + pair.key + '" style="display: none;">';
        
        if (productType.customFields.list.length > 0) {
          html += '<div class="custom_fields">';
          for(var i=0;i< productType.customFields.list.length;i++) {
            var field = productType.customFields.list[i];
            html +=
            '<div class="custom_field" id="pt_fc_' + field.id + '" style="display:none;">' +
              '<label>' +
                '<span class="help" id="cf_lbl_' + field.id + '">' + field.name + '</span>' +
                '<b class="tip" id="cf_tip_' + field.id + '">' +
                  '<b>' + field.description + '</b>' +
                '</b>' +
              '</label>' +
              '<div id="pt_foc_' + field.id + '" class="custom_option"></div>' +
            '</div>';
          }
          html += '</div>';
        }
        html += '</li>';
        new Insertion.Bottom(cfContainer, html);
      });
    }
    
    $H(this.productTypes).each(function(pair){
        if (pair.value != d.currentProductType) {
          pair.value.bindElements();
          pair.value.selectCategory(0);
          pair.value.deselect();
        }
    });
    this.currentProductType.selectCategory(0);
  },
  
  setDesignerOptions: function D_setDesignerOptions(flags) {
    this.designerOptions = new DesignerOptions(flags);
  },
  
  setDesignElements: function D_setDesignElements(sizer, designPane) {
    this.sizer = sizer;
    this.designPane = designPane;
    this.rotator = $("ts_rot");
    if(this.rotator == null) this.rotator = this.sizer;
    
    this.roSizers = new Hash();
    
    this.roSizers["TR"] = $("ts_tr");
    this.roSizers["TL"] = $("ts_tl");
    this.roSizers["BR"] = $("ts_br");
    this.roSizers["BL"] = $("ts_bl");
    
   
  },
  
  bindElements: function D_bindElements() {
    this.saveContainer = $("save_div");
    if($('copy_options') != null) {
      if(!this.designerOptions.showCopyPaste() && !this.designerOptions.showCopyItem()) { //no c&p available
        $('copy_options').hide();
        $('paste_options').hide();
      } else if(!this.designerOptions.showCopyPaste()) {
        $('copy_area_button').hide();
      } else if(!this.designerOptions.showCopyItem()) {
        $('copy_item_button').hide();
      }
    }
    if($('zoom_options') != null) {
       if(!this.designerOptions.showZoom()) {
         $('zoom_options').hide();
       }
    }
    this.qtyEl = $('qty_container');
    if(this.qtyEl != null) {
      Event.observe(this.qtyEl, "mousedown", this.checkQtyClick.bindAsEventListener(this));
    }
  },
  
  checkQtyClick: function(event) {
    if($('qty').disabled) {
      if(this.currentCProduct.usingTeamnames) {
        this.currentCProduct.getTeamNames().editTeamNames();
      } else {
        alert(ml("You must enter in quantity values against sizes"));
        var el = $('multi_container_' + this.currentCProduct.product.type.sizeField.id);
        if(el != null) {
          new Effect.Highlight(el);
        }
      }
    }
  },
  
  setManagePaneContainer: function D_setManagePaneContainer(el) {
    this.managePaneContainer = el;
  },
  
  setDefaultProcess: function D_setDefaultProcess(processId) {
    this.userSelectedProcessId = processId; 
    this.defaultProcess = processes.byId[processId];
    this.defaultProcessId = processId;
  },
  
  setDebugAjax: function D_setDebugAjax(doDebug) {
    this.debugAjax = doDebug;
  },
  
  ajaxUrl: function D_ajaxUrl(url) {
    if(this.translateParams != null) {
      url = addUrlParam(url, "translate=" + this.translateParams);
    }
    if(this.debugAjax != 0) {
      return addUrlParam(url, "email_request_log=" + this.debugAjax);
    } else {
      return url;  
    }
  },
  
  //are we in 'simple' mode (not designer)
  inSimpleMode: function(type) {
    if(type == null) {
      return (this.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT || this.mode == DESIGNER_MODE_TEMPLATE);   
    }
    if(type == TEMPLATE_MODE_CP) {
      //if(this.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) return true; old configured product view uses DESIGNER_MODE_VIEW_CUSTOM_PRODUCT, which should not be considered inSimpleMode 
      return (this.mode == DESIGNER_MODE_TEMPLATE && this.templateMode == TEMPLATE_MODE_CP);
    }
    if(type == TEMPLATE_MODE_DESIGN) {
      return (this.mode == DESIGNER_MODE_TEMPLATE && this.templateMode == TEMPLATE_MODE_DESIGN);
    }
    return false;
  },
  
  getThumbnailSize: function() {
    if(this.mode == DESIGNER_MODE_TEMPLATE || this.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return 3;
    }
    return 11;
  },
  
  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");
    if (this.productsById[product.id]) delete this.productsById[product.id];
    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 = null;
    if(parseInt(options.type, 10) == 202) {
      asset = new TemplateTextAsset(options, this);
    } else {
      asset = new Asset(options, this);
    }
    return this.addAssetObj(asset);
  },
  
  addAssetObj: function(asset) {
    this.assets[asset.id] = asset;
    if(asset.sid != null && asset.sid != asset.id && this.assets[asset.sid] == null) {
      this.assets[asset.sid] = asset;
    }
    return asset;
  },
  
  addPriceToUpdate: function D_addPriceToUpdate(el_id, pp_id) {
    if(!d.pricesToUpdate) d.pricesToUpdate = [] ;
    d.pricesToUpdate.push({id: pp_id, ele: el_id+pp_id}) ;
  },
  
  selectCurrentProductType: function D_selectCurrentProductType(id, doUpdate) {
    this.currentProductType = this.productTypes[id];
  },
  
  getProductType: function D_getProductType(id, callback) {
    if(this.productTypes[id] != null) {
      callback(this.productTypes[id]);
      return;
    }
    var self = this;
    this.getData({ptid:id, pid:-1}, function() {
      callback(self.productTypes[id]);
    }, "m_apparel_pane");
  },
  
  //called from the cart
  selectConfiguredProduct: function D_selectConfiguredProduct(id, doUpdate, doSelect) {
    log("d.selectConfiguredProduct()");
    //check if the current item is dirty....
    if((typeof(dontWarnBeforeLeaving) != 'undefined')&&(dontWarnBeforeLeaving == false)&&(!ignoreDirtyProducts)) { //current item is dirty...
      msgBox(ml("Save Changes?"), ml("The currently loaded item has unsaved changes. Do you want to save the item before switching product?"), null, ml("Yes"), ml("No"), ml("Cancel"), function(result) {
          if(result == 0) { //yes
            d.currentCProduct.save(false, false, function() {
                d.selectConfiguredProduct(id, doUpdate, doSelect);
            });
          } else if(result == 1) { //no
            dontWarnBeforeLeaving = true;
            if(d.currentCProduct.addedToCart) {
              d.currentCProduct.rollBack();
            }
            d.selectConfiguredProduct(id, doUpdate, doSelect);
          } else {
            //cancel...
          }
      });
      return;
    }

    this.currentCProduct = this.cart.getProduct(id);
    log(id);
    if(this.currentCProduct.product.type.id != this.currentProductType.id) {
      log("changing product type");
      this.currentProductType.deselect();
      this.currentProductType = this.currentCProduct.product.type;
      this.currentProductType.select( function() {
        d.currentProductType.selectConfiguredProduct(d.currentCProduct, doUpdate, doSelect);
      }, d.currentCProduct.product.categoryId, this.currentCProduct.product.id);
    } else {
      log("select configured product for type.");
      this.currentProductType.selectConfiguredProduct(this.currentCProduct, doUpdate, doSelect);
    }
    this.cart.selectCartItem(this.currentCProduct);
    this.currentCProduct.setAlertIcons();
    this.currentCProduct.selected();
  },
  
  updateConfiguredProductId: function D_updateCurrentConfiguredProductId(oldId, newId) {
    log("updateConfiguredProductId from " + oldId + " to " + newId);
    var cp = this.cart.productsById[oldId];
    if(cp == null) {
      if((this.mappedConfiguredProductIds != null) && (this.mappedConfiguredProductIds[oldId] != null)) {
        var preMappedId = this.mappedConfiguredProductIds[oldId];
        log("updateConfiguredProductId already mapped from " + oldId + " to " + preMappedId);
        //add this mapping...the server map refer to newId when it really wants preMappedId
        this.mappedConfiguredProductIds[newId] = preMappedId;
      } else {
        log("Unable to find old configured product");
      }
      return;
    }
    if(this.mappedConfiguredProductIds == null) {
      this.mappedConfiguredProductIds = {};
    }
    this.mappedConfiguredProductIds[oldId] = newId;
    
    cp.id = newId;
    this.cart.productsById.remove(oldId);
    this.cart.productsById[newId] = cp;
    if(this.currentCProduct == cp) {
      log("updateConfiguredProductId: this.currentCProduct == cp");
      //need to regen the fields...
      this.currentProductType.setFields(this.currentCProduct);
    }
    this.cart.products.each(function(p) {
        if (p.cp_type == CP_EXTRA_CHARGE) {
          var index = p.associatedItems.indexOf(oldId);
          if (index != -1) {
            p.associatedItems[index] = newId;
          }
        }
    });
  },
  
  getConfiguredProductId: function(id) {
    if((this.mappedConfiguredProductIds != null) && (this.mappedConfiguredProductIds[id] != null))  {
      var mappedId = this.mappedConfiguredProductIds[id];
      log("getConfiguredProductId:" + id + "=>" + mappedId);
      return mappedId;
    }
    return id;
  },
  
  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.updateCancelState(options);
    }
  },
  
  // Find the first view/area that has the selected
  initViews: function D_initViews(defaultView, processIds){
    if(processIds == null) {
      processIds = [d.userSelectedProcessId];
    }
    //log("initViews:");
    //init the base price to include the first area price that can use the selected process
    var selData = this.currentCProduct.findFirstProcess(processIds);
    if(selData == null) {
      alert("Unable to select an area that supports the desired decoration processes");  
    } else {
      d.userSelectedProcessId = selData.process.id;
      d.selectCurrentView(selData.view.id, selData.area.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.inSimpleMode(TEMPLATE_MODE_CP)) {
      this.currentCView.show();
      
      this.currentCView.setBgPosition();
    } else if(this.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) { //lets also set the area and load the items used in that area
      
      if(!this.currentCProduct.isNonDecProduct()) {
        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();
      if(!this.currentCProduct.isNonDecProduct()) {
        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(d.mode == DESIGNER_MODE_TEMPLATE) return;
    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(d.mode == DESIGNER_MODE_TEMPLATE) return;
    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);
    if(d.mode == DESIGNER_MODE_TEMPLATE) {
      for(var viewId in this.currentCProduct.views) {
        var view =  this.currentCProduct.views[viewId];
        if(view.isUsed()) {
          view.showPanels();  
        }
      }
    }
  },
  
  //check if in ammend mode and trying to do something that could modify the price...
  canAddAsset: function D_canModifyDesign(processId) {
    if(this.mode == DESIGNER_MODE_AMEND) {
      var cpvap = this.currentCViewArea.processes[processId];
      if(cpvap.items.list.length == 0) { //about to add
        return false;
      }
      return cpvap.productProcess.canModifyDesign();
    }
    return true;
  },
  
  addImage: function D_addImage(processId, options) {
    if(options == null) options = {};
    if(processId == null) {
      //select process
      processId = this.showSelectProcess("image", options.useDefaultProcess);
      if(processId == null) {
        return;
      }
    }
    
    if(!this.canAddAsset(processId)) {
      alert(ml("You cannot add an image to this area because it would change the price of your order"));
      return;
    }
    this.newProcessId = processId;
    
    var processArea = this.currentCViewArea.processes[processId];
    
    var om = this.inOrderManager ? '&om=1' : '';
    
    this.decorationLibraryManager.selectImage(this.currentCViewArea.productArea.processes.byId[processId], function(image) {
        var newAssetDef = d.addAsset(image.assetOptions());
        if((this.mode == DESIGNER_MODE_AMEND) && (newAsset.hasCost())) {
          if(newAssetDef.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(newAssetDef.imageType == 2) {
          opts.emb="1";
          opts.stc = newAssetDef.stitchCount;
          opts.cw = newAssetDef.colorWays.length > 0 ? 0 : null;
        } 
        if(options.toReplace != null) {
          opts.t = options.toReplace.top;
          opts.l = options.toReplace.left;
          opts.z = options.toReplace.zIndex;
          if(newAssetDef.imageType != 2) { //cant set emb size...
            opts.w = options.toReplace.width;
            opts.h = options.toReplace.height;
          }
        }
        var process = processes.byId[this.newProcessId];
        
        var self = this;
        
        newAssetDef.getForAddToDesigner(opts, processArea, function(newAsset, opts) {
          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 ajax = new Ajax.Request(d.ajaxUrl("/shared/library/digitize_asset/" + newAsset.id + "?color_count=" + process.maxColors + "&process_id=" + process.id + om + "&uid=" + cCustomerId), {asynchronous:true, evalScripts:true, 
                  onSuccess: function D_onSuccess(transport) {
                    var result = transport.responseText.evalJSON();
                    log(result);
                    newAsset = d.addAsset(result);
                    var existingColors = newAsset.getMetaData(["split_colors", process.maxColors], null);
                    //var merged = hashMerge(existing, result);
                    //newAsset.setMetaData(["split_colors", process.maxColors], merged);
                    log(newAsset);
                    //init the items colors
                    opts.c = existingColors.process_colors[self.newProcessId].join(":");
                    
                    if(options.toReplace != null) {
                      options.toReplace.del(); 
                    }
                    self.addNewItemByAsset(self.newProcessId, newAsset, opts);
                    
                    self.decorationLibraryManager.getLibrary(self.newProcessId, function(decLib) {
                        decLib.myDigitizedDesigns.toggleVisible(true);
                        if(decLib.myDigitizedDesigns.selected) {
                          decLib.myDigitizedDesigns.refreshImages(true);
                        } else {
                          decLib.myDigitizedDesigns.select();
                        }
                    });
                  },
                  onFailure: function D_onFailure(transport) {
                    alert("An Error Occured:" + transport.responseText);
                  },
                //  onException: function D_onException() {
              //      alert("Exception occured");
                //  },
                  onComplete: function D_onComplete() {
                    closePopup("split_colors_status");
                  }
              });
              return true;
            } else {
              
              popup("copy_digitize_image");
  
              var ajax = new Ajax.Request(d.ajaxUrl("/shared/library/make_digitize_copy/" + newAsset.id + "?process_id=" + process.id + om + "&uid=" + cCustomerId), {asynchronous:true, evalScripts:true, 
                  onSuccess: function(transport) {
                    var result = transport.responseText.evalJSON();
                    log(result);
                    newAsset = d.addAsset(result);
                    newAsset.getOpacity(null, function(percent) {
                        closePopup("copy_digitize_image");
                        newAsset.setMetaData("percent_opaque", percent);
                        opts.po = percent;
                        if(options.toReplace != null) {
                          options.toReplace.del(); 
                        }
                        self.addNewItemByAsset(self.newProcessId, newAsset, opts);
                        self.decorationLibraryManager.getLibrary(self.newProcessId, function(decLib) {
                            decLib.myDigitizedDesigns.toggleVisible(true);
                            if(decLib.myDigitizedDesigns.selected) {
                              decLib.myDigitizedDesigns.refreshImages(true);
                            } else {
                              decLib.myDigitizedDesigns.select();
                            }
                        });
                    });
                  },
                  onFailure: function(transport) {
                    closePopup("copy_digitize_image");
                    alert("An Error Occured:" + transport.responseText);
                  }
              });
              return true;
            }
          } else if(newAsset.imageType != 2 && process.isWilcomEMB()) { //its a digitized asset..          
            if(process.autoSplit) { //fulfillment house setup to auto reduce/split colors 
              var existingColors = newAsset.getMetaData(["split_colors", process.maxColors], null); //get the existing colors...
              if(existingColors != null) { 
                opts.c = existingColors.process_colors[process.id].join(":");
                opts.cc = process.maxColors;
                opts.spc = "1" ;
              }
            }
          }
          if(options.toReplace != null) {
            options.toReplace.del(); 
            options.toReplace = null;
          }
          self.addNewItemByAsset(self.newProcessId, newAsset, opts);
          return true;
        });
        
    }.bind(this));
    d.track("add-image");
    return;
  },
  
  addText: function D_addText(processId, options) {
    if(options == null) options = {};
    if(processId == null) {
      processId = this.showSelectProcess("text", options.useDefaultProcess);
      if(processId == null) {
        return;
      }
    }
    
    if(!this.canAddAsset(processId)) {
      alert(ml("You cannot add an image to this area because it would change the price of your order"));
      return;
    }
    this.newTextOptions = options;
    this.newProcessId = processId;
    $("new_text").value = "";
    
    popup("new_text_popup");
    $("new_text").focus();
  },
  
  addTeamName: function D_addText(processId, options) {
    if(options == null) options = {};
    if(processId == null) {
      processId = this.showSelectProcess("teamname", options.useDefaultProcess);
      if(processId == null) {
        return;
      }
    }
    //check there is not already a teamname (if EMB)
    if(processes.byId[processId].isWilcomEMB()) {
      if(this.currentCViewArea.processes[processId].isUsingItemOfType(2)) {
        alert(ml("You can only have 1 embroidery teamname per decoration area"));
        return;
      }
    }
    
    this.newProcessId = processId;
    //$("teamname_templates_container").update(this.teamNameTemplates.buildHtml(processId));
    
    
    if(d.mode == DESIGNER_MODE_CONFIGURE) {
      this.continueAddTeamname(false);
    } else if(hashFirstElement(this.currentCProduct.getTeamNames().items) != null) {
      this.continueAddTeamname(true);
    } else {
      var sizeHtml = "";
      var bindCheck = false;
      if(this.currentCProduct.supportMultiSizes() ) {
        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 {
        var msg = '';
        
        if(this.currentCProduct.product.usesBundles()) {
          msg = ml("Available in bundles of %1s with a minimum of %2s", [this.currentCProduct.product.bundleSize, this.currentCProduct.getMinQty()]);
          bindCheck = true;
          
        } else if(this.currentCProduct.product.usesMinQty()) {
          msg = ml("minimum of %s", this.currentCProduct.getMinQty());
          bindCheck = true;
        } 
        
        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 + '"/> ' + msg;
      }
      $("teamname_sizing_container").update(sizeHtml);
      if(bindCheck) {
        var self = this;
        $('tn_qty').onchange = function() {
          var qty = self.currentCProduct.checkQty(this.value, false, false); 
          if(qty != parseInt(this.value, 10)) {
            this.value = qty;
            if(self.currentCProduct.product.usesBundles()) {
              alert(ml("Changed quantity to %s because of minimum quantity or bundle sizes", qty));
            } else {
              alert(ml("Changed quantity to %s because that is the minimum allowed", qty));
            }
          }
        }
      }
      popup("new_teamname_popup");
    }
  },
  
  
  continueAddTeamname: function(skipInit) {
    
    var proc = processes.byId[this.newProcessId];
    
    var txtClr = pwColorPicker.getDefaultColor(this.newProcessId, "#FF3333", proc.defaultTextColorId);
    
    
    var teamItems = [];
    
    if(d.mode == DESIGNER_MODE_CONFIGURE || proc.isWilcomEMB()) {
      var opts = {};
      opts.tc = txtClr;
      if(proc.isWilcomEMB()) opts.emb = "1";
      var lbl = this.currentCProduct.getTeamNames().getUniqueLabel("Team Name");
      opts.lbl = lbl;
      opts.iw = 15;
      var teamItem = this.addNewItemByAsset(this.newProcessId, new TeamNameAsset(), opts);
      teamItems.push(teamItem);
    } else {
      //we want to add a name and number
      //the name should be spaced: 5% gap, 15% name, 5% gap, 65% number
      //work in designer space....
      var cProc = this.currentCViewArea.processes[this.newProcessId];
      var height = parseFloat(this.currentCViewArea.productArea.dH);
      
      var pHeight = cProc.productProcess.fullHeight;
      var pWidth = cProc.productProcess.fullWidth;
      
      var opts = {};
      opts.tc = txtClr;
      opts.fs = parseInt(height * 0.15, 10);
      opts.mw = 95; 
      opts.lbl = this.currentCProduct.getTeamNames().getUniqueLabel("Name");
      opts.iw = 15;
      //opts.l = pWidth * 0.025; // 2.5% margin 
      opts.l = (pWidth - 10) / 2; //center 
      opts.t = pHeight * 0.125; // 5% margin
      opts.w = 10;
      opts.h = 10;
      opts.defaultText = "Name";
      //opts.w = pWidth * 0.95;
      //opts.h
      
      var teamItem = this.addNewItemByAsset(this.newProcessId, new TeamNameAsset(), opts);
      teamItems.push(teamItem);
      
      var opts = {};
      opts.tc = txtClr;
      opts.fs = parseInt(height * 0.75, 10);
      opts.mw = 95; 
      opts.lbl = this.currentCProduct.getTeamNames().getUniqueLabel("Number");
      opts.iw = 3;
      opts.l = (pWidth - 10) / 2; //center 
      opts.t = pHeight * 0.60; // 25% margin
      opts.w = 10;
      opts.h = 10;
      opts.defaultText = "00";
      //opts.w = pWidth * 0.95;
      //opts.h
      
      teamItem = this.addNewItemByAsset(this.newProcessId, new TeamNameAsset(), opts);
      teamItems.push(teamItem);
      
    }
    
    if(this.currentCProduct.getTeamNames().initNew(teamItems, false, (skipInit != true) )) {
      if(d.mode == DESIGNER_MODE_CONFIGURE) $('add_teamname_member').hide();
      popup("edit_teamname_popup");
    } else {
      alert(ml("You must specify the number of teamnames you want"));
    }
  },
  
  addPlaceholder: function(processId) {
    if(processId == null) {
      processId = this.showSelectProcess("placeholder");
      if(processId == null) {
        return;
      }
    }
    
    var opts = {text: "Placeholder Artwork"};
    var proc = processes.byId[processId];
    if(proc.isWilcomEMB()) {
      opts.emb = "1";
    }
    this.addNewItemByAsset(processId, new PlaceholderAsset(), opts);
  },
  
  //get a list of allowed item types (image/text/teamname) by process
  getAllowedItemTypes: function D_getAllowedItemTypes() {
    if(this.allowedItemTypes != null) {
      return this.allowedItemTypes;
    }
    var allowed = {
      image: { count:0, single:null},
      text: { count:0, single:null},
      teamname: { count:0, single:null},
      placeholder: { count:0, single:null}
    };
    for(var i=0; i < processes.list.length; i++) {
      var proc = processes.list[i];
      if(proc.allowImages) {
        allowed.image.count ++;
        allowed.image.single = proc.id;
      }
      if(proc.allowText) {
        allowed.text.count ++;
        allowed.text.single = proc.id;
      }
      if(proc.allowTeamNames) {
        allowed.teamname.count ++;
        allowed.teamname.single = proc.id;
      }
      if(proc.allowPlaceHolder) {
        allowed.placeholder.count ++;
        allowed.placeholder.single = proc.id;
      }
    }
    this.allowedItemTypes = allowed;
    return this.allowedItemTypes;
  },
  
  //when an area/process is selected check what add item options should be available...
  checkAddAvailableOptions: function D_checkAddAvailableOptions() {
    var allAllowed = this.getAllowedItemTypes();
    
    if(allAllowed.image.count == 0) {
      //$("no_items_0_image").hide();
      $("add_item_image").hide();
    } else {
      //$("no_items_0_image").show();
      $("add_item_image").show();
    }
    if(allAllowed.text.count == 0) {
      //$("no_items_0_text").hide();
      $("add_item_text").hide();
    } else {
      //$("no_items_0_text").show();
      $("add_item_text").show();
    }
    if(allAllowed.teamname.count == 0) {
      //$("no_items_0_teamname").hide();
      $("add_item_team_name").hide();
    } else {
      //$("no_items_0_teamname").show();
      $("add_item_team_name").show();
    }
    if(this.inOrderManager) {
      $("add_item_placeholder").show();
    } else {
      $("add_item_placeholder").hide();
    }
  },
  
  showSelectProcess: function D_showSelectProcess(type, useDefaultProcess) {
    var types = ["text","teamname","image", "placeholder"];
    for(var i=0; i < types.length; i++) {
      if(types[i] == type) {
        $("select_type_" + types[i]).show();
      } else {
        $("select_type_" + types[i]).hide();
      }
    }
    //$("select_type_" + type).style.display = "";
    //$("select_type_" + otherType).style.display = "none";
    
    var allowedProcesses = this.currentCViewArea.getAllowedProcesses();
    var allowedTypes = this.getAllowedItemTypes();
    
    if(allowedTypes[type].count == 1) {
      if(this.defaultProcess != null && this.defaultProcess.id == allowedTypes[type].single) {
        log("Only one allowedTypes[process].count and its the default process... skip select process step");
        return allowedTypes[type].single;
      }
    }
    
    var procSize = hashSize(allowedProcesses);
    if(procSize == 0) {
      alert(ml("This product has been configured incorrectly and has no usable decoration processes assigned to it."));
      return null;
    } else {
      
      var allowCount = 0;
      var allowProcessId = null;
      var foundDefaultProcess = false;
      for(var i=0; i < processes.list.size(); i++) {
        var process = processes.list[i];
        /*if(process.allow[type] == true) { //can this process allow this type (i.e can emb allow text?)
          $("add_" + type + "_type_" + process.id).show();
        } else {
          $("add_" + type + "_type_" + process.id).hide();
        }*/
        
        if((allowedProcesses[process.id])&&(process.allow[type] == true)) { //we can use the process....
          if (processes.list.size() == 1) {
            return process.id;
          }
          $("add_" + type + "_type_" + process.id).className="";
          allowCount ++ ;
          allowProcessId = process.id;
          if(d.defaultProcessId == process.id) {
            foundDefaultProcess = true;
          }
        } else {
          $("add_" + type + "_type_" + process.id).className="disabled";
          //why cant we add this?
          if(process.allow[type] != true) {
            if(type=="text") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have text added", process.name));
            } else if(type=="teamname") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have team names added", process.name));
            } else if(type=="image") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have image designs added", process.name));
            } else if(type=="placeholder") {
              $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be have placeholder added", process.name));
            }
          } else if(this.currentCViewArea.productArea.canDecorate(process)) { //because of mixing
            $("add_item_" + type + "_disabled_" + process.id).update(ml("Unable to use %s because it cannot be mixed with existing decorations", process.name));
          } else {
            $("add_item_" + type + "_disabled_" + process.id).update(this.currentCViewArea.productArea.getDecProcDisabledMessage(process));
          }
          
        }
      }
      if(useDefaultProcess && foundDefaultProcess) {
        return d.defaultProcessId;
      }
      /*
      if(this.defaultProcess != null) {
        if(allowCount == 1 && allowProcessId == this.defaultProcess.id) {
          return this.defaultProcess.id;
        }
      }*/
      popup("select_type_popup");
      return null;
    }
  },
  
  continueAddText: function D_continueAddText() {
    var text = $("new_text").value;
    if((text == null)||(text == "")) {
      alert(ml("You must enter a value"));
      return;
    }
    var opts = {text: text};
    var proc = processes.byId[this.newProcessId];
    if(proc.isWilcomEMB()) {
      opts.emb = "1";
    }
    if(this.newTextOptions != null) {
      if(this.newTextOptions.toReplace != null) {
        opts.t = this.newTextOptions.toReplace.top;
        opts.l = this.newTextOptions.toReplace.left;
        opts.z = this.newTextOptions.toReplace.zIndex;
        this.newTextOptions.toReplace.del();
      } 
    }
    opts.tc = pwColorPicker.getDefaultColor(this.newProcessId, "#FF3333", proc.defaultTextColorId);
    this.addNewItemByAsset(this.newProcessId, new TextAsset(), opts);
    this.newTextOptions = null;
  },
  
  addNewItemByAsset: function D_addNewItemByAsset(processId, asset, initData) {
    //add to current canvas
    var newItem = this.currentCViewArea.addNewItem(processId, asset, initData);
    if(newItem != null) { 
      newItem.select();
      d.itemChanged(true);
      return newItem;
    }
  },
  
  startNewCartItem: function D_startNewCartItem(doSelect, showTab) {
    //check if the current item is dirty....
    if((typeof(dontWarnBeforeLeaving) != 'undefined')&&(dontWarnBeforeLeaving == false)&&(!ignoreDirtyProducts)) { //current item is dirty...
      msgBox(ml("Save Changes?"), ml("The currently loaded item has unsaved changes. Do you want to save the item before designing another product?"), null, ml("Yes"), ml("No"), ml("Cancel"), function(result) {
          if(result == 0) { //yes
            d.currentCProduct.save(false, false, function() {
                d.startNewCartItem(doSelect, showTab);
            });
          } else if(result == 1) { //no
            dontWarnBeforeLeaving = true;
            if(d.currentCProduct.addedToCart) {
              d.currentCProduct.rollBack();
            }
            d.currentProductType.itemSaved();
            d.startNewCartItem(doSelect, showTab);
          } else {
            //cancel...
          }
      });
      return;
    }
    
    var cp = this.cart.getUnsavedProduct();
    if(cp != null) {
      this.selectConfiguredProduct(cp.id, true, doSelect);
      if(showTab) {
        d.selectTab('m','apparel');
      }
      cp.setAlertIcons();
    } else {
      
      var asyncKey = asyncStart("designer_container");
      var self = this;
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/new_product?id=" + this.currentCProduct.product.id), {asynchronous:true, evalScripts:true, onComplete: function D_onComplete(response) {
          asyncFinish(asyncKey);
          cp = self.cart.getUnsavedProduct();
          if(cp != null) {
            self.selectConfiguredProduct(cp.id, true, doSelect);
            if(showTab) {
              d.selectTab('m','apparel');
            }
            cp.setAlertIcons();
          } else {
            alert("ERROR: No new item was added");
          }
        }
       });
    }
    
  },
  
  tbTransformed: function D_tbTransformed(configuredProductId, viewId, areaId, options) {
    var cp = this.cart.getProduct(configuredProductId);
    if(cp == null) {
      log("tbTransformed: Unable to get configured product " + configuredProductId);
      return;
    }
    var view = cp.views[viewId];
    if(view == null) {
      log("tbTransformed: Unable to get view " + viewId);
      return;
    }
    var area = view.areas[areaId];
    if(area == null) {
      log("tbTransformed: Unable to get area " + areaId);
      return;
    }
    area.tbTransformed(options);
  },
  
  selectTab: function D_selectTab(type, tab, callback) {
    var curTab = this.selected_tabs[type];
    if(curTab == tab) {
      return;
    }
    d.deSelectCurrentItem();
    d.track("select-tab/" + type + "/" + tab);
    
    if(callback!=null) {
      if(!callback(tab)) {
        return;
      }
    }
    log("cur tab = " + curTab + " type=" + type);
    var oldTab = $(type + "_" + curTab);
    if(oldTab != null) {
      if(oldTab.className != "unselected_tab_hidden") {
        oldTab.className = "unselected_tab";
      }
    }
    var newTab = $(type + "_" + tab);
    if(newTab != null) {
      $(type + "_" + tab).className = "selected_tab";
    }
    
    if(callback==null) {
      var oldPane = $(type + "_" + curTab + "_pane");
      if(oldPane != null) oldPane.hide();
      var newPane = $(type + "_" + tab + "_pane");
      if(newPane != null) newPane.show();
    }
    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(d.mode==DESIGNER_MODE_TEMPLATE) return;
    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;
  },
  
  applyZoom: function D_applyZoom(dims) {
    var res = {};
    if(dims.l != null) {
      res.l = dims.l * this.zoom;
    }
    if(dims.t != null) {
      res.t = dims.t * this.zoom;
    }
    if(dims.h != null) {
      res.h = dims.h * this.zoom;
    }
    if(dims.w != null) {
      res.w = dims.w * this.zoom;
    }
    return res;
  },
  
  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;
    }
    if((d.inSimpleMode(TEMPLATE_MODE_DESIGN))&&(this.currentCView!= null)) {
      //we need to swap the design to the selected view  
      var view = this.currentCProduct.getView(viewId, true);
      if(view.moveTemplateToView(this.currentCView) >= 0) {
          this.currentCView.queueViewUpdate(50);
          view.queueViewUpdate(50);
      }
    }
    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.usesBundlesOrMinQty()) {
      this.currentCProduct.updateQty($("qty").value, false);
      d.itemChanged();
    } else if(!fromKeyUp && this.currentCProduct.product.usesBundlesOrMinQty()) {
      this.currentCProduct.updateQty($("qty").value, false);
      d.itemChanged();
    }  
  },
  
  multiQtyChange: function D_multiQtyChange(el, fieldId, optionId, subOptionId, unitPrice, isAutoPrice, unitDelta) {
    var value = (typeof el == 'number') ? el : el.value;
    this.currentCProduct.updateMultiQty(value, fieldId, optionId, subOptionId, unitPrice, isAutoPrice, unitDelta);
    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").update(this.currentCProduct.buildQtyDropdownHtml());
      $("qty_dropdown_float").show();
      this.qtyDropDownActive=true;
      //$("qty_selector_sprites").style.top = "-66px";
	  $("qty_selector").className="alt";
	  
      this.qtyDropDownTime = new Date().getTime();
      Event.observe(document.body, "mouseup", this.onQtyDDBgEvent);
    }
  },
  
  qtyDropDownSelected: function D_qtyDropDownSelected(qty) {
    $("qty").value = qty;
    this.currentCProduct.updateQty(qty, false);
    d.itemChanged();
  },
  
  onQtyDDBg: function D_onQtyDDBg(event) {
    var thisTime = new Date().getTime();
    if(this.qtyDropDownTime != null && thisTime - this.qtyDropDownTime < 2000) {
       this.qtyDropDownTime = null;
       return; //this was the mouse up event from when we bound it... we ignore it....
    }
    //$("qty_selector_sprites").style.top = "-22px";
	$("qty_selector").className=null;
	
    $("qty_dropdown_float").hide();
    this.qtyDropDownActive = false;
    Event.stopObserving(document.body, "mouseup", this.onQtyDDBgEvent);
  },
  
  fieldChange: function D_fieldChange(el, fieldId) {
    d.currentCProduct.setFieldValue(fieldId, parseInt($(el).value, 10)) ;
  },
  
  itemChanged: function D_itemChanged(is_layout_change) {
    if (typeof(dontWarnBeforeLeaving) != 'undefined') dontWarnBeforeLeaving = false;
    if(is_layout_change) {
      this.currentCProduct.onLayoutChanged();
      this.currentCView.queueViewUpdate();
      if(this.saveContainer != null) this.saveContainer.show();
    }
    this.currentCProduct.product.type.itemChanged();
  },
  
  //from server callback to let us know a thumbnail for a view is ready...
  updateView: function D_updateView(cpid, viewId, src, renderVersion, results) {
    var p = this.cart.getProduct(this.getConfiguredProductId(cpid));
    if(p!=null) {
      p.updateView(viewId, src, renderVersion, results);
    }
  },
  
  checkout: function D_checkout() {
    if(d.mode == DESIGNER_MODE_AMEND) {
      window.location = d.pathPrefix + "/user/view_order/" + this.cart.id + this.extraExternalParams;
    } else {
      /*
      if($("update_cart_container") != null) {
        if($("update_cart_container").style.display == "") {
          if(!confirm(ml("You have unsaved changes to the current item in your cart. Are you sure you want to checkout with saving?"))) {
            return;
          }
        }
      }*/
      d.track("checkout");
      proceedWithOrder();
      //window.location = d.pathPrefix + "/shop/verify" + this.extraExternalParams;
      
    }
  },
  
  continueShopping: function D_continueShopping() {
    if(d.mode == DESIGNER_MODE_AMEND) {
      window.location = d.pathPrefix + "/home/create";
    } else {
      if($("update_cart_container") != null) {
        if($("update_cart_container").style.display == "") {
          if(!confirm(ml("You have unsaved changes to the current item in your cart. Are you sure you want to navigate away without saving?"))) {
            return;
          }
        }
      }
      d.track("continue shopping");
      window.location = d.pathPrefix + "/home/create";
    }
  },
  
  notifyCartChanged: function D_notifyCartChanged() {
    if(window.opener != null) {
      try {
        window.opener.notifyCartChanged();
      } catch(e) {
        log("Unable to notify opener window of item saved");
        log(e);
      }
    }
  },
  
  offsetTop: function D_offsetTop(element, cutOff) {
    var valueT = 0;
    do {
      valueT += element.offsetTop  || 0;
      element = element.offsetParent;
    } while (element && element != cutOff);
    return valueT;
  },
  
  scrollCat: function D_scrollCat(amount) {
    this.currentProductType.scrollCat(amount);
  },
  
  showQualityWarning: function D_showQualityWarning() {
    if(this.hideQualityWarning) {
      return false;
    }
    //popup('quality_warning');
    //return true;
    return false;
  },
  
  acceptQualityWarning: function D_acceptQualityWarning() {
    var doHide = $("dont_show_qual_warning").checked;
    if(doHide) {
      this.hideQualityWarning = true;
      var t2 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/user_quality_warning"), {asynchronous:true, evalScripts:true });
    }
    closePopup('quality_warning');
  },
  
  //user has selected a different product type....
  changeProductType: function D_changeProductType(el) {
    var pt = el.value;
    
    if(pt != this.currentProductType.id) {
      this.deSelectCurrentItem();
      
      d.track("change-product-type");
      
      this.currentProductType.deselect();
      var self = this;
      
      this.getProductType(pt, function(prodType) {
        self.currentProductType = prodType;
        if (self.currentProductType.id == $("sel_prod_type").value) {
          self.currentProductType.select( function() {
            d.currentProductType.selectConfiguredProduct(d.currentCProduct, true, false);
            d.currentCProduct.afterProductChanged();
          }, -1, -1);
        }
        //$("product_type_tab_name").innerHTML = prodType.name;
      });
      
    }
    
  },
  
  
  //user has selected a different product ....
  changeProduct: function D_changeProduct(productId, color) {
    var self = this;
    this.getProduct(productId, function(product) {
        self.getProductType(product.type.id, function(prodType) {
          if(product.type != self.currentProductType) {
            self.currentProductType = prodType;
            self.currentProductType.select( function() {
              d.currentProductType.selectConfiguredProduct(d.currentCProduct, true, false, productId);
              d.currentCProduct.afterProductChanged();
            }, -1, -1);
          } else {
            d.currentProductType.changeSelectedProduct(productId);
          }
          if (color && $("cpc_" + color.id) != null) {
            var el = $("cpc_" + color.id);
            if(prodType.selectedColorCell!=null) {
              prodType.selectedColorCell.removeClassName("selected");
            }
            d.track("change-product-color");
            prodType.selectedColorCell = el;
            prodType.selectedColorCell.addClassName("selected");
            d.currentCProduct.selectColor(color);
            d.itemChanged();
          }
        });
    }, "d_pane");
  },
  
  recalibrateCanvas: function D_recalibrateCanvas() {
    if(this.currentCViewArea!=null) {
      this.currentCViewArea.canvasX = null;
      if(this.currentCViewArea.selectedItem != null) {
        this.currentCViewArea.selectedItem.autoRepositionHandles();
      }
    }
  },
  
  roundPrice: function D_roundPrice(price) {
    if(this.doPriceRounding) {
      if(this.priceRounding > 1) {
        log("rounding " + price + " to " + this.priceRounding);
        var cents = Math.round(price * 100);
        log("cents = " + cents);
        var divs = parseInt(cents / this.priceRounding, 10);
        log("divs = " + divs);
        var adjCents = divs * this.priceRounding;
        log("adjCents = " + adjCents);
        if(adjCents < cents) {
          log("adjCents < cents");
          adjCents += this.priceRounding;
        }
        //round to 99cents if on 1 dollar
        if(this.price99) {
          if((adjCents % 100 == 0)&&(cents>0)) {
            log("adjCents % 100");
            adjCents -= 1;
          }
        }
        price = parseFloat(adjCents) / 100;
    } else {
      var cents = Math.round(price * 100);
      if(this.price99) {
        if((cents % 100 == 0)&&(cents>0)) {
          log("cents % 100");
          cents -= 1;
        }
      }
      price = parseFloat(cents) / 100;
      }
    }
    return price;
  },
  
  roundPriceObject: function D_roundPriceObject(price) {     
    price = price.copy();
    price.rrp = this.roundPrice(price.rrp);
    price.crp = this.roundPrice(price.crp);
    if(!price.isOverriden) price.override = this.roundPrice(price.override);
    if(price.legacy) price.legacy = this.roundPrice(price.legacy); //we will assume the rounding rules hanvnt changed.....
    return price;
  },
  
  round: function D_round(price) {
    return parseFloat(Math.round(price * 100.0)) / 100.0
  },
  
  addPrice: function D_addPrice(base, inc) {
    var res = {cost:0.0, rrp: 0.0, crp: 0.0, overide:0.0};
    var p = [base, inc];
    for(var i=0; i < 2; i++) {
      if(p[i].cost != null) res.cost +=  p[i].cost;
      if(p[i].rrp != null) res.rrp +=  p[i].rrp;
      if(p[i].crp != null) res.crp +=  p[i].crp;
      if(p[i].overide != null) res.overide +=  p[i].overide;
    }
    return res;
  },
  
  legacyPrice: function D_legacyPrice(legacy, fallback) {
    if((this.useLegacyPrices || this.forceLegacyPrices) && legacy != null) {
      return legacy;
    } else {
      return fallback;
    }
  },
  
  registerLegacyPrice: function D_registerLegacyPrice(object) {
    this.legacyPriceChanges[object.id] = object;
  },
  
  resetLegacyPriceTracking: function() {
    this.legacyPriceChanges = {};
  },
  
  getLegacyPrices: function() {
    var objectsWithLegacy = [];
    for(var k in this.legacyPriceChanges) {
      var obj = this.legacyPriceChanges[k];
      if(obj.hasLegacyChanges()) {
        objectsWithLegacy.push(obj);
      }
    }
    return objectsWithLegacy;
  },
  
  formatPrice: function D_formatPrice(price, clazz, applyTax, rrp) {
    if(applyTax==null) {
      applyTax = true;
    }
    var taxWarn = "";
      if((this.tMod != 1)&&(applyTax) && (!d.currentCProduct || !d.currentCProduct.product.taxExempt)) {
      log("Applying tax mod of " + this.tMod);
      taxWarn = '<span class="tax_warning">*</span>';
      price *= this.tMod;
    }
    price *= this.cMod;
    if(rrp != null) {
      if((this.tMod != 1)&&(applyTax) && (!d.currentCProduct || !d.currentCProduct.product.taxExempt)) {
        rrp *= this.tMod;
      }
      rrp *= this.cMod;
    }
      
    var dc = price.toPrice(); //fix: .toFixed(2) was truncating
    var codeHtml = this.curCode;
    if(clazz!=null) {
      codeHtml = '<span class="' + clazz + '">' + this.curCode + "</span>";
    }
    if((rrp != null)&&(rrp != price)&&(typeof(orderManager) == "undefined")) {
      rrp = '<span class="rrp">' + this.curGlyph + (parseFloat(Math.round(rrp * 100)) / 100.0) + '</span> ';   
    } else {
      rrp = '';  
    }
    
    switch(this.priceFormat) {
      case 1: return rrp + this.curGlyph + dc + " " + taxWarn;
      case 2: return rrp + dc + " " + codeHtml + taxWarn;
      case 3: return rrp + dc + " " + taxWarn;
      case 4: return rrp + dc + this.curGlyph + " " + taxWarn;
      default: return rrp + this.curGlyph + dc + " " + codeHtml + taxWarn;
    }
  },
  
  deSelectCurrentItem: function D_deSelectCurrentItem() {
    if(this.currentCViewArea) {
      log("deSelectCurrentItem");
      this.currentCViewArea.selectItem(null);
    }
  },
  
  onKeyUp: function D_onKeyUp(event) {
    if(this.currentCViewArea && this.currentCViewArea.selectedItem!=null) {
      if(event==null) {
        event = window.event;
      }
      if(event.keyCode==Event.KEY_DELETE) {
        var el = Event.element(event);
        
        var allowDelete = true;
        
        while(el != null) {
          if(el.tagName == "INPUT" || el.tagName == "SELECT" || el.tagName == "TEXTAREA") {
            return;
          } else if(el.getAttribute("allow-delete") == "true") {
            return;
          }
          el = el.parentElement;
        }
        if(this.currentCViewArea.selectedItem.canModifyDesign) {
          this.currentCViewArea.selectedItem.deleteClick(event);
        }
      }
    }
  },
  
  showProductInfo: function D_showProductInfo() {
      d.deSelectCurrentItem();
      var purl = this.pathPrefix + "/product_info/ppop";
      var url = "";
      if(purl.indexOf("?") == -1) {
        url = purl + "?pid=" + this.currentCProduct.product.id;
      } else {
        url = purl + "&pid=" + this.currentCProduct.product.id;
      }
    //}
    $("dynamic_popup").innerHTML = "<br/><br/>Generating..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({success:"dynamic_popup"}, d.ajaxUrl(url), {asynchronous:true, evalScripts:true,
      onComplete: function D_onComplete() { 
        asyncFinish(aKey);
        $("dynamic_popup").style.width="650px";
        repositionPopup("dynamic_popup");
      },
      onFailure: function D_onFailure() {
        asyncFinish(aKey);
        closePopup("dynamic_popup");
        alert("An error occured processing preview");
      }
    });
    
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  showDesignerHelp: function D_showDesignerHelp() {
    d.deSelectCurrentItem();
    var url = this.pathPrefix + "/product_info/designer_help/ppop?popup";
    $("dynamic_popup").innerHTML = "<br/><br/>Generating..<br/><br/>";
    var aKey = null;
    var t2 = new Ajax.Updater({success:"dynamic_popup"}, d.ajaxUrl(url), {asynchronous:true, evalScripts:true,
      onComplete: function D_onComplete() { 
        asyncFinish(aKey);
        //$$("#dynamic_popup .popup")[0].style.width="550px";
        $("dynamic_popup").style.width="550px";
        $("dynamic_popup").style.marginLeft="-275px";
        repositionPopup("dynamic_popup");
      },
      onFailure: function D_onFailure() {
        closePopup("dynamic_popup");
        alert("An error occured processing preview");
      }
    });
    
    popup("dynamic_popup");
    aKey = asyncStart($("dynamic_popup"));
  },
  
  displayAssetInformation: function D_displayAssetInformation(asset) {
    $("asset_view").style.display='';
    $("i_no_tab").width="600";
    var aImg = $("asset_view_img");
    var dim = asset.scale(300,300);
    aImg.style.width = parseInt(dim.w, 10) + "px";
    aImg.style.height = parseInt(dim.h, 10) + "px";
    setTransparentImage($("asset_view"), aImg, asset.getWUrl(), function() { repositionPopup('add_image'); });
    if(asset.colors != null) {
      var tr = $("asset_colors_tr");
      while(tr.firstChild != null) {
        tr.removeChild(tr.firstChild);
      }
      for(var i=asset.colors.length-1; i >=0; i--) {
        var color = asset.colors[i];
        
        var td = document.createElement("TD");
        td.innerHTML = '<div class="mp_color_button" style="background-color:' + color + ';"><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?dmode=" + this.mode), {asynchronous:true, evalScripts:true, parameters:Form.serialize("email_data", true),
      onComplete: function D_onComplete() {
        asyncFinish(aKey);
      }
    });
    aKey = asyncStart($("dynamic_popup"));
  },
  
  displayGrid: function D_displayGrid(isOn) {
    this.showGrid = isOn;
    if(this.showGrid) {
      $("hide_grid").style.display="";
      $("show_grid").style.display="none";
    } else {
      $("hide_grid").style.display="none";
      $("show_grid").style.display="";
    }
    this.currentCView.prepareAreas(false);
  },
  
  showPreview: function D_showPreview() {
    this.currentCProduct.showPreview();
  },
  
  setDiscountStructure: function D_setDiscountStructure(id, discounts, discontinued, name) {
    this.discountNames[id] = name;
    if (discontinued) {
      if (this.discounts[id]) {
        delete this.discounts[id];
      }
    } else {
      this.discounts[id] = discounts;
      if(this.defaultDiscountId == null) {
        this.defaultDiscountId = id;
      }
    }
  },
  
  setAffiliateDiscounting: function D_setAffiliateDiscounting(affiliateDiscounting) {
    this.affiliateDiscounting = affiliateDiscounting;
  },
  
  getDiscountPercent: function(discountId, qty) {
    var dis = 0;
    var dt = this.discounts[discountId];
    if(dt == null) {
      log("ERROR: missing discount configuration " + discountId);
      return 0;
    }
    for(var i=0; i < dt.size(); i++) {
      if(qty > dt[i].m) {
        log("found discount level at " + dt[i].m + " discount=" + dt[i].d);
        dis = dt[i].d;
      }
    }
    return dis;
  },
  
  getDiscount: function D_getDiscount(discountId, qty, price, cost) {
    var dis = this.getDiscountPercent(discountId, qty)
    if(dis == 0) {
      return 0;
    }
    //apply qty before rounding because discount is not at the unit level...
    if(this.affiliateDiscounting) {
      log("applying discount of " + dis + "% to " + price + ", qty =" + qty);
      return Math.round(price * dis * qty) / 100.0 ;
    } else {
      log("applying discount of " + dis + "% to " + cost + ", qty =" + qty);
      return Math.round(cost * dis * qty) / 100.0 ;
    }
  },
  
  selectBackgroundColor: function D_selectBackgroundColor(processId) {
    this.currentCViewArea.selectBackgroundColor(processId);
  },
  
  setError: function D_setError(cProductId,views, errors) {
    cProductId = this.getConfiguredProductId(cProductId);
    //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";
    }
  },
  
  getAreaUnit: function D_getAreaUnit() {
    switch (this.lengthUnit)
    {
    case 1:
      return "sq inch";
    case 2:
      return "sq 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;
    }
  },
  
  convertAreaFromSqInches: function D_convertAreaFromSqInches(a) {
    switch (this.lengthUnit)
    {
    case 1:
      return a;
    case 2:
      return a * 2.54 * 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 = ["emb"].concat(this.nonEmbFormats); // ["jpg", "jpeg", "png" , "gif", "emb", "svg", "eps"];
        } else {
          allowed = ["emb"];
        }
      } else {
        allowed = this.nonEmbFormats;
      }
    }
    return allowed;
  },
  
  setNonEmbFormats: function(formats) {
    this.nonEmbFormats = formats;
  },
  
  //load the popup asking what to add....
  showAddItemIntro: function() {

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

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


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

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

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

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

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


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


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

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

var DFLAG_SHOW_ROTATE_DEGREES = DFLAG_BASE << 7;

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

var DFLAG_SHOW_COPY_ITEM = DFLAG_BASE << 16;


var DFLAG_WARN_IF_OUTSIDE = DFLAG_BASE << 17;

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

var DFLAG_SHOW_ADD_ITEM_INTRO = DFLAG_BASE << 21;

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

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

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

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

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

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


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

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

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

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

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

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

function toHex(c) {
	var s = c.toString(16).toUpperCase();
	if(s.length == 1)
		return "0" + s;
	return s;
}

function isHtmlColorString(val, checkHash) {
	if(val == null) return false;
	if(checkHash) { //has is mandatory..
		if(val.substring(0,1) != "#") {
			log("isHtmlColorString:false (color does not start with #)");
			return false;
		}
		val = val.substring(1, val.length); //remove the #
	} else { //hash is optional
		if(val.substring(0,1) == "#") {
			//log("isHtmlColorString: stripping leading #");
			val = val.substring(1, val.length); //remove the #
		}
	}
	if((val != null)&&(val.length == 6)) {
		for(var i=0; i < 6; i++) {
			var cc = val.charCodeAt(i);
			if(!( ((cc >=48)&&(cc <=57)) || ((cc>=65)&&(cc<=70)) || ((cc>=97)&&(cc<=102)) )) {
				log("isHtmlColorString:false (char code " + cc + " at " + i + " invalid)");
				return false;
			}
		}
		return true;
	} else {
		log("isHtmlColorString:false (length (" + val.length + ") != 6)");
		return false;
	}
}

var PikiColor = Class.create({
  CLASSDEF: {
      name: 'PikiColor'
  },
  
  initialize: function(options, id) {
    this.id = id;
    if(options == null || options=="") {
      this.deSerialize("transparent");
    } else if(typeof options == 'string') {
      this.deSerialize(options);
    } else {
      this.type = options.type;
      this.r = parseInt(options.r, 10);
      this.g = parseInt(options.g, 10);
      this.b = parseInt(options.b, 10);
      this.c = parseInt(options.c, 10);
      this.m = parseInt(options.m, 10);
      this.y = parseInt(options.y, 10);
      this.k = parseInt(options.k, 10);
      this.name = options.n == null ? options.name : options.n;
      this.cid = options.cid;
      this.tint = options.tint;
      this.checkValues();
    }
  },
  
  htmlColor: function() {
    //if(this.type == "rgb") {
        return "#" + toHex(this.r) + toHex(this.g) + toHex(this.b);
    //}
    //return "#777777";
  },
  
  serialize: function() {
    return encodeURIComponent(this.type + "&" + this.r + "&" + this.g + "&" + this.b + "&" + this.c + "&" + this.m + "&" + this.y + "&" + this.k + "&" + encodeURIComponent(this.name) + "&" + this.cid + "&" + this.tint);   
  },
  
  deSerialize: function(val) {
    if(val == "transparent" || val == "Transparent") {
      this.type = "transparent";
      this.r = 0;
      this.g = 0;
      this.b = 0;
      this.name = "transparent";
      this.cid = "";
    } else if(isHtmlColorString(val, false)) {
      //old style... just a html string....  
      var idx = 0;
      if(val.substr(0,1) == "#") {
        idx = 1;
      }
      this.type = 'rgb';
      this.r = parseInt(val.substr(idx,2), 16);
      this.g = parseInt(val.substr(idx+2,2), 16);
      this.b = parseInt(val.substr(idx+4,2), 16);
      this.name = "";
      this.cid = "";
      
    } else {
      var decoded =  decodeURIComponent(val);
      var els = decoded.split('&');
      this.type = els[0];
      this.r = parseInt(els[1], 10);
      this.g = parseInt(els[2], 10);
      this.b = parseInt(els[3], 10);
      this.c = parseInt(els[4], 10);
      this.m = parseInt(els[5], 10);
      this.y = parseInt(els[6], 10);
      this.k = parseInt(els[7], 10);
      this.name = decodeURIComponent(els[8]);
      this.cid = els[9];
      this.tint = els[10];
    }
    this.checkValues();
  },
  
  convertToCMYK: function() {
    //normalise to 0 - 1
    var r = parseFloat(this.r) / 255.0;
    var g = parseFloat(this.g) / 255.0;
    var b = parseFloat(this.b) / 255.0;
    
    
    var k = this.minf(this.minf(1-r,1-g) ,1-b);
    var c = k==1 ? 0 : (1-r-k)/(1-k);
    var m = k==1 ? 0 : (1-g-k)/(1-k);
    var y = k==1 ? 0 : (1-b-k)/(1-k);
    
    
    this.c = parseInt(c * 100.0);
    this.m = parseInt(m * 100.0);
    this.y = parseInt(y * 100.0);
    this.k = parseInt(k * 100.0);
  },
  
  clip01: function(amt) {
    if(amt > 1.0) return 1.0;
    return amt;  
  },

  convertToRGB: function() {
    
    var nc = parseFloat(this.c) / 100.0;
    var nm = parseFloat(this.m) / 100.0;
    var ny = parseFloat(this.y) / 100.0;
    var nk = parseFloat(this.k) / 100.0;
    
    
    //double c, m, y, aw, ac, am, ay, ar, ag, ab;
  
    var c = this.clip01( nc + nk );
    var m = this.clip01( nm + nk );
    var y = this.clip01( ny + nk );
    var aw = (1-c) * (1-m) * (1-y);
    var ac = c * (1-m) * (1-y);
    var am = (1-c) * m * (1-y);
    var ay = (1-c) * (1-m) * y;
    var ar = (1-c) * m * y;
    var ag = c * (1-m) * y;
    var ab = c * m * (1-y);
    
    r = this.clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar);
    g = this.clip01(aw + 0.6196*ac + ay + 0.5176*ag);
    b = this.clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag + 0.4863*ab);
    
  
    this.r = parseInt(r * 255.0, 10);
    this.g = parseInt(g * 255.0, 10);
    this.b = parseInt(b * 255.0, 10);
	/*
    //normalise to 0 - 1
    var c = parseFloat(this.c) / 100.0;
    var m = parseFloat(this.m) / 100.0;
    var y = parseFloat(this.y) / 100.0;
    var k = parseFloat(this.k) / 100.0;
  
    var r = 1.0 - minf( 1.0, c*(1-k)+k);
    var g = 1.0 - minf( 1.0, m*(1-k)+k);
    var b = 1.0 - minf( 1.0, y*(1-k)+k);
  
    this.r = parseInt(r * 255.0);
    this.g = parseInt(g * 255.0);
    this.b = parseInt(b * 255.0);
    */
  },

  minf: function(l,r) {
    if(l < r)
      return l;
    return r;
  },
  
  checkValues: function() {
    if(this.r == null) this.convertToRGB();
    if(this.c == null) this.convertToCMYK();
    if(this.cid == null) this.cid = "";
    if(this.name == null) this.name = "";
    if(this.tint == null) this.tint = "";
  }
});


// 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 existing 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) {
    if(options.urlFunction) {
      options.url = options.urlFunction();
    }
    log("Running Ajax Queue Item for " + options.url);
    this.running = options;
    var self = this;
    var onComplete = options.options.onComplete;
    //override the onComplete to queue requests
    options.options.onComplete = function(transport, jsonResult) {
      if(onComplete != null) { //call the original onComplete
        onComplete(transport, jsonResult);
      }
      self.running = null;
      self.runNext();
    };
    
    if(options.parameters != null) { //we have a function which makes the params...
      options.options.parameters = options.parameters();
    }
    if(options.mode == 0) { //request
      var req = new Ajax.Request(d.ajaxUrl(options.url), options.options);
    } else if(options.mode == 1) { //updater
      var req = new Ajax.Updater(options.target, d.ajaxUrl(options.url), options.options);
    }
  },
  
  runNext: function AQ_runNext() {
    var next = this.queue.shift();
    if(next != null) {
      log("Running Next Ajax Queue Item");
      this.run(next);
    }
  }
});

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

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

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

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

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


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


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

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

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


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

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


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

var Item = Class.create({
  CLASSDEF: {
      name: 'Item'
  },
  
  initialize: function Item_initialize(id, cViewProcess, asset, options) {
    this.id = id;
    this.asset = asset;
    this.options = options;
    this.enabled = true;
    this.setLocked(dopt(options, "lk", "false") == "true");
    this.allowPersonalization = (dopt(options, "per", "0") == "1");
    
    
    this.cViewProcess = cViewProcess;
    this.cViewArea = cViewProcess.configuredViewArea;
    this.cView = this.cViewArea.configuredView;
    this.ready = false;
    
    this.isWilcomEMB = (options.emb == "1");
    //if(!d.inOrderManager) {
      this.isSavedDigitized = (this.asset.digitizedState > 1);
    //}
    this.stitchCount = options.stc;
    
    this.elId = this.cView.configuredProduct.id + "_" + this.id;
    
    this.reRender = false;
    this.renderVersion = (options.rv == null) ? 0 : options.rv;
    
    this.templateAssetId = options.taid;
    
    this.allowHandleResizing = true;
    
    this.useCanvas = useCanvas;
    
    this.managePaneVisible = true;
    if(options.z) {
      this.zIndex = options.z;
    } else {
      this.zIndex = this.cViewProcess.getNextZOrder();
    }
    
    if((this.options.l != null)&&(this.options.t != null)&&(this.options.w != null)&&(this.options.h != null)) { //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);
      this.originalWidth = this.width;
      this.originalHeight = this.height;
      this.originalLeft = this.left;
      this.originalTop = this.top;
    } else if((!this.startPosition(this.options))||((this.options.w != null)&&(this.options.h != null))) { //the derived object doesnt have special start positon logic (Text) or it alreeady has a width/height (copy text)
      var w =  this.cViewProcess.productProcess.fullWidth;
      var h = this.cViewProcess.productProcess.fullHeight;
      if(this.options.w == null) {
        if((!this.isPlaceholder()) && ((this.isWilcomEMB)||(this.isSavedDigitized)) ) {
          this.height = parseInt(this.asset.height, 10);
          this.width = parseInt(this.asset.width, 10);
        } else {
          //auto position
          //by default this will position itself in the largest centered position allowed.
          log("rW=" + w + ", rH=" + h);
          var dims = this.asset.scale(h,w);
          dims = this.minDims(dims);
          this.maintainAspect = true;
          this.height = parseInt(dims.h, 10);
          this.width = parseInt(dims.w, 10);
        }
      } else {
        //does have size data (no position)
        this.height = parseFloat(this.options.h);
        this.width = parseFloat(this.options.w);
      }
      if(this.options.t == null) {
        this.top = (h - this.height) / 2;
        this.left = (w - this.width) / 2;
      } else {
        this.top = this.options.t;
        this.left = this.options.l;
      }
      this.originalWidth = this.width;
      this.originalHeight = this.height;
      this.originalLeft = this.left;
      this.originalTop = this.top;
    }
    
    this.deleteClickEvent = this.deleteClick.bindAsEventListener(this);
    this.zDownClickEvent = this.zDownClick.bindAsEventListener(this);
    this.zUpClickEvent = this.zUpClick.bindAsEventListener(this);
    
    this.eventResizeStart =  this.resizeStart.bindAsEventListener(this);
    this.eventResizeMouseUp =  this.resizeFinished.bindAsEventListener(this);
    this.eventResizeMouseMove =  this.resizeMove.bindAsEventListener(this);
    
    this.eventRotatorStart = this.rotatorStart.bindAsEventListener(this);
    this.eventRotatorMouseUp = this.rotatorFinished.bindAsEventListener(this);
    this.eventRotatorMouseMove =  this.rotatorMove.bindAsEventListener(this);
    
    this.eventMouseDown =  this.mouseDown.bindAsEventListener(this);
    
    this.eventLockToggle = this.lockToggle.bindAsEventListener(this);
    this.eventPersonalizationToggle = this.personalizationToggle.bindAsEventListener(this);
    
    this.isLastZ = false;
    this.isFirstZ = false;
    this.qualityOK = true;
    
    if(options.ar == null) {
      if( (this.width / this.height) ==  this.asset.aspectRatio) {
        this.maintainAspect = true;
      } else {
        this.maintainAspect = false;
      }
    } else {
      this.maintainAspect = (options.ar == "1");
    }
    this.allowResize = true;
    
    this.alerts = [{},{},{}]; //array of alerts: error/warning/notice
    
    this.canModifyDesign = this.cViewProcess.productProcess.canModifyDesign();
  },
  
  compare: function Item_compare(other) {
    return this.zIndex - other.zIndex;
  },
  
  beforeAddNew: function Item_beforeAddNew() {
    return false;
  },
  
  continueAdd: function Item_continueAdd() {
    
  },
  
  isPlaceholder: function() {
    return false;
  },
  
  isTemplateItem: function() {
    if(this.asset != null) {
      return (this.asset.type >= 200 && this.asset.type < 203);  
    }
    return false;
  },
  
  initialiseManagePane: function Item_initialiseManagePane() {
    var self = this;
    log("Item.initialiseManagePane()");
    if ($('mp1_' + this.elId)) $('mp1_' + this.elId).remove();
    this.cViewProcess.buildManagePane(this);
    this.managePane = $("mp1_" + this.elId);
    this.titleThumb = $("mp1_" + this.elId + "_stimg");
    
    if(!this.managePaneVisible) {
      this.managePane.hide();
    }
    
    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;
    
    this.perContainer = $("mp1_" + this.elId + "_per_container");
    this.perCheckBox = $("mp1_" + this.elId + "_personalize");
    this.perCaptionContainer = $("mp1_" + this.elId + "_personalize_caption_container");
    this.perCaptionText = $("mp1_" + this.elId + "_personalize_caption");
   
    
    if(this.locked) {
      this.lockIcon.style.visibility="visible";
      if(this.perContainer != null) this.perContainer.hide();
    } else {
      this.lockIcon.style.visibility="hidden";
      if(this.perContainer != null) this.perContainer.show();
    }
	
    
    Event.observe(this.lockCheckBox, "click", this.eventLockToggle);
    
    
    if(this.perCheckBox != null) {
      this.perCheckBox.checked = this.allowPersonalization;
       Event.observe(this.perCheckBox, "click", this.eventPersonalizationToggle);
    }
    
    
	
    
    
    
    if (d.mode == DESIGNER_MODE_CONFIGURE) {
      this.lockContainer.show();
      
      if((this.perContainer != null)&&(d.allowPersonalization)) {
        this.perContainer.show();
        this.perCaptionText.value = this.perCaption;
        if(this.perCheckBox.checked) {
          this.perCaptionContainer.show();
        }
        if(this.locked) {
          this.perContainer.hide();
        } else {
          this.perContainer.show();
        }
      }
    } else {
      this.lockContainer.hide();
      this.lockIcon.style.display="none";
      if(this.perContainer != null) {
        this.perContainer.hide();
        this.perCaptionText.value = this.perCaption;
      }
    }
    
    
    
    
    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);
  },
  
  
  initialiseTemplatePane: function Item_initialiseTemplatePane() {
    var self = this;
    log("Item.initialiseTemplatePane()");
    this.cViewProcess.buildTemplatePane(this);
    this.managePane = $("mp1_" + this.elId);
    if(!this.managePaneVisible) {
      this.managePane.hide();
    }
    
    if(d.templateMode == TEMPLATE_MODE_CP) {
      var suffix = (this.cView.id == this.cView.configuredProduct.firstViewId) ? "" : " (" + this.cView.productView.name + ")";
      
      $("mp_" + this.elId + "_caption").update(this.perCaption + suffix);
    } else if(d.templateMode == TEMPLATE_MODE_DESIGN) {
      $("mp_" + this.elId + "_caption").hide();
    }
    
    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]);
      }
    }
  },
  
  setManagePaneVisibility: function(vis) {
    this.managePaneVisible = vis;
    if(this.managePane != null) {
      if(vis) {
        this.managePane.show();
      } else {
        this.managePane.hide();
      }
    }
  },
  
  setName: function Item_setName(name) {
    if((name!=null)&&(name.length > 15)) {
      this.name = name.substring(0,13) + "...";
    } else {
      this.name = name;
    }
    
    if(this.titleSpan != null) {
      this.titleSpan.innerHTML = this.name;
    }
  },
  
  addToDesigner: function Item_addToDesigner(doSelect) {
    this.el = this.buildElement();
    log("built el");
    var self = this;
    
    
    log("inited drag");
      
    if((this.options.l)||(this.options.t)||(this.options.w)||(this.options.h)) { //options contains positioning data
      log("options contains positioning data: setPosition()");
      log(this.options);
      log("w=" + this.width + ", h=" + this.height);
      this.setPosition();
      log("set Pos Done");
    } else if(!this.startPosition(this.options)) {
      
      log("set Pos");
      this.setPosition();
      log("set Pos Done");
    }
    var self = this;
    if(d.mode!=DESIGNER_MODE_TEMPLATE) {
      this.el.onmousemove = function(ev) { self.rollover(); };
      this.el.onmouseout = function(ev) { self.leave(); };
      Event.observe(this.el, "mousedown", this.eventMouseDown);
    }
    this.positionType = this.getItemPositioning();
    this.showEL();
    if(d.mode!=DESIGNER_MODE_TEMPLATE) {
      if(doSelect) {
        this.select();
      }
      
      this.enableDragging();
    }
  },
  
  enableDragging: function Item_enableDragging() {
    if(d.mode==DESIGNER_MODE_TEMPLATE) return;
    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.getManagePaneContainer(this.itemType).insertBefore(this.managePane, cViewProcess.getManagePaneContainer(this.itemType).firstChild);
      //cViewProcess.managePane.appendChild(this.managePane); -- we want to prepend as top (first) is on top...
    }
    this.cViewProcess = cViewProcess;
    this.cViewArea = cViewProcess.configuredViewArea;
    this.cView = this.cViewArea.configuredView;
    
    log("moveToDesigner: before move: this.width=" + this.width + " this.height=" + this.height + " this.left=" + this.left + " this.top=" + this.top);
    
    this.width *= scale;
    this.height *= scale;
    
    if(this.zIndex == null) this.zIndex = this.cViewProcess.getNextZOrder();
    
  /*  this.left = this.left * oldDScale;
    log("left in design scale=" + this.left);
    this.left -= oldLeft;
    log("left after old left removed=" + this.left);
    this.left /= resizeScale;
    log("left after rescaled=" + this.left);
    this.left += offsetLeft;
    log("left after offset (" + offsetLeft + ") added=" + this.left);
    this.left /=  this.cViewProcess.productProcess.designScale;
    log("left after back in full scale=" + this.left);
    */
    
    this.left = ((((this.left * oldDScale) - oldLeft) / resizeScale) + offsetLeft) / this.cViewProcess.productProcess.designScale; //change to designer co-ords, remove old left, rescale, add new offset, put back to full scale 
    
    
    //this.left *= scale + (offsetLeft / this.cViewProcess.productProcess.designScale);
    this.top = ((((this.top * oldDScale) - oldTop) / resizeScale) + offsetTop) / this.cViewProcess.productProcess.designScale; //change to designer co-ords, remove old left, rescale, add new offset, put back to full scale
    //this.top *= scale + (offsetTop / this.cViewProcess.productProcess.designScale);
  
    this.setDirty(true, (resizeScale==1)); //dont queue updates (calling code will do this), use cached version if resizeScale == 1
    log("moveToDesigner: after move: this.width=" + this.width + " this.height=" + this.height + " this.left=" + this.left + " this.top=" + this.top);
  },
  
  startPosition: function Item_startPosition() {
    return false;
  },
   //skipCheck added because this is called when transferring designs between products and the view data is not setup yet
  setPosition: function Item_setPosition(skipCheck) {
    if(this.el==null) {
      return;
    }
    var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
    log(dims);
    this.setHeight(parseInt(dims.h, 10));
    this.setWidth(parseInt(dims.w, 10));
    if(!this.isMoving) {
      this.setTop(parseInt(dims.t, 10));
      this.setLeft(parseInt(dims.l, 10));
    }
    this.sizeChanged();
    if(skipCheck == null || skipCheck == false) {
      this.checkPosition();
    }
  },
  
  setCanvasSize: function(w,h) {
    if(this.useCanvas) {
      this.canvas.setSize( w, h);
      
      if(this.rotated) {
        
        dims = this.getRealDesignerDims();
        
        
        this.canvasImg.attr({width: dims.w, height: dims.h, x:(w - dims.w) / 2,y:(h - dims.h) / 2});
        
        this.canvasImg.rotate(this.rotatedRef.angle, w/2, h/2);
        
        
      } else {
        this.canvasImg.rotate(0, true);
        this.canvasImg.attr({width: w, height: h, x:0,y:0});
      }
      
      
  /*    var hScale = parseFloat(h) / this.canvasImgHeight;
      var wScale = parseFloat(w) / this.canvasImgWidth;
      
      
      
      var xTrans = (parseFloat(w)/2.0); - (this.canvasImgWidth / 2.0);
      var yTrans = (parseFloat(h)/2.0); - (this.canvasImgHeight / 2);
      
      log("setCanvasSize(" + w + "," + h + "): canvasImgWidth=" + this.canvasImgWidth + ", canvasImgHeight=" + this.canvasImgHeight + ", hScale=" + hScale + ",  wScale=" + wScale + ",  xTrans=" + xTrans + ", yTrans=" + yTrans); 
      this.canvasImg.scale(wScale, hScale);
      this.canvasImg.translate(xTrans, yTrans);
  
      this.canvasImgWidth = parseFloat(w);
      this.canvasImgHeight = parseFloat(h);
      */
    }
  },
  
  //used when zooming.. no need to check anything...
  quickSetPosition: function() {
    var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.el.style.height = parseInt(dims.h, 10) + "px";      
    this.el.style.width = parseInt(dims.w, 10) + "px";
    this.el.style.top = parseInt(dims.t, 10) + "px";
    this.el.style.left = parseInt(dims.l, 10) + "px";
    this.setCanvasSize( parseInt(dims.w, 10), parseInt(dims.h, 10));
  },
  
  showPanel: function Item_showPanel() {
    if(this.managePane != null) {
      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";
      if(this.perContainer != null) this.perContainer.hide();
    } else {
      this.setLocked(false);
      this.lockIcon.style.visibility="hidden";
      if(this.perContainer != null) this.perContainer.show();
    }
    
  },
  
  personalizationToggle: function Item_pt(event) {
    this.allowPersonalization = this.perCheckBox.checked;
    if(this.perCheckBox.checked) {
      this.perCaptionContainer.show(); 
    } else {
      this.perCaptionContainer.hide(); 
    }
    this.checkPersonaliseOptions();
  },
  
  checkPersonaliseOptions: function() {
    
  },
  
  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();
    }
    
    //check if the shift or crtl button is pressed
    if(event != null) {
      if(event.ctrlKey || event.altKey || event.shiftKey) {
        this.cViewArea.multiSelectItem(this);
        return;
      }
    }
    
    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(d.mode != DESIGNER_MODE_TEMPLATE) {
        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(d.mode==DESIGNER_MODE_TEMPLATE) return;
    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(d.mode != DESIGNER_MODE_TEMPLATE) {
      if(this.managePane!=null) {
        this.managePane.className = "managepaneoff2";
      }
    }
  },
  
  setMultiSelected: function Item_setMultiSelected(is) {
    this.multiSelected = is;
    if(is) {
      this.addHandles();
    } else {
      this.removeHandles();  
    }
  },
  
  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(useLayoutScale) {
    var scale = useLayoutScale == true ? this.cViewProcess.productProcess.layoutScale : this.cViewProcess.productProcess.designScale;
    return {t: this.top * scale, l:this.left * scale, h:this.height * scale, w:this.width * scale};
  },
  
  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(canvasType) {
    return d.toCanvasDims({w:parseFloat(this.width), h:parseFloat(this.height), l:parseFloat(this.left), t:parseFloat(this.top)},this.cViewProcess.productProcess, canvasType );
  },
  
  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)||(this.multiSelected)) {
      return this.buildReadOnlySizer(cursor, position);
    }
    if(this.sizers == null) {
      //console.debug("new sizer array");
      this.sizers = new Hash();
    }
    var sizer = d.sizer.cloneNode(true);
    sizer.id = d.getNextId();
    sizer.style.cursor = cursor;
    sizer.style.zIndex = 1010;
    sizer.positioning_mode = position;
    document.body.appendChild(sizer);
    this.sizers[position] = sizer;
    Event.observe(sizer, "mousedown", this.eventResizeStart);
    return sizer;
  },
  
  buildReadOnlySizer: function Item_buildReadOnlySizer(cursor, position) {
    if(this.sizers == null) {
      //console.debug("new sizer array");
      this.sizers = new Hash();
    }
    var sizer = d.roSizers[position].cloneNode(true);
    sizer.id = d.getNextId();
    sizer.style.zIndex = 1010;
    sizer.positioning_mode = position;
    document.body.appendChild(sizer);
    this.sizers[position] = sizer;
    return sizer;
  },
  
  buildRotator: function Item_buildSizer(cursor, position) {
    if(this.sizers == null) {
      //console.debug("new sizer array");
      this.sizers = new Hash();
    }
    var sizer = d.rotator.cloneNode(true);
    sizer.id = d.getNextId();
    sizer.style.cursor = cursor;
    sizer.style.zIndex = 1010;
    sizer.positioning_mode = position;
    document.body.appendChild(sizer);
    this.sizers[position] = sizer;
    Event.observe(sizer, "mousedown", this.eventRotatorStart);
    return sizer;
  },
  
  resizeStart: function Item_resizeStart(event) {
    if(this.handleResizeStart(event)) {
      
    } else {
      this.currentSizer = Event.element(event);
      log("resizeStart for " + this.currentSizer.positioning_mode);
      
      Event.observe(document, "mouseup", this.eventResizeMouseUp);
      Event.observe(document, "mousemove", this.eventResizeMouseMove);
      Event.stop(event);
    }
    return false;
  },
  
  handleResizeStart: function Item_handleResizeStart(event) {
    return false;
  },
  
  resizeMove: function Item_resizeMove(event) {
    //log("resizeMove");
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) {
      return false;
    }
    this._lastPointer = pointer;
    
    
    var pl = pointer[0]-PPSizerHalf;
    var pt = pointer[1]-PPSizerHalf;
    
    
    
    var pm = this.currentSizer.positioning_mode;
    
    var dims = this.cViewArea.abs(this.getDims());
    var t = dims.t;
    var l = dims.l;
    var h = dims.h;
    var w = dims.w;
    
    var ot = t;
    var ol = l;
    var oh = h;
    var ow = w;
    
    if(pm == "BR") {
      h = pointer[1] - t;
      w = pointer[0] - l;
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        pt = t + h - PPSizerHalf;
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        pl = l + w - PPSizerHalf;
      }
      this.repositionHandles("BR",t,l,h,w);
    } else if(pm == "BL") {
      h = pointer[1] - t;
      w = w + (l - pointer[0]);
      l = pointer[0];
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        pt = t + h - PPSizerHalf;
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        l = (ol + ow) - w;
        pl = l - PPSizerHalf;
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      } else {
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      }
      this.repositionHandles("BL",t,l,h,w);
    }  else if(pm == "TL") {
      h = h + (t - pointer[1]);
      w = w + (l - pointer[0]);
      t = pointer[1];
      l = pointer[0];
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        t = (ot + oh) - h;
        pt = t - PPSizerHalf;
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      } else {
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        l = (ol + ow) - w;
        pl = l - PPSizerHalf;
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      } else {
        this.el.style.left = (l - this.cViewArea.canvasX) + "px";
      }
      this.repositionHandles("TL",t,l,h,w);
    } else if(pm == "TR") {
      h = h + (t - pointer[1]);
      w = pointer[0] - l;
      t = pointer[1];
      if(!this.setHeight(h)) {
        h = this.getDims().h;
        t = (ot + oh) - h;
        pt = t - PPSizerHalf;
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      } else {
        this.el.style.top = (t - this.cViewArea.canvasY) + "px";
      }
      if(!this.setWidth(w)) {
        w = this.getDims().w;
        pl = l + w - PPSizerHalf;
      }
      this.repositionHandles("TR",t,l,h,w);
    }
    
    this.currentSizer.style.left = pl + "px";
    this.currentSizer.style.top = pt + "px";
    
    this.itemSized();
    
   
    return false;
  },
  
  itemSized: function() {
   if(this.cViewProcess != null) {
      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();
    }
  },
  
  resizeFinished:  function(event, fromMulti) {
    log("resizeFinished");
    if(!fromMulti) {
      this.releaseResizing();
    }
    
    var dims = d.fromCanvasDims(this.getDims(),this.cViewProcess.productProcess);
    this.width = dims.w;
    this.height = dims.h;
    this.top = dims.t;
    this.left = dims.l;
    this.sizeChanged();
    this.checkPosition();
    //d.itemChanged(true);
    this.setDirty();
    d.selectTab('m','customize'); 
    //d.expandManage();
  },
  
  releaseResizing: function Item_releaseResizing() {
    this._lastPointer = null;
    this.currentSizer = null;
    Event.stopObserving(document, "mouseup", this.eventResizeMouseUp);
    Event.stopObserving(document, "mousemove", this.eventResizeMouseMove);
  },
  
  
  rotatorStart: function Item_rotatorStart(event) {
    log("rotatorStart");
    Event.observe(document, "mouseup", this.eventRotatorMouseUp);
    Event.observe(document, "mousemove", this.eventRotatorMouseMove);
    Event.stop(event);
    
    var x = Event.pointerX(event);
    var y = Event.pointerY(event);
    
    var el = Event.element(event);
    
    this.rotElement = $(el.cloneNode(true));
    this.rotElement.id = d.getNextId();
    document.body.appendChild(this.rotElement);
    var elPos = Position.cumulativeOffset(this.rotElement);
    
    this.rotOffsetX = x - elPos[0];
    this.rotOffsetY = y - elPos[1];
    
   
    this.rotationOffset = this.getRotation() - this.getRotationAngle(x,y);
    
    
    
    
    return false;
  },

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

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

    
    
    //first get size of the sides

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

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

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

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

    //console.debug(dims);
    
    var sizer = this.buildSizer("nw-resize","BR");
    
    
    this.positionHandle(sizer,"BR",t,l,h,w);
    sizer.style.display="";
    
    
    sizer = this.buildSizer("ne-resize","BL");
    this.positionHandle(sizer,"BL",t,l,h,w);
    sizer.style.display="";
    
    sizer = this.buildSizer("nw-resize","TL");
    this.positionHandle(sizer,"TL",t,l,h,w);
    sizer.style.display="";
    
    sizer = this.buildSizer("ne-resize","TR");
    this.positionHandle(sizer,"TR",t,l,h,w);
    sizer.style.display="";
    
    if((this.useCanvas)&&(!this.noRotation)&&(d.mode != DESIGNER_MODE_TEMPLATE)) {
      if((this.allowResize)&&(!this.multiSelected)) { // (this.allowHandleResizing)&& would stop text getting rotator
        sizer = this.buildRotator("pointer","ROT");
        this.positionHandle(sizer,"ROT",t,l,h,w);
        sizer.style.display="";
      }
    }
    
    this.hasHandles = true;
    
  },
  
  removeHandles: function Item_removeHandles() {
    if(this.sizers!=null) {
      var self = this;
      this.sizers.each(function(pair) {
        if(pair.value!=null) {
          Event.stopObserving(pair.value, "mousedown", self.eventResizeStart);
          document.body.removeChild(pair.value);
        }
      });
      this.sizers = null;
    }
    this.hasHandles = false;
  },
  
  positionHandle: function Item_positionHandle(sizer, pos, t, l, h, w) {
    try {
      if(pos=="BR") {
        sizer.style.left = (l + w - PPSizerHalf) + "px";
        sizer.style.top = (t + h - PPSizerHalf) + "px";
      } else if(pos=="TL") {
        sizer.style.left = (l - PPSizerHalf) + "px";
        sizer.style.top = (t - PPSizerHalf) + "px";
      } else if(pos=="TR") {
        sizer.style.left = (l + w - PPSizerHalf) + "px";
        sizer.style.top = (t- PPSizerHalf) + "px";
      } else if(pos=="BL") {
        sizer.style.left = (l - PPSizerHalf) + "px";
        sizer.style.top = (t + h - PPSizerHalf) + "px";
      } else if(pos=="ROT") {
        sizer.style.left = (l + w + 10 + PPSizerHalf) + "px";
        sizer.style.top = (t - PPSizerHalf - 10 ) + "px";
      } else {
        log("Unknown position:" + pos);
      }
    } catch(e) {}
  },
         
  repositionHandles: function Item_repositionHandles(exclude, t,l,h,w) {
    var self = this;
    if(this.sizers==null) {
      this.addHandles();
    }
    this.sizers.each(function(pair) {
      if(pair.key != exclude) {
        //console.debug(key);
        self.positionHandle(pair.value, pair.key, t,l,h,w);
      }
    });
  },
  
  autoRepositionHandles: function Item_autoRepositionHandles() {
    //var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
    //dims = this.cViewArea.abs(dims);
    var dims = this.cViewArea.abs(this.getDims());
    this.repositionHandles("",dims.t, dims.l, dims.h, dims.w);
  },
  
  sizeChanged: function Item_sizeChanged() {
    this.updateTitleSize();
  },
  
  minDims: function Item_minDims(dims) {
    return dims;
  },
  
  //check if we can rescale this item using desiredScale to work with process...
  checkScale: function Item_checkScale(desiredScale, process) {
    return desiredScale; //default behaviour
  },
  
  serializeToOptions: function Item_serializeToOptions(o, excludeRotation) {
    if(o == null) o = {};
    o.id =this.id;
    o.p =this.cViewProcess.id;
    o.rv =this.renderVersion;
    o.lk =this.locked;
    o.w =this.width;
    o.h =this.height;
    o.l =this.left;
    o.t =this.top;
    o.z =this.zIndex;
    o.it =this.asset.getItemType();
    
    if(this.templateAssetId != null) {
      o.taid = this.templateAssetId;
    }
    if(this.allowPersonalization) {
      o.per = "1";
    } else {
      o.per = "0";
    }
    if (d.mode == DESIGNER_MODE_CONFIGURE) {
      if(this.perCaptionText != null) {
        o.perc = this.perCaptionText.value;
      } else {
        o.perc = this.perCaption;
      }
    } else {
      o.perc = this.perCaption;
    }
    
    var aid = this.asset.id;
    if(aid!=null) {
      o.aid =aid;
    }
    
    o.use_canvas = this.useCanvas ? "1" : "0";
    
    
    
    if(this.isWilcomEMB) {
      o.emb=1;
      o.stc=this.stitchCount;
    }
    return o;
  },
  
  serialize: function Item_serialize(queryComponents, prefix, excludeRotation) {
    if(prefix==null) {
      prefix = "t[" + this.id + "]";
    }
    var o = this.serializeToOptions({}, excludeRotation);
    
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    
    serializeObject(queryComponents, prefix, o);
    
    return queryComponents.join('&');
  },
  
  
  //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) {
    if((this.features == null)||(this.features[feature] == null)) return true; //default to true
    return this.features[feature];
  },
  
  getItemSize: function Item_getItemSize() {
    if (this.width != null && this.height != null) {
      var w = 0;
      var h = 0;
      if(this.rotated) {
        var dims = this.getRealDims();
        w = dims.w / this.cViewProcess.productProcess.perfectDPI;
        h = dims.h / this.cViewProcess.productProcess.perfectDPI;
      } else {
        w = this.width / this.cViewProcess.productProcess.perfectDPI;
        h = this.height / this.cViewProcess.productProcess.perfectDPI;
      }
      return [d.convertLengthFromInches(w).toFixed(2), d.convertLengthFromInches(h).toFixed(2)];
    } else return null;
  },
  
  updateTitleSize: function Item_updateTitleSize() {
    if (this.titleSize != null) {
      var size = this.getItemSize();
      if (size) this.titleSize.innerHTML = size[0] + d.getLengthUnit() + " x " + size[1] + d.getLengthUnit();
    }
  },
  
  setFeature: function(feature, enabled) {
    if(this.features == null) this.features = {};
    this.features[feature] = enabled;
    var el = $("mp1_"+this.elId+"_fc_" + feature);
    if(el != null) {
      if(enabled) {
        el.show();
      } else {
        el.hide();
      }
    }
  },
  
  initFeature: function(feature) {
    if((this.features == null)||(this.features[feature] == null)) return; //nothing to do....
    this.setFeature(feature, this.features[feature]);
  },
  
  itemReady: function Item_itemReady() {
    if (!this.ready) {
      if (this.top != null) {
        this.ready = true;
      }
    }
    return this.ready;
  },
  
  itemVisible: function Item_itemVisible() {
    if(!this.managePaneVisible) {
      return false;
    } else if (d.mode != DESIGNER_MODE_CONFIGURE && this.locked) {
      return false;
    } else if(d.inSimpleMode(TEMPLATE_MODE_CP) && !this.allowPersonalization) {
      return false;
    } else {
      return true;
    }
  },
  
  copyTo: function Item_copyTo(destArea) {
    var allowedProcesses = destArea.getAllowedProcesses();
    var passed = 0;
    if(allowedProcesses[this.cViewProcess.id]) {
      var process = destArea.processes[this.cViewProcess.id];
      var initData = this.serializeToOptions();
      log(initData);
      //centered (remove t/l)
      initData.l = null;
      initData.t = null; 
      initData.z = null; //need new zindex
      var newId = destArea.configuredProduct.getNextItemId();
      var asset = this.asset;
      eval("var newItem = new " + this.CLASSDEF.name + "(newId, process, asset, initData);");
      destArea.insertNewItem(newItem);
      passed += 1;
      newItem.select();
      d.itemChanged(true);
    }
    return [passed, 1];
  },
  
  copy: function() {
    var initData = this.serializeToOptions();
    
    var newId = this.cViewArea.configuredProduct.getNextItemId();
    var asset = this.asset;
    eval("var newItem = new " + this.CLASSDEF.name + "(newId, this.cViewProcess, asset, initData);");
    this.cViewArea.insertNewItem(newItem, true);
    return newItem;
  }

});

var ImageItem = Class.create({
  CLASSDEF : {
      name:  'ImageItem',
      parent: Item
  },
  
  initialize: function ImgItem_initialize(id, cViewProcess, asset, options) {
    this.itemType = 0;
    this.itemTypeName = "Image";
    if(asset == null) {
      alert("Unable to get image " + options.aid);
      return;
      //asset = makePlaceholderAsset();
      //loadAssetIntoItem(options.aid, this)
    } 
    
    ImageItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    
    this.perCaption = dopt(options, "perc", ml("Set Image"));
    
    this.allowReplaceClear = dopt(options, "parc", "1");
    
    
    //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.replaceImageEvent = this.replaceImage.bindAsEventListener(this);
    this.clearImageEvent = this.clearImage.bindAsEventListener(this);
    
    
    this.workingAssetLoaded = false;
    
    if(options.c != null) {
      var legacyColors = options.c.split(":");
      this.colors = [];
      for(var i=0; i < legacyColors.length; i++) {
        this.colors.push(new PikiColor(legacyColors[i]));
      }
    } else if(options.colors != null) {
      this.colors = [];
      for(var i=0; i < options.colors.length; i++) {
        this.colors.push(new PikiColor(options.colors[i]));
      }
    } else {
      this.colors = null;
    }
    this.colorCount = (options.cc == null) ?  this.asset.defaultColorCount : options.cc ;
    
    if(options.tc == null) {
      this.transparentColor = new PikiColor("transparent");
    } else {
      this.transparentColor = new PikiColor(options.tc);
    }

    this.rotated = false;
    this.setName(asset.name);
    this.oldTabName = "effects";
    this.currentSingleEffect = null;
    this.effects = options.eff;
    if(this.effects == null) {
      this.effects = {};
    }
    this.effectId = hashFirstKey(this.effects);
    this.effect = hashFirstElement(this.effects);
    
    
    this.borderId = options.b;
    if(this.borderId==null) {
      this.borderId = 0;
    }
    
    var rw = this.width;
    var rh = this.height;
    if(this.options.rw != null && this.options.rh != null) {
      rh = parseFloat(this.options.rh);
      rw = parseFloat(this.options.rw);
    }
    var deg = options.rot == null ? 0 : parseFloat(options.rot);
    this.setReferenceRotation(deg, true, {w:rw, h:rh});
    if(deg != 0) this.rotated = true;
    
    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 || d.inOrderManager))) {
        //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(60, 60);
      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");
    if(this.aspectEl != null) {
      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");
    if(this.rotateContainer != null) {
      this.rotateImg = $("mp1_" + this.elId + "_rotate");
      this.rotateImg.style.cursor = "pointer";
      this.rotateImgLeft = $("mp1_" + this.elId + "_rotate_l");
      this.rotateImgLeft.style.cursor = "pointer";
      this.rotateImgRight = $("mp1_" + this.elId + "_rotate_r");
      this.rotateImgRight.style.cursor = "pointer";
      
      this.rotateImgTxt = $("mp1_" + this.elId + "_rotate_t");
      
      this.rotateWidget = new  RotateWidget(this.rotateContainer, this.rotateImg, this.rotateImgLeft, this.rotateImgRight, this.rotateImgTxt, function(d) { self.rotationChanged(d); }, function(d) { self.rotationChanging(d);});
      
      if(this.options.rot) {
        this.rotateWidget.setValue(parseInt(this.options.rot, 10));
      }
      if(this.getRotation() != 0) {
        this.rotated = true;
        if(this.aspectEl != null) 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");
    if(this.advLink != null) {
      Event.observe(this.advLink, "click", this.advClickEvent);
    }
    
    this.advTable = $("mp1_" + this.elId + "_advt");
    
    
    this.initColors(true);
    
    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 && !d.inOrderManager) {
        $("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);
      }
    }
    if((this.itemType==0) && (d.mode == DESIGNER_MODE_CONFIGURE)) {
      var chkbx = $("mp1_" + this.elId + '_allow_replace_clear');
      if(chkbx != null) {
        chkbx.checked = (this.allowReplaceClear=="1");
        chkbx.onclick = function(el) {
          self.allowReplaceClear = chkbx.checked ? "1" : "0";
        };
        this.checkPersonaliseOptions();
      }
    }
    this.initFeature("size_arrows");
    if(this.itemType==0 && this.isTemplateItem()) {
      //templated assets dont use advanced or select transparent color
      if (d.mode == DESIGNER_MODE_CONFIGURE) {
        //advanced options for locking
        this.setFeature("effects_content", false);
      } else { 
        this.setFeature("adv", false);
        this.advLink.hide();
         
      }
      $("mp1_" + this.elId + "_trans_container").hide();
    } else {
      this.initFeature("adv");
      if((this.itemType==0)&&(d.designerOptions.showImageAdvanced())&&(d.designerOptions.showImageAdvancedOpen())&&(this.allowFeature("adv"))) {
        this.advClick();
      }
    }
    //this.initEBPane();
  },
  
  checkPersonaliseOptions: function() {
    var cont = $("mp1_" + this.elId + '_allow_replace_clear_container');
    if(cont != null) { 
      var colors = this.asset.getColors(this.colorCount);
      if(colors != null && this.allowPersonalization) {
        cont.show();
      } else {
        cont.hide();
      }
    }
    
  },
  
  initialiseTemplatePane: function ImgItem_initialiseTemplatePane() {
    log("ImageItem.initialiseTemplatePane()");
    var self = this;
    ImageItem.parentClass.method("initialiseTemplatePane").call(this);
    // bind objects
    
    
    this.thumb = $("mp_" + this.elId + "_img");
    if(this.thumb!=null) {
      var sizes = this.asset.scale(60, 60);
      setTransPng(this.thumb, this.asset.getSUrl(), parseInt(sizes.w, 10), parseInt(sizes.h, 10));
    }
    if(this.itemType == 0) {
      if(this.allowReplaceClear == "1") {
        Event.observe($("mp_" + this.elId + '_ri'), "click", this.replaceImageEvent);
        Event.observe($("mp_" + this.elId + '_a'), "click", this.replaceImageEvent);
      
        Event.observe($("mp_" + this.elId + '_ci'), "click", this.clearImageEvent);
      } else {
        $("mp_" + this.elId + '_ri_container').className = "hidden";
        $("mp_" + this.elId + '_ci_container').className = "hidden";
        $("mp_" + this.elId + '_a').onclick= function() { return false; };
      }
      
      this.initColors(false);
    }
  },
  
  initEBPane: function ImgItem_initEBPane() {
    var div = $("i_effects_list_" + this.elId);
    if(div!= null) {
      var html = '<ul>';
      
      cell = '<li class="et_effect" align="center" valign="top" id="i_ef_' + this.elId + '_-1" onmouseover="ieOMO(this);" onmouseout="ieOMOu(this);"  onclick="ieOMc(' + this.id + ',-1);"><img src="/images/noborder.gif"/>';
      cell += "<label>No Effect</label>";
      cell += '</li>';
      
      html += cell;
        
        
      for(var i=0;i< imageEffects.length;i++) {
        var effect = imageEffects[i];
        var sel = '';
        if(this.effectId == effect[0]) {
          sel = ' selected';
        }
        cell = '<li class="et_effect' + sel + '" align="center" valign="top" id="i_ef_' + this.elId + '_' + effect[0] + '" onmouseover="ieOMO(this);" onmouseout="ieOMOu(this);"  onclick="ieOMc(' + this.id + ',' + i + ');"><img src="/ppr/images/effects/' + effect[2] + '"/>';
        cell += "<label>"+effect[1]+"</label>";
        cell += '</li>';
        
        html += cell;
      }
      html += "";
      
      div.innerHTML = html;
      
      d.effectIconsLoaded = true;
    
    
    }
  },
  
  initColors: function ImgItem_initColors(allowTransparent) {
    var colors = this.asset.getColors(this.colorCount);
    var allowChangeColor = true;
    if(this.isSavedDigitized && !d.inOrderManager) {
      allowChangeColor = false;
    } else if(this.digitize) {
      if(this.digitizedAsset.primaryItem() != this) {
        allowChangeColor = false;
      }
    }
    
    var colorContainer = $("mp1_" + this.elId + "_color_row");
    if(colorContainer != null) colorContainer.hide();
    
    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(new PikiColor(color));
          } else if((this.colors[i] == null)||(this.colors[i] == "")) {
            this.colors[i] = new PikiColor(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.type == "transparent") {
        	td.innerHTML = '<div class="' + buttonClass + '" style="background-attachment: initial; background-origin: initial; background-clip: initial; background-image: url(/ppr/images/trans-display-small.gif); background-position: initial initial; background-repeat: initial initial;">&nbsp;</div>';
        } else {
          td.innerHTML = '<div class="' + buttonClass + '" style="background-color:' + color.htmlColor() + ';">&nbsp;</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").show();
    } else if(allowTransparent) {
      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.type=="transparent") {
          this.transColorButton.style.backgroundColor = "";
          this.transColorButton.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
        } else {
          this.transColorButton.style.backgroundColor = this.transparentColor.htmlColor();
          this.transColorButton.style.backgroundImage = "";
        }
      
        if(allowChangeColor) {
          var self = this;
          this.transColorButton.onclick =function() { 
            pwColorPicker.selectColor(null, self.transColorButton, this.transparentColor,  function(c, co) { self.colorCallback(c, co, 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, co) { self.colorCallback(c, co, 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, colorObject, 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 = new PikiColor(colorObject);
      if(this.digitize && !this.splitColors) {
        this.asset.getOpacity(color, function(percent) {
            this.percentOpaque = parseFloat(percent);
            this.updateDigitizationNotice(true,true);
        }.bind(this));
      }
      dnRunCallback("designer.imageitem.set_transparent_color", { item: this});
    } else {
      this.colors[idx] = new PikiColor(colorObject);
      dnRunCallback("designer.imageitem.set_color", { item: this});
    }

    this.updateImageSrc();
    this.setDirty();
    this.updateDigitizationNotice(true,true);
    if(this.digitizedAsset != null) {
      this.digitizedAsset.changed(this,DIG_EVENT_COLOR);
    }
    //d.itemChanged(true);
  },
  
  
  buildElement: function ImgItem_buildElement() {
    if ($("d_" + this.elId)) $("d_" + this.elId).remove();
    if(this.useCanvas) {
      this.el = $(document.createElement("SPAN"));
      this.canvas = Raphael(this.el, 10, 10);
    } else {
      this.el = $(document.createElement("IMG"));
    }
    this.el.id = "d_" + this.elId;
    this.el.style.display = "none";
    this.el.style.position = "absolute";
    if(d.mode != DESIGNER_MODE_TEMPLATE) {
      this.el.style.cursor="pointer";
    }
    var deg = this.getRotation();
    if(this.useCanvas) {
      this.canvasImg = this.canvas.image(this.asset.getUrl(), 0, 0, 10, 10);
      this.canvasImgWidth = 10.0;
      this.canvasImgHeight = 10.0;
      //image-rendering  optimizeQuality
    } else {
      if(deg==0) {
        setTransPng(this.el, this.asset.getUrl());
      } else {
        this.el.src = "/images/trans.gif"; //it looks crap when the image appears unrotated/oversize...
      }
    }
    this.cViewArea.canvas.appendChild(this.el);
    
    //var self = this;
    
    if((this.useCanvas || deg==0)&&(this.colors == null)&&(this.transparentColor == null || this.transparentColor.type == "transparent")&&(this.effect == null)&&(this.borderId == 0)) {
      this.asset.loadWorkingAsset(function() { 
        this.setSrc(this.asset.getWUrl());
        log("loaded working asset");
      }.bind(this));
    } else {
      this.updateImageSrc();
      this.asset.loadWorkingAsset(function() { 
        log("loaded working asset");
      }.bind(this));
    }
    
    this.el.style.zIndex = this.getElementZIndex();
    
    return this.el;
  },
  
  del: function ImgItem_del() {
    if(this.moveButtons != null) {
      for(var i=0; i < this.moveButtons.length; i++) {
        Event.stopObserving(this.moveButtons[i], "click", this.moveClickEvent);
        Event.stopObserving(this.moveButtons[i], "dblclick", this.moveClickEvent);
      }
      Event.stopObserving(this.sizeDown, "click", this.sizeDownClickEvent);
      Event.stopObserving(this.sizeDown, "dblclick", this.sizeDownClickEvent);
      Event.stopObserving(this.sizeUp, "click", this.sizeUpClickEvent);
      Event.stopObserving(this.sizeUp, "dblclick", this.sizeUpClickEvent);
      if(this.aspectEl != null) 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);
      if(this.advLink != null) Event.stopObserving(this.advLink, "click", this.advClickEvent);
    } 
    if(this.rotateWidget!=null) {
      this.rotateWidget.del();
    }
    if(this.digitizedAsset != null) {
      this.digitizedAsset.removeItem(this);
    }
    ImageItem.parentClass.method("del").call(this);
  },
  
  setSrc: function ImgItem_setSrc(url, updateInfo) {
    var self = this;
    var rotCount = this.rotationCounter;
    var rotRef = this.rotatedRef;
    this.currentSrc = url;
    
    if(this.el) {
      var iel = this.useCanvas ? null : this.el;
      setTransparentImage(this.el, iel, url, function() {
          if(this.useCanvas) {
            self.canvasImg.attr({src:url});
          } else {
            if(self.itemType == 0) {
              self.translateRotated(rotCount, rotRef);
              self.checkPosition();
            }
          }
          if(updateInfo != null) {
            self.finishUpdating(updateInfo);
            if(updateInfo.checkCropIssue == true) {
              window.setTimeout( function() {
                  var refreshJoin = "?";
                  if(self.currentSrc.indexOf("?") != -1) {
                    refreshJoin = "&";
                  }
                  var newUrl = self.currentSrc + refreshJoin + "refresh";
                  log("Resetting Src Because of useCroppedImageHack to " + newUrl);
                  if(this.useCanvas) {
                    self.canvasImg.attr({src:newUrl});
                  } else if(useAlphaHack) {
                    $(self.el.id).style.fontSize = "1px";
                    $(self.el.id).style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + newUrl + '", sizingMethod="scale")';
                  } else {
                    $(self.el.id).src = newUrl;
                  }
              }, 1000);
              
            }
          }
      });
    }
  },
  
  getSrc: function ImgItem_getSrc() {
    if(this.currentSrc == null) {
      if(this.cView.configuredProduct.id > 0) {
        return "/image_gen/get_configured_product_asset?cid=" + this.id + "&cid=" + this.cView.configuredProduct.id + "&cp=" + this.cView.configuredProduct.id + "&cpv=" + this.cView.id + "&cpva=" + this.cViewArea.id + "&cpvap=" + this.cViewProcess.id;
      } else {
        return null;
      }
    } else {
      return this.currentSrc;
    }
  },
  
  select: function ImgItem_select(dontEnterCustomisePane, event) {
    ImageItem.parentClass.method("select").call(this, dontEnterCustomisePane,event);
  },
  
  moveClick: function ImgItem_moveClick(event) {
    Event.stop(event);
    var liEl = Event.findElement(event, "li");
    var deltaX = this.cViewProcess.productProcess.fullWidth / 20;
    var deltaY = this.cViewProcess.productProcess.fullHeight / 20;
    
    var vDelta = liEl.className.substr(0,1);
    var hDelta = liEl.className.substr(2,1);
    
    if(vDelta == "t") {
      this.top -= deltaY;
    } else if(vDelta == "b") {
      this.top += deltaY;
    }
    if(hDelta == "l") {
      this.left -= deltaX;
    } else if(hDelta == "r") {
      this.left += deltaX;
    }
    
    var newPositionType = this.getItemPositioning();
    
    if(newPositionType == 1) {
      log("outside!!!");
      this.del();
      return;
    }
      
      
    this.setPosition();
    if(this.selected) {
      this.autoRepositionHandles();
    }
    this.setDirty();
    
  },
  
  sizeUpClick: function ImgItem_sizeUpClick(event) {
    Event.stop(event);
    if(!this.allowResize) {
      return;
    }
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      var hinc = this.rotatedRef.h / 50;
      var maxHeight = this.rotatedRef.h + (10 * hinc);
      if(this.height < maxHeight) {
        this.height += hinc;
        var winc = this.rotatedRef.w / 50;
        this.width += winc;
        this.left -= winc/2;
        this.top -= hinc/2;
      } else {
        log("max height (" + maxHeight + ") reached");
        return;
      }
    } else {
      var dX = this.width / 20;
      var dY = this.height / 20;
      
      this.width += dX;
      this.height += dY;
      this.left -= dX/2;
      this.top -= dY/2;
      if(!this.rotated) { //the aspect ratio could have changed... recalc ref rotation data
        this.setReferenceRotation(0, true);
      }
      this.updateDigitizationNotice(true,true);
    }
    
    
    this.setPosition();
    if(this.selected) {
      this.autoRepositionHandles();
    }
    this.setDirty();
    //d.itemChanged(true);
    if(!this.checkQuality()) {
      d.showQualityWarning();
    }
  },
  
  sizeDownClick: function ImgItem_sizeDownClick(event) {
    Event.stop(event);
    if(!this.allowResize) {
      return;
    }
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      
      
      var hinc = this.rotatedRef.h / 50;
      var minHeight = this.rotatedRef.h - (10 * hinc);
      if(this.height > minHeight) {
        this.height -= hinc;
        var winc = this.rotatedRef.w / 50;
        this.width -= winc;
        this.left += winc/2;
        this.top += hinc/2;
      } else {
        log("min height (" + minHeight + ") reached");
        return;
      }
    } else {
      var dX = this.width / 20;
      var dY = this.height / 20;
      
      this.width -= dX;
      this.height -= dY;
      this.left += dX/2;
      this.top += dY/2;
      if(!this.rotated) { //the aspect ratio could have changed... recalc ref rotation data
        this.setReferenceRotation(0, true);
      }
      this.updateDigitizationNotice(true,true);
    }
    
    
  
    this.setPosition();
    if(this.selected) {
      this.autoRepositionHandles();
    }
    this.setDirty();
    //d.itemChanged(true);
    
  },
  
  aspectClick: function ImgItem_aspectClick(event) {
    this.maintainAspect = this.aspectEl.checked;
    if(this.maintainAspect) {
      var dims = d.toCanvasDims({w:this.width, h:this.height, l:this.left, t:this.top},this.cViewProcess.productProcess );
      this.setHeight(parseInt(dims.h, 10));
      var dims = d.fromCanvasDims(this.getDims(),this.cViewProcess.productProcess);
      this.width = dims.w;
      this.autoRepositionHandles();
      this.updateDigitizationNotice(true,true);
    }
  },
  
  centerClick: function ImgItem_centerClick(event) {
    var w =  this.cViewProcess.productProcess.fullWidth;
    var h = this.cViewProcess.productProcess.fullHeight;
    this.top = (h - this.height) / 2;
    this.left = (w - this.width) / 2;
    this.setPosition();
    this.autoRepositionHandles();
    this.setDirty();
    Event.stop(event);
  },  
  
  centerVClick: function ImgItem_centerVClick(event) {
    var h = this.cViewProcess.productProcess.fullHeight;
    this.top = (h - this.height) / 2;
    this.setPosition();
    this.autoRepositionHandles();
    this.setDirty();
    Event.stop(event);
  },
  
  centerHClick: function ImgItem_centerHClick(event) {
    var w =  this.cViewProcess.productProcess.fullWidth;
    this.left = (w - this.width) / 2;
    this.setPosition();
    this.autoRepositionHandles();
    this.setDirty();
    Event.stop(event);
  },
  
  advClick: function ImgItem_advClick(event) {
    if(this.advTable.style.display=="none") {
      this.initAdvanced();
      this.advTable.style.display="";
	  this.advLink.className="alt";
      //this.advButton.src=d.pathPrefix + "/images/mp/arrow_up.gif";
    } else {
      this.advTable.style.display="none";
	  this.advLink.className=null;
      //this.advButton.src=d.pathPrefix + "/images/mp/arrow_down.gif";
    }
    if(event != null) Event.stop(event);
    return false;
  },
  
  initAdvanced: function ImgItem_initAdvanced() {
    if(!this.advancedInit) {
      this.initEBPane();
      this.advancedInit = true;
    }
  },
  
  rotationChanged: function ImgItem_rotationChanged(deg) {
    
    if(this.rDelay != null) {
      window.clearTimeout(this.rDelay);
    }
    if(this.rotationCounter==null) {
      this.rotationCounter = 1;
    } else {
      this.rotationCounter ++;
    }
    this.setReferenceRotation(deg);
  
    var x = this.left + this.width / 2;
    var y = this.top + this.height / 2;
    
    
    
    log("rotationChanged: w=" + this.rotatedRef.w + "(" + this.width + ")" + " ,h=" + this.rotatedRef.h + "(" + this.height + ") ,deg=" + this.rotatedRef.angle);
    
    this.width = this.rotatedRef.w;
    this.height = this.rotatedRef.h;
    this.left = x - this.rotatedRef.w/2;
    this.top = y - this.rotatedRef.h/2;
    
    //this.allowResize = false; //should this be commented out?
    this.removeHandles();
    this.addHandles();
    if(this.useCanvas) {
      this.setDirty();
    } else {
      var self = this;
      this.rDelay = window.setTimeout( function() { 
        self.updateImageSrc();
        self.setDirty();
      }, 100);
    }
    
    log(deg);
    if(deg != 0) {
      this.rotated = true;
      if(this.aspectEl != null) this.aspectEl.disabled = true;
    } else {
      this.rotated = false;
      if(this.aspectEl != null) this.aspectEl.disabled = this.lockedDigitization();
    }
    this.updateTitleSize();
    this.checkPosition();
  },
  
  rotationChanging: function(deg) {
    if(this.useCanvas) {
      if(this.rotationCounter==null) {
        this.rotationCounter = 1;
      } else {
        this.rotationCounter ++;
      }
      this.setReferenceRotation(deg);
    
      if(deg != 0) {
        this.rotated = true;
        if(this.aspectEl != null) this.aspectEl.disabled = true;
      } else {
        this.rotated = false;
        if(this.aspectEl != null) this.aspectEl.disabled = this.lockedDigitization();
      }
    
      var x = this.left + this.width / 2;
      var y = this.top + this.height / 2;
      
      
      
      //log("rotationChanged: w=" + this.rotatedRef.w + "(" + this.width + ")" + " ,h=" + this.rotatedRef.h + "(" + this.height + ") ,deg=" + this.rotatedRef.angle);
      
      this.width = this.rotatedRef.w;
      this.height = this.rotatedRef.h;
      this.left = x - this.rotatedRef.w/2;
      this.top = y - this.rotatedRef.h/2;
      
      //this.allowResize = false; //should this be commented out?
      this.removeHandles();
      //this.addHandles();
      this.quickSetPosition();
    }
  },
  
  setReferenceRotation: function ImgItem_setReferenceRotation(deg, ignoreFrom, passedDims) {
    var dims = {};
    if(passedDims != null) {
      dims = passedDims;
    } else if((this.rotatedRef != null)&&(ignoreFrom!=true)) {
      dims = this.fromRotatedDims({w: this.width, h:this.height}, this.rotatedRef.angle); //toRotatedDims needs the actual dims, not the bounding box..
      //log("setReferenceRotation (from): w=" + dims.w + "(" + this.width + ")" + " ,h=" + dims.h + "(" + this.height + ") ,deg=" + this.rotatedRef.angle);
      
    } else {//first time called...TODO: handle loading rotated image...
      dims.w = this.width;
      dims.h = this.height;
      //log("setReferenceRotation (init): w=" + dims.w + " ,h=" + dims.h);
    }
    this.rotatedRef = this.toRotatedDims(dims, deg);
    //log("toRotatedDims: w=" + this.rotatedRef.w + " ,h=" + this.rotatedRef.h + " deg=" + deg);
    this.rotatedRef.a = parseFloat(this.rotatedRef.w) / parseFloat(this.rotatedRef.h);
  },
  
  //after a rotation we need to recalc the size...
  translateRotated: function ImgItem_translateRotated(rotCount, rotatedRef) {   
    if((this.rotationCounter == rotCount)&&(this.rotationCounter != null)) {
      this.rotationCounter = null; //only do this once... we need to rotate again for this to get called...
      if(rotatedRef != null) {
        var x = this.left + this.width / 2;
        var y = this.top + this.height / 2;
        
        this.width = rotatedRef.w;
        this.height = rotatedRef.h;
        this.left = x - this.rotatedRef.w/2;
        this.top = y - this.rotatedRef.h/2;
      }
      var dims = this.toCanvasDims(); 
      
      this.setHeight(parseInt(dims.h, 10));
      this.setWidth(parseInt(dims.w, 10));
      this.setTop(parseInt(dims.t, 10));
      this.setLeft(parseInt(dims.l, 10));
      //this.allowResize = true; //should this be commented out?
      if(this.selected) {
        this.removeHandles();
        this.addHandles();
      }
    }
    
  },
  
  //convert the unrotated size to the bounding box of the rotated dims
  toRotatedDims: function ImgItem_toRotatedDims(dims, angle) {
    var pi2 = Math.PI / 2.0;

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

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

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

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

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

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

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

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

    
    return {w:newWidth, h:newHeight, rw:oldWidth, rh:oldHeight, angle:angle, oa:oldWidth/oldHeight  };
  },
  
  //convert the size from the bounding box to the rotated dims (uses hack).. assumes dims are in actual size (not canvas size)
  //this looks at the ratio of the current bounding box to the last stored bounding box and applies that ratio to the real size stored at same time as last stored bounding box
  fromRotatedDims: function ImgItem_fromRotatedDims(dims, angle) {
    //compare the size between this.rotatedRef and size.. use ratio to determine final size..
    if(angle == null) {
      angle = parseFloat(this.getRotation());
    }
    if(this.rotatedRef == null) {
      this.setReferenceRotation(); //make a bounding box from the original asset size..
    }
    //compare ratio of heights...
    var ratio = parseFloat(this.rotatedRef.h) / parseFloat(dims.h);
    return {h: this.rotatedRef.rh / ratio, w: this.rotatedRef.rw / ratio};
    //this.height = this.asset.height / ratio;
    //this.width = this.asset.width / ratio;
  },
  
  //calls above using current dims to get non rotated dims of the object..
  getRealDims: function ImgItem_getRealDims() {
    return this.fromRotatedDims({w:this.width, h:this.height});;
  },
  
  //calls above using current dims to get non rotated dims of the object..
  getRealDesignerDims: function ImgItem_getRealDesignerDims() {
    return this.fromRotatedDims({w:parseFloat(this.el.style.width), h:parseFloat(this.el.style.height)});
  },
  
  updateImageSrc: function ImgItem_updateImageSrc() {
    var deg = this.getRotation();
    if(this.useCanvas) deg=0;
    if((deg==0)&&(this.colors == null)&&(!this.hasEffects())&&(this.borderId==0)&&(this.transparentColor.type=="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?ver=3&id=" + this.asset.id + this.getColorParam() + "&deg=" + deg + "&bid=" + this.borderId + 
        "&tc=" + this.transparentColor.serialize() +cw_t+
        "&" + this.serialiseState("es").join('&') +"&oa=" + encodeURIComponent(oa);
      this.setSrc(url);
      
      //var colors = this.getColorParam();
      //this.setSrc(d.pathPrefix + "/designer/rotate_image?id=" + this.asset.id + "&deg=" + deg + colors);
      
    }
  },
  
  getColorParam: function ImgItem_getColorParam() {
    var colors = "";
    if(this.colors != null) {
      
      
      
      colors = ""; //&colors=";
      for(var i=0;i<this.colors.length;i++) {
        colors += "&colors[]=" + this.colors[i].serialize();
        /*
        if(i != 0) {
          colors +=encodeURIComponent(":");
        }
        colors += encodeURIComponent(this.getIeSafeColorParam(this.colors[i]));*/
      }
      colors += "&cc=" + this.colorCount;
    }
    return colors;
  },
  
 /* getIeSafeColorParam: function ImgItem_getIeSafeColorParam(color) {       
    if((color == null)||(color == "")) {
        return "";
    }
    if(color.substr(0, 1) == "#") {
      return color.substr(1, 6);
    }
    return color;
  },*/
  
  setHeight: function ImgItem_setHeight(height) {
    height = parseFloat(height);
    //log("setHeight:" + height);
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    var elHeight = null;
    var elWidth = null;
    var retVal = true;
    if((this.isWilcomEMB)||(this.isSavedDigitized)) { //check the resize is within 20%
      
      var newHeight = this.fromCanvasDim(height);
      var delta = Math.abs(this.rotatedRef.h - newHeight);
      var twentyPercent = this.rotatedRef.h / 5;
      if(delta > twentyPercent + 1) {
        log("delta (" + delta + ") > twentyPercent (" + twentyPercent + ") changing size...");
        if(newHeight < this.rotatedRef.h) {
          newHeight = this.rotatedRef.h - twentyPercent;
        } else {
          newHeight = this.rotatedRef.h + twentyPercent;
        }
        height = this.toCanvasDim(newHeight);
        elHeight = height;
        //this.el.style.height = height + "px";
        
        var width = parseInt(height * this.rotatedRef.a, 10);
        elWidth = width;
        //this.el.style.width = width + "px";
        retVal=false;
        //return false;
      } else {
        //this.el.style.height = height + "px";
        elHeight = height;
        var width = parseInt(height * this.rotatedRef.a, 10);
        elWidth = width;
        //this.el.style.width = width + "px";
        retVal=true;
        //return true;
      }
    } else {
      //this.el.style.height = height + "px";
      elHeight = height;
      if((this.maintainAspect)||(this.rotated)) {
        var width = parseInt(height * this.rotatedRef.a, 10);
        //this.el.style.width = width + "px";
        elWidth = width;
      }
      retVal=true;
      //return true;
    }
    if(elHeight!= null) {
      this.el.style.height = elHeight + "px";
    } else {
      elHeight = parseInt(this.el.style.height);
    }
    if(elWidth!= null) {
      this.el.style.width = elWidth + "px";
    } else {
      elWidth = parseInt(this.el.style.width);
    }
    this.setCanvasSize(elWidth,elHeight);
    return retVal;
  },
  
  
  setWidth: function ImgItem_setWidth(width) {
    if((this.isWilcomEMB)||(this.isSavedDigitized)) {
      return false;
    } else if((this.maintainAspect)||(this.rotated)) {
      return false;
    }
    this.el.style.width = width + "px";
    this.setCanvasSize(width, parseInt(this.el.style.height));
    return true;
  },
  
  //preload max/min size of this object so multi select can resize quickly...
  getSizeConstraints: function() {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    if((this.isWilcomEMB)||(this.isSavedDigitized)) { //check the resize is within 20%
      var twentyPercentH = this.rotatedRef.h / 5;
      var twentyPercentW = this.rotatedRef.w / 5;
      
      return { max: d.toCanvasDims({ w: this.rotatedRef.w + twentyPercentW, h: this.rotatedRef.h + twentyPercentH},this.cViewProcess.productProcess, 0), min:  d.toCanvasDims({ w: this.rotatedRef.w - twentyPercentW, h: this.rotatedRef.h - twentyPercentH},this.cViewProcess.productProcess, 0)};
    }
    return null;
  },
  
  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();
    }
    if(this.asset != null && this.asset.isVector()) return 1.0;
    
    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 mScaleRefactor = this.cViewProcess.productProcess.getScaleRefactor();
    
    //var minScale = this.layoutManager.layoutData.lScale / mScaleRefactor;
    
    return this.asset.applyScaleRefactor(mScaleRefactor, dims);
    
  },
  
  //check if we can rescale this item using desiredScale to work with process...
  //desiredScale is the rescale in design canvas sizes...
  checkScale: function ImgItem_checkScale(desiredScale, process) {
    
    
    //we have the rescale factor when in design canvas....
    
    //convert to design scale -> desiredScale -> process.unscale_from_design_scale
    
    var scale = this.cViewProcess.productProcess.designScale / desiredScale / process.designScale;
    
    var testHeight = parseFloat(this.height) * scale;
    var testWidth = parseFloat(this.width) * scale;
    var scaleFailed = false;
    if(!this.cViewProcess.productProcess.process.usesDPI) {
      //we cannot rescale this image more then 20%....
      var vDiff = parseFloat(testHeight - this.asset.height) / parseFloat(this.asset.height);
      var hDiff = parseFloat(testWidth - this.asset.width) / parseFloat(this.asset.width);
      
      if(Math.abs(vDiff) > 0.2 || Math.abs(hDiff) > 0.2 ) {
        log("rescale is over 20%: desiredScale=" + desiredScale + ", scale=" + scale + ", testHeight=" + testHeight + " asset.height=" + this.asset.height + ", testWidth=" + testWidth + " asset.height=" + this.asset.height);
        var reScale = 0;
        //we are scaling over 20%... lets cap it at 20%....
        if(Math.abs(vDiff) > Math.abs(hDiff)) {
          //vertically scaled more...
          if(vDiff > 0) {
            reScale = testHeight / (this.asset.height * 1.2);
          } else {
            reScale = testHeight / (this.asset.height * 0.8);
          }
        } else {
          if(hDiff > 0) {
            reScale = testWidth / (this.asset.width * 1.2);
          } else {
            reScale = testWidth / (this.asset.width * 0.8);
          }
        }
        testHeight /= reScale;
        testWidth /= reScale;
        scaleFailed = true;
        log("fixed scale: testHeight=" + testHeight + " testWidth=" +testWidth);
      }
    } else {
      
      // if(testHeight > this.height) { //only care if we scaled up....
        var quality = this.getQuality(testWidth,testHeight,process);
        log("Checking scale, scale=" +scale + " testHeight=" +testHeight + " testWidth=" + testWidth + " quality=" + quality + " this.asset.width=" + this.asset.width + " this.asset.height=" + this.asset.height );
        if((quality < 0.33) && (this.manualQuality == null || this.manualQuality > quality)) { //below 33% quality and below last manually set quality (if manually set)...
          var targetDPI = process.minDPI + (process.perfectDPI - process.minDPI) / 3; // 1/3 between min and perfect dpi
          
          var reScale = 0;
          var dpi = (parseFloat(this.asset.width) / parseFloat(testWidth)) * process.perfectDPI;
          var dpi2 = (parseFloat(this.asset.height) / parseFloat(testHeight)) * process.perfectDPI;
          if(dpi2 < dpi) {
            //height is the bigger problem...
            var betterHeight = parseFloat(this.asset.height) / (targetDPI / process.perfectDPI);
            reScale = testHeight / betterHeight;
            log("Check Scale Failed: targetDPI=" + targetDPI + " height_dpi=" + dpi2 + " width_dpi=" + dpi + " betterHeight=" + betterHeight + " reScale=" + reScale);
          } else {
            var betterWidth = parseFloat(this.asset.width) / (targetDPI / process.perfectDPI);
            reScale = testWidth / betterWidth;
            log("Check Scale Failed: targetDPI=" + targetDPI + " height_dpi=" + dpi2 + " width_dpi=" + dpi + " betterWidth=" + betterWidth + " reScale=" + reScale);
          }
          testHeight /= reScale;
          testWidth /= reScale;
          scaleFailed = true;
          log("Check Scale Failed: testHeight=" + testHeight + " testWidth=" + testWidth);
          
        } else {
          log("Scale check passed this.manualQuality=" + this.manualQuality);
        }
      // } else {
      //   log("Skipping scale check because rescaled smaller");
      // }
    }
    
    if(scaleFailed) { //we could rescale amount wanted.. we have recalced the final w/h.. lets get the scale back from that...
      scale = testHeight / parseFloat(this.height);
      log("scale1=" + scale);
      var scale2 = scale * process.designScale;
      log("scale2=" + scale2);
      
      desiredScale = this.cViewProcess.productProcess.designScale / scale2;
      log("scale3=" + desiredScale);
      
      scale2 = this.cViewProcess.productProcess.designScale / desiredScale / process.designScale;
      
      testHeight = parseFloat(this.height) * scale2;
      testWidth = parseFloat(this.width) * scale2;
      log("scale=" + scale + " scale2=" + scale2 + " testHeight=" + testHeight + " testWidth=" + testWidth + " check q=" + this.getQuality(testWidth,testHeight,process));
      
    }
    return desiredScale; //default behaviour
  },
  
  serializeToOptions: function Item_serializeToOptions(o, excludeRotation) {
    o = ImageItem.parentClass.method("serializeToOptions").call(this,o);

    if(this.stitchCount != null) {
      o.stc = this.stitchCount;
    }
    if(this.colors != null) {
      o.colors = [];
      for(var i=0; i < this.colors.length; i++) {
        o.colors.push(this.colors[i].serialize());
      }
    }
    if(excludeRotation != true) {
      if(this.rotateWidget!=null) {
        o.rot = this.rotateWidget.value;
      } else if(this.options.rot) { 
        o.rot = this.options.rot;
      }
    }

    if(this.maintainAspect) {
      o.ar = "1";
    } else {
      o.ar = "0";
    }
    if(this.itemType == 0 || this.useCanvas) { //text does not use real width/height unless using canvas
      try {
        var realSize = this.getRealDims();
        o.rw = realSize.w;
        o.rh = realSize.h;
      } catch(e) {
        log("Error serializing real site of object type " + this.itemType + ": " + e.message);
      }
    }
      
    if(this.itemType==0) {
      
      o.parc = this.allowReplaceClear;

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

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

    for(k in this.effects) {
      var e = this.effects[k];
      if(e != null) {
        log(e);
        for(ck in e) {
          //log("serialiseStateA:" + ck);
          queryComponents.push(encodeURIComponent(prefix + "[" + k + "][" + ck + "]") + "=" + encodeURIComponent(e[ck]));
        }
      }
      
    }
    
    return queryComponents;
  },
  
  hasEffects: function ImgItem_hasEffects() {
    return (this.effectId != null);
  },
  
  borderClick: function ImgItem_borderClick(bIdx) {
    var border = borderEffects[bIdx];
    if(this.borderId == border[0]) {
      return;
    }
    $("i_b_" + this.elId + '_' + this.borderId).className = "et_effect";
    this.borderId = border[0];
    $("i_b_" + this.elId + '_' + this.borderId).className = "et_effect select";
    this.updateImageSrc();
  },
  
  //find the percent of an image thats opaque...
  calculatePercentOpaque: function ImgItem_calculatePercentOpaque() {
    if(!this.splitColors) {
      return this.percentOpaque;
    } else {
      var totalPercent = parseFloat(this.asset.getMetaData(["split_colors", this.colorCount, "percent_transparent"], 0));
      var counts = this.asset.getMetaData(["split_colors", this.colorCount, "counts"], null);
      for(var i=0; i < this.colorCount; i++) {
        if((this.colors[i] != null) && (this.colors[i].type == "transparent")) {
          var colorPercent = counts[i];
          totalPercent += colorPercent;
        }
      }
      log("Percent Transparent = " + totalPercent);
      return 1.0 - totalPercent;
    }
  },
  
  //get area in sq inches of the image
  calculateArea: function ImgItem_calculateArea() {
    //get the real width/height of image
    var realSize = this.getRealDims();
    //convert to inches
    var dpi = parseFloat(this.cViewProcess.productProcess.perfectDPI);
    this.widthInches = parseFloat(realSize.w) / dpi;
    this.heightInches = parseFloat(realSize.h) / dpi;
    //make area
    log("dpi=" + dpi + ", widthInches=" + this.widthInches + ", heightInches=" + this.heightInches);
    var area = this.widthInches * this.heightInches;
    return area;
  },
  
  updateDigitizationCosts: function ImgItem_updateDigitizationCosts() {
    var percentOpaque = this.calculatePercentOpaque();
    var area = this.calculateArea();
    this.digitizationArea = area * percentOpaque;
    var process = this.cViewProcess.productProcess.process;
    
    this.digitizationFeeRRP = process.digitizationFeePerInch ? process.digitizationFee * d.convertAreaFromSqInches(this.digitizationArea) : process.digitizationFee;
    
    log("process.digitizationFeePerInch=" + process.digitizationFeePerInch + ", process.digitizationFee=" + process.digitizationFee + ", this.cView.configuredProduct.getPercentMarkup=" + this.cView.configuredProduct.getPercentMarkup(true));
    
    var digFee = priceFromCost(this.digitizationFeeRRP, d.commissionRate, this.cView.configuredProduct.getPercentMarkup(true), 0.0); 
    this.digitizationFee = digFee.rrp;
    
    
    if(this.digitizedAsset.primaryItem() == this) {
      log("Primary Digitization Found");
    } else {
      log("Secondary Digitization Found");
    }
    this.stitchCount = parseInt(d.convertAreaFromSqInches(this.digitizationArea) * process.stitchesPerInch);
    log("updateDigitizationCosts: digitizationArea=" + this.digitizationArea +",  digitizationFee=" + this.digitizationFee + ", stitchCount=" + this.stitchCount, true);
  },
  
  updateDigitizationNotice: function ImgItem_updateDigitizationNotice(updateCosts, updatePrice) {
    if(this.digitize) {
      if(updateCosts)  this.updateDigitizationCosts();
      var notice = null;
      
      var calcedArea = d.convertAreaFromSqInches(this.digitizationArea).toFixed(2) + d.getAreaUnit();
      
      var crp = d.round(this.digitizationFee * (100.0 - cDisc) / 100.0);
      
      if(this.digitizedAsset.primaryItem() == this) {
        if(!this.splitColors) {
          notice = ml('Digitizing Fee = %1s will be added to Shopping Cart<br />Size = %2s, Stitch Estimate = %3s<br/>You are looking at the original version of the image you have uploaded. The image will be professionally digitized to at most %4s colors.',
          [d.formatPrice(d.roundPrice(crp), "fee_currency_code", true, d.roundPrice(this.digitizationFee)), calcedArea, this.stitchCount, this.cViewProcess.productProcess.process.maxColors]);
        } else {
          notice = ml('Digitizing Fee = %1s will be added to Shopping Cart<br />Size = %2s, Stitch Estimate = %3s<br/>Colors = Approximated. Final thread colors may vary.',
          [d.formatPrice(d.roundPrice(crp), "fee_currency_code", true, d.roundPrice(this.digitizationFee)), calcedArea, this.stitchCount]);
        }
      } else {
        if(!this.splitColors) {
          notice = ml('The price to digitize this image is included against another item in your cart.<br />Size = %1s<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.',
          [calcedArea, this.cViewProcess.productProcess.process.maxColors]);
        } else {
          notice = ml('The price to digitize this image is included against another item in your cart.<br />Size = %1s<br/>Colors = Approximated. Final thread colors may vary.',
          [calcedArea]);
        }
      }
      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(true);
      } 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(true);
        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(true);
      } else {
        if((event & DIG_EVENT_COLOR) == DIG_EVENT_COLOR) {
          this.initColors(true);
        }
      }
      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;
  },
  
  replaceImage: function ImgItem_replaceImage(event) {
    var self = this;
    d.decorationLibraryManager.selectImage(this.cViewProcess.productProcess, function(image) {
        var newAssetDef = d.addAsset(image.assetOptions());
        self.asset = newAssetDef;
        
        //resize proportionatly inside the existing size bounding box....
        var dims = self.asset.scale(self.originalHeight, self.originalWidth);
        
        var mScaleRefactor = self.cViewProcess.productProcess.getScaleRefactor();
        dims =  newAssetDef.applyScaleRefactor(mScaleRefactor, dims); 
        
        //center it inside the current bb...
        var x = self.originalLeft + (self.originalWidth - dims.w) / 2;
        var y = self.originalTop + (self.originalHeight - dims.h) / 2;
        
        self.left = x;
        self.top = y;
        self.width = dims.w;
        self.height = dims.h;
        self.setPosition();
        self.updateImageSrc();
        self.setDirty();
        self.initColors(false);
        if(self.thumb!=null) {
          var sizes = self.asset.scale(60, 60);
          setTransPng(self.thumb, self.asset.getSUrl(), parseInt(sizes.w, 10), parseInt(sizes.h, 10));
        }
        d.currentProductType.updatePrice();
    });
    
    Event.stop(event);
    return false;
  },
  
  clearImage: function ImgItem_clearImage(event) {
    if(confirm("Are you sure you want to clear this image?")) {
      setTransPng(this.thumb, "/images/trans.gif", 5, 5);
      this.asset = new NoAsset();
      this.setSrc("/images/trans.gif");
      this.setDirty();
      this.initColors(false);
      d.currentProductType.updatePrice();
    }
    Event.stop(event);
    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.perCaption = dopt(options, "perc", ml("Set Text"));
    
    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.editTextEvent = this.editTextClick.bindAsEventListener(this);
    
    
    this.sizeFieldChangedEvent = this.sizeFieldChanged.bindAsEventListener(this);

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

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

    this.buildFontDropdown();
    //this.fontInput.value = 
    */
    
    this.aligns = [$("mp1_" + this.elId + "_all"), $("mp1_" + this.elId + "_alc"), $("mp1_" + this.elId + "_alr")];
    if(this.aligns[0] != null) {
      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);
    };
 
    if(this.itemType != 4) {
      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.gradientTypeVal = this.value; self.fontStyleChanged("gradient"); self.setDirty(); self.setText(); };
      
      this.textColor = $("mp1_" + this.elId + "_cb");
      this.gradientColor = $("ep_gradient_color_" + this.elId);
      this.effectColor = $("ep_effectcolor_" + this.elId);
      this.strokeColor = $("ep_stroke_color_" + this.elId);
      
      this.textColor.style.backgroundColor = this.textColorVal.htmlColor();
      this.textColor.onclick = function() { self.fontStyleChanged("text-color"); self.setColorClick(self.textColor, 1); };
      this.gradientColor.style.backgroundColor = this.gradientColorVal.htmlColor();
      this.gradientColor.onclick = function() { self.fontStyleChanged("gradient-color"); self.setColorClick(self.gradientColor, 2); };
      this.effectColor.style.backgroundColor = this.effectColorVal.htmlColor();
      this.effectColor.onclick = function() { self.fontStyleChanged("effect-color"); self.setColorClick(self.effectColor, 3); };
      this.strokeColor.style.backgroundColor = this.strokeColorVal.htmlColor();
      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.effectTypeVal = this.value; self.fontStyleChanged("effect"); self.setEffectType(true);};
      
      //this.glowStrength = $("ep_glow_strength_" + id);
      //this.glowStrength.onclick = function(el) { _this.setDirty(); _this.setText();};
      
      this.sliderGlow = new Control.Slider('ep_g_str_' + this.elId + '_s','ep_g_str_' + this.elId + '_t', {
        range:$R(0,10),
        sliderValue:this.glowStrength,
        onChange:function(v){ self.glowStrength=v; self.fontStyleChanged("glow"); self.setDirty(); self.setText(); }
      });
      
      this.sliderBlur = new Control.Slider('ep_b_str_' + this.elId + '_s','ep_b_str_' + this.elId + '_t', {
        range:$R(0,10),
        sliderValue:this.blurVal,
        onChange:function(v){ self.blurVal=v;  self.fontStyleChanged("blur"); self.setDirty(); self.setText(); }
      });
      
      this.shadowEffects = $("shadow_effects_" + this.elId); 
      this.glowEffects = $("glow_effects_" + this.elId); 
      
      this.shape_select = $("warp_shape_select_"+this.elId);
      
      
        
      if($('max_width_' + this.elId + '_t') != null) {
        this.sliderMaxWidth = new Control.Slider('max_width_' + this.elId + '_s','max_width_' + this.elId + '_t', {
          range:$R(0,100),
          sliderValue:this.maxWidth,
          onSlide:function(v){ self.setMaxWidth(v);}
        });
        
        $('mp1_' + this.elId + '_use_max_width').checked = this.useMaxWidth;
        if(!this.useMaxWidth) {
          $('max_width_' + self.elId + '_t').style.visibility='hidden';
        }
        $('mp1_' + this.elId + '_use_max_width').onclick= function() {
          
          self.useMaxWidth = $('mp1_' + self.elId + '_use_max_width').checked;
          if(self.useMaxWidth) {
            $('max_width_' + self.elId + '_t').style.visibility='';
          } else {
            $('max_width_' + self.elId + '_t').style.visibility='hidden';
          }
          self.setText();
        };
      }
    
  //    this.shape
  
      if(!this.allowFeature("text-warp")) {
        $("et_shape_" + this.elId).hide();
      } else {
  
        if (this.isWilcomEMB) {
          
          var shapes_li = "<ol class='emb_shapes'>" ;
          for(var k in embShapes) {
            var shape = embShapes[k] ;
            shapes_li += "<li id='emb_shape_"+this.elId+"_"+k+"' value='"+k+"' class='"+shape.code+"'><span>"+shape.c+"</span></li>" ;
          }
          shapes_li += "</ol>" ;
          
          this.shape_select.innerHTML = shapes_li ;
          
          for (var k in embShapes) {
            var emb_ele = $('emb_shape_'+this.elId+'_'+k) ;
            var that = this ;
            if (this.shape == k) emb_ele.addClassName("alt") ;
            emb_ele.observe("click", function(e) {
              var ele = Event.element(e);
              if (ele.tagName.toUpperCase() != "LI") ele = ele.parentNode ;
    //          log("** changing element "+that.elId+", from shape "+that.shape+" to "+ele.value) ;
              $("emb_shape_"+that.elId+"_"+that.shape).removeClassName("alt") ;
              ele.addClassName("alt") ;
              self.shape = ele.value ;
              self.setShapeType(true, true);
            }.bind(that));
          }
          
        } else {
          this.shapes = $("ep_shape_sel_down_" + this.elId);
    
          addSelectOption(this.shapes, "None", "0"); 
          addSelectOption(this.shapes, "Arched", "5"); 
          for(var k in availableShapes) {
            var shape = availableShapes[k];
            addSelectOption(this.shapes, shape["c"], k); 
          }
          this.shapes.value = self.shape; // TODO: test this works in IE.
          this.shapes.onchange = function() {
            self.shape = self.shapes.value;
            self.setShapeType(true);
          };
        }
      
        this.xAmpC = $("ep_s_x_" + this.elId);
        this.yAmpC = $("ep_s_y_" + this.elId);
        
        this.sliderX = new Control.Slider('ep_x_str_' + this.elId + '_s','ep_x_str_' + this.elId + '_t', {
          range:$R(0,100),
          sliderValue:this.xAmp,
          onChange:function(v){self.xAmp=v;   self.setDirty(); self.setText();}
        });
        this.sliderY = new Control.Slider('ep_y_str_' + this.elId + '_s','ep_y_str_' + this.elId + '_t', {
          range:$R(0,100),
          sliderValue:this.yAmp,
          onChange:function(v){self.yAmp=v; self.setDirty(); self.setText(); }
        });
      }
      
      this.offsetWidget = $("mp_" + this.elId + "_offset");
      this.offsetHandleWidget = $("mp_" + this.elId + "_offset_handle");
      
      this.offsetHandleWidget.style.top = (this.shadow_y + 18 - offsetWidgetSize) + "px";
      this.offsetHandleWidget.style.left = (this.shadow_x + 18 - offsetWidgetSize) + "px";
      
      //log("this.offsetHandleWidget.style.top=" + this.offsetHandleWidget.style.top);
      //log(this.shadow_x);
      //log(offsetWidgetSize);
      
      this.eventStartOffset =  this.startOffset.bindAsEventListener(this);
      this.eventOffsetMove =  this.offsetMove.bindAsEventListener(this);
      this.eventOffsetFinish =  this.offsetFinish.bindAsEventListener(this);
      Event.observe(this.offsetHandleWidget, "mousedown", this.eventStartOffset);
      
      //this.fsInput.value = dopt(this.options, "fs", "23");
      //this.b_input.checked = (dopt(this.options, "b", "true")=="true")? true:false;
      //this.i_input.checked = (dopt(this.options, "i", "false")=="true")? true:false;
      
      this.gradientType.value = this.gradientTypeVal;//dopt(this.options, "g", "0");
      this.effectType.value = this.effectTypeVal;//dopt(this.options, "e", "0");
      this.setEffectType(false);
      if(this.allowFeature("text-warp")) {
        this.setShapeType(false);
      }
      if(this.isWilcomEMB) {
        $("advanced_pane_tabs_" + this.elId).hide();
        this.selectTab("shape", this.elId);
        
        $("mp1_" + this.elId + "_alc").hide();
      }
      if(!this.allowFeature("text-align")) {
        $("mp1_" + this.elId + "_alc").hide();
      }
      if((d.designerOptions.showTextAdvanced())&&(d.designerOptions.showTextAdvancedOpen())) {
        this.advClick();
      }
    }
    //this.setText(false); moved to buildElement
  },
  
  initialiseTemplatePane: function TextItem_initialiseTemplatePane() {
    log("TextItem.initialiseTemplatePane()");
    TextItem.parentClass.method("initialiseTemplatePane").call(this);
    //check if we need multiline...
    if(this.text != null && this.text.indexOf("\n") != -1) {
      this.editTextArea = $("mp_" + this.elId + "_txta");
      var rows = this.text.split("\n").length;
      if(rows > 2) {
        if(rows > 5) rows = 5;
        this.editTextArea.rows = rows;
      }
    } else {
      this.editTextArea = $("mp_" + this.elId + "_txt");
    }
    this.editTextArea.value = this.text;
    Event.observe(this.editTextArea, "keyup", this.editTextEvent);
    Event.observe(this.editTextArea, "change", this.editTextEvent);
    Event.observe(this.editTextArea, "blur", this.editSaveClickEvent);
    
    
    this.applyTextButton = $("mp_" + this.elId + "_apply"); 
    Event.observe(this.applyTextButton, "click", this.editSaveClickEvent);

    var clrCount = this.buildTemplateColorSelectors();
    
    if(this.itemType == 1) {
      this.editTextArea.style.width =  (325 - (clrCount * 27)) + "px";
      this.editTextArea.show();
    }
  },
  
  buildTemplateColorSelectors: function(itemList) {
    var colorsEl = $("mp_" + this.elId + "_colors");
    var clrCount = 0;
    if(itemList == null) {
      itemList = [this];  
    }
    var self = this;
    if(colorsEl != null) {
      if(this.itemType == 1 || this.itemType == 6) {
        clrCount ++;
        this.textColor = this.buildTemplatePaneColorButton(colorsEl, this.textColorVal, function() { 
            for(var i=0; i < itemList.length; i++) {
              itemList[i].fontStyleChanged("text-color"); 
            }
            self.setColorClick(self.textColor, 1, itemList); 
            return false;
        });
        if(dopt(this.options, "g", "0") != "0") {
          this.gradientColor = this.buildTemplatePaneColorButton(colorsEl, this.gradientColorVal, function() { 
            for(var i=0; i < itemList.length; i++) {
              itemList[i].fontStyleChanged("gradient-color"); 
            }
            self.setColorClick(self.gradientColor, 2, itemList); 
            return false;
          });
          clrCount+= 1;
        }
        if(this.strokeWidth > 0) {
          this.strokeColor = this.buildTemplatePaneColorButton(colorsEl, this.strokeColorVal, function() { 
            for(var i=0; i < itemList.length; i++) {
              itemList[i].fontStyleChanged("stroke-color"); 
            }
            self.setColorClick(self.strokeColor, 4, itemList); 
            return false;
          });
          clrCount+= 1;
        }
        if(dopt(this.options, "e", "0") != "0") {
          this.effectColor = this.buildTemplatePaneColorButton(colorsEl, this.effectColorVal, function() { 
            for(var i=0; i < itemList.length; i++) {
              itemList[i].fontStyleChanged("effect-color"); 
            }
            self.setColorClick(self.effectColor, 3, itemList); 
            return false;
          });
          clrCount+= 1;
        }
      }
    }
    return clrCount;
  },
  
  buildTemplatePaneColorButton: function Txt_btpcb(colorsEl, color, callback) {
    var el = $(document.createElement('li'));      
    el.update('<a onmouseout="d.colorMouseOut(this);" onmousemove="d.colorMouseMove(this);" class="color_panel_cell" href="#">' + 
      '<span style="background-color: ' + color.htmlColor() + '; height: 20px;">&nbsp;</span><b>&nbsp;</b>' + 
      '</a>'
      );
    el.onclick = callback;
    colorsEl.appendChild(el);
    return el;
  },
  

  checkPersonaliseOptions: function() {
    var cont = $("mp1_" + this.elId + '_fc_max_width');
    if(cont != null) { 
      if(this.allowPersonalization) {
        cont.show();
      } else {
        cont.hide();
      }
    }
  },
  
  fontStyleChanged: function TxtItem_fontStyleChanged(attribute) {
    
  },
  
  setFontImage: function TxtItem_setFontImage() {
    if(this.fontTextImg!=null) {
      var font = pwFontManager.allFonts[this.fontFace];
      if(font == null) {
        if(this.allowUnmatchedFont) {
          return;
        } else {
          var fallback = this.isWilcomEMB ? "Arial Rounded" : "Arial";
          alert("Cannot find font definition for '" + this.fontFace + "', using '" + fallback + "' instead");
          this.fontFace = fallback;
          font = pwFontManager.allFonts[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(false, 1, false);
  },
  //do nothing: text can rescale to desired scale....
  checkScale: function TxtItem_checkScale(desiredScale, process) {
    return desiredScale;
  },
  
  del: function TxtItem_del() {
    if(this.editButton!=null) {
      Event.stopObserving(this.editButton, "click", this.editClickEvent);
      Event.stopObserving(this.editSaveButton, "click", this.editSaveClickEvent);
      Event.stopObserving(this.editCancelButton, "click", this.editCancelClickEvent);
      //Event.stopObserving(this.advButton, "click", this.advClickEvent);
    }
    TextItem.parentClass.method("del").call(this);
  },
  
  //does this item handle its own start positions?
  startPosition: function TxtItem_startPosition(options) {
    if(options != null && options.t != null) {
      this.top = options.t;
      this.left = options.l;
    } else {
      this.doCenter = true;
    }
    return true;
  },
  
  setRolloverStyle: function TxtItem_setRolloverStyle() {
    
  },
  
  setLeaveStyle: function TxtItem_setLeaveStyle() {
    this.el.style.zIndex = this.getElementZIndex();
  },

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

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

    this.setDirty();
    this.setText();
  },
  
  setEffectType: function TxtItem_setEffectType(fromUI) {
    var val = this.effectType.options[this.effectType.selectedIndex].value;
    if(val==0) {
      this.shadowEffects.style.display='none';
      this.glowEffects.style.display='none';
    } else if(val==1) {
      this.shadowEffects.style.display='';
      this.glowEffects.style.display='none';
    } else {
      this.shadowEffects.style.display='none';
      this.glowEffects.style.display='';
    }
    if(fromUI) {
       this.setDirty();
       this.setText();
    }
  },
  
  setShapeType: function TxtItem_setShapeType(fromUI, emb) {
    if(!emb) emb = false ;
    
    if(this.shape == "5") {
      this.yAmpC.style.display='';
    } else {
      var shape = emb ? embShapes[this.shape] : availableShapes[this.shape];
      if(shape==null) {
        this.xAmpC.style.display='none';
        this.yAmpC.style.display='none';
      } else {
        if(shape["x"]) {
          this.xAmpC.style.display='';
        } else {
          this.xAmpC.style.display='none';
        }
        if(shape["y"]) {
          this.yAmpC.style.display='';
        } else {
          this.yAmpC.style.display='none';
        }
      }
    }
    if(fromUI) {
       this.setDirty();
       this.setText();
    }
  },
                
  selectTab: function TxtItem_selectTab(tab, id) {
    if(this.oldTabName != null) {
      if(this.oldTabName==tab) {
        return;
      }
      var oldTab = $('ep_' + this.oldTabName + "_" + id);
      oldTab.style.display='none';
      oldTab = $('et_' + this.oldTabName + "_" + id);
      oldTab.className = "et_unselected_tab";
    }
    var newTab = $('ep_' + tab + "_" + id);
    newTab.style.display='';
    newTab = $('et_' + tab + "_" + id);
    newTab.className = "et_selected_tab";
    this.oldTabName = tab;
  },
  
  beforeAddNew: function TxtItem_beforeAddNew() {
    popup('new_text_popup');
    $("new_text").focus();
    return true;
  },
  
  continueAdd: function TxtItem_continueAdd() {
    this.text = $("new_text").value;
    $("new_text").value = "";
    this.setName(this.text);
    this.checkUtf8();
  },
  
  buildElement: function TxtItem_buildElement() {
    if ($("d_" + this.elId)) $("d_" + this.elId).remove();
    if(this.useCanvas) {
      this.el = $(document.createElement("SPAN"));
      this.canvas = Raphael(this.el, 10, 10);
      this.canvasImg = this.canvas.image("/images/trans.gif", 0, 0, 10, 10);
      this.canvasImgWidth = 10.0;
      this.canvasImgHeight = 10.0;
    } else {
      this.el = $(document.createElement("IMG"));
    }
    this.el.id = "d_" + this.elId;
    this.el.style.display = "none";
    this.el.style.position = "absolute";
    this.el.style.cursor="pointer";
	
    if(!this.useCanvas) {
      if(useAlphaHack) {
        this.el.src = d.pathPrefix + "/images/trans.gif";
      }
    }
    this.cViewArea.canvas.appendChild(this.el);
    this.el.style.zIndex = this.getElementZIndex();
    
    this.setText(false);
    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.applyTextButton != null) && (this.applyTextButton.style.display == 'none')) {
      //no changes.. on blur event...
      return;
    }
    
    if(this.editTextArea.value=="") {
      alert(ml("NO_TEXT"));
      return;
    }
    this.text = this.editTextArea.value;
    this.checkUtf8();
    this.setName(this.text);
    if(this.editButton != null) {
      this.editButton.show();
      this.managePaneBody.show();
      this.editContainer.hide();
    }
    this.setDirty();
    this.setText();
    if(this.applyTextButton != null) {
      this.applyTextButton.hide();  
    }
  },
  
  editCancelClick: function TxtItem_editCancelClick(event) {
    this.editButton.style.display = '';
    this.managePaneBody.style.display = '';
    this.editContainer.style.display = 'none';
  },
  
  editTextClick: function TxtItem_editTextClick(event) {
    if(this.text != this.editTextArea.value ) {
      this.applyTextButton.show();
    } else {
      this.applyTextButton.hide();
    }
    /*
    this.text = this.editTextArea.value;
    this.checkUtf8();
    this.setName(this.text);
    this.setDirty();
    this.setText();*/
  },
  
  rotationChanged: function TxtItem_rotationChanged(deg) {
    var self = this;
    if(this.rotDelay != null) {
      window.clearTimeout(this.rotDelay);
    }
    if(this.useCanvas) {
      this.setDirty();
      this.autoRepositionHandles();
    } else {
      this.rotDelay = window.setTimeout( function() { 
        self.setDirty();
        self.setText(false); 
        //d.itemChanged(true);
      }, 100);
    }
    return;
  },
  
  serializeToOptions: function Item_serializeToOptions(o, excludeRotation) {
    o = TextItem.parentClass.method("serializeToOptions").call(this,o, excludeRotation);
    
    o.font = this.fontFace;

    o.fs = this.fs;
    o.scale = this.cViewProcess.productProcess.designerScale;
                                                      
    o.al = this.align;
                                                                                              
    o.b = this.isBold;
    o.i = this.isItalics;
      
    o.text =  this.text;
                                                      
    o.tc = this.textColorVal.serialize();
    o.gc = this.gradientColorVal.serialize();
    o.sc = this.strokeColorVal.serialize();
    o.ec = this.effectColorVal.serialize();
                                                      
    o.sw = this.strokeWidth;
                                                    
    //o.g = this.gradientType.value;
    o.g = this.gradientTypeVal;
                                                      
    //o.e = this.effectType.value;
    o.e = this.effectTypeVal;
    o.gs = this.glowStrength;
    o.bl = this.blurVal;
                                                      
    o.sh_x = this.shadow_x;
    o.sh_y = this.shadow_y;
                                                      
    o.s = this.shape;
    o.s_x = this.xAmp;
    o.s_y = this.yAmp;
    
    if(this.itemType == 1) {
      //we only allow max width when configuring predec or in personalise mode..
      if(d.mode == DESIGNER_MODE_CONFIGURE || d.mode == DESIGNER_MODE_TEMPLATE) {
        if(this.maxWidth != null) {
          o.mw = this.maxWidth;   
        }
        if(this.useMaxWidth != null) {
          o.umw = this.useMaxWidth ? "1" : "0"; 
          
          if(this.cView.configuredProduct.serializingForSave != true && d.mode == DESIGNER_MODE_CONFIGURE) {
            o.mwb = 1; //add a border for max width
          }
        }
      }
    }
    return o;
  },
  
  setMaxWidth: function(val) {
    this.maxWidth = val;

    if(this.useMaxWidth) {
      this.setText(); 
      this.setDirty();
    }
  },
 
  setText: function TxtItem_setText(doDelay, pass, showStatus) {
    //var updateInfo = this.startUpdating();
    if(doDelay==null) {
      doDelay = true;
    }
    if(showStatus == null) showStatus = true;
    
    if(pass == null) {
      pass = 1;
    }
    if(!this.isWilcomEMB && pass == 1) { // we only need multiple pass with wilcom emb text
      pass = 2;
    }
    var self = this;
    if(this.textDelay != null) {
      window.clearTimeout(this.textDelay);
      this.textDelay = null;
    }
    if(doDelay) {
      this.textDelay = window.setTimeout( function() { self.setText(false, pass, showStatus); }, 100);
      return;
    }
    
    if(pass != 3) { //pass == 3 means we dont want two passes so the first pass is skipped...
    //startAsyncAction();
    //if(useAlphaHack) { //we are now always using 2 stage method...
      if(showStatus) this.startUpdating();
      var excludeRotation=this.useCanvas;
      ajaxQueueManager.queueRequest("save_product/" + this.cView.configuredProduct.clientId, 3, {
          mode: 0,
          subKey: "text_" + this.id,
          urlFunction: function() {
            return d.pathPrefix + "/designer/prepare_text?id=" + self.id + "&cp=" + self.cView.configuredProduct.id + "&cpv=" + self.cView.id + "&cpva=" + self.cViewArea.id + "&cpvap=" + self.cViewProcess.id + "&pass=" + pass;
          },
          parameters: function() {
            return self.serialize([],"a", excludeRotation)
          },
          options: {asynchronous:true, evalScripts:true}
      });
    }
    
    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, showStatus); }, 1000);
    }
    
  },
  
  tbTransformed: function TxtItem_tbTransformed(data) {
    log("tbTransformed" );
    log(data);
    var checkCropIssue = true;
    //finishAsyncAction();
    var oldWidth = this.width;
    var oldHeight = this.height;
    var reCenter = true;
    if(this.width == null) reCenter = false;
    if(parseInt(data.version, 10) == this.renderVersion) { //we have made more changes just update the image....
      this.width = parseInt(data.width, 10); // / this.layoutManager.layoutData.dScale;
      this.height = parseInt(data.height, 10);  // / this.layoutManager.layoutData.dScale;
      if(this.useCanvas) {
        this.setReferenceRotation(0, true, {w:this.width, h:this.height});  //store the rotated ref from the servers size dims.
        if(this.rotated) {
          this.setReferenceRotation(this.getRotation());
          this.width = this.rotatedRef.w;
          this.height = this.rotatedRef.h;
        }
      }
    } else {
      log("render version changed, skipping updating width/height" );
    }
    if(this.doCenter) {
      log("centering image");
      this.top = (this.cViewProcess.productProcess.fullHeight - this.height) / 2;
      this.left = (this.cViewProcess.productProcess.fullWidth - this.width) / 2;
      this.doCenter = false;
      reCenter = 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;
      }
    }
    if(reCenter) {
      var xDelta = (oldWidth - this.width) / 2;
      var yDelta = (oldHeight - this.height) / 2;
      log("recentering image: xDelta=" + xDelta + ", yDelta=" + yDelta);
      this.left += xDelta;
      this.top += yDelta;
    }
    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");
        if(this.allowUnmatchedFont) {
          return;
        }
      } else if(f.supportsPages(usedUtfPages)) {
        log("checkUtf8: font supports utf8");
				return;
			}
      log("checkUtf8: font does not support utf8, this.isWilcomEMB=" + this.isWilcomEMB);
			//current font doesnt support it... go set font to first utf8 font...
			for(var k in pwFontManager.allFonts) {
        var f = pwFontManager.allFonts[k];
				if((f.embFont == this.isWilcomEMB)&&(f.supportsPages(usedUtfPages))) {
					this.fontFace = f.name;
          log("Changed font to " + f.name);
					this.setFontImage();
					return;
				}
			}
      log("No Fonts Support UTF8");
		} else {
      pwFontManager.utf8Pages = null;
		}
	},
  
  usedUtf8: function TxtItem_usesUtf8() {
    var usedPages = {};
    var foundLanguage = false;
    for(var i=0;i < this.text.length; i++) {
      var theChar = this.text.charCodeAt(i);
      if(theChar != 32) { //ignore spaces....
        var pages = this.getUtfCodePage(theChar);
        if(pages == null) {
          alert("Unable to get code page for " + theChar);
        } else {
          var codes = pages[3];
          if(codes == null) codes = pages[2];
          usedPages[codes] = true;
          foundLanguage = true;
          //log(theChar + "=" + codes);
        }
      }
    }
    return foundLanguage ? usedPages : null;
  },
  
  getUtfCodePage: function TxtItem_getUtfCodePage(i) {
    var left = 0;
    var right = utf8CodePages.length - 1;
    while (left <= right) {
      var mid = parseInt((left + right)/2);
      var page = utf8CodePages[mid];
      if(page[0] <= i && page[1] >= i)
        return page;
      else if (page[0] < i)
        left = mid + 1;
      else
        right = mid - 1;
    }
    return null;
  },
  
  setHeight: function TxtItem_setHeight(height) {
    log("setHeight: " + height);
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.el.style.height = height + "px";
    if(this.el.style.width != "" && this.el.style.width != null) {
      this.setCanvasSize(parseFloat(this.el.style.width), parseFloat(this.el.style.height));
    }
    return true;
  },
  
  setWidth: function TxtItem_setWidth(width) {
    if(this.rotatedRef == null) {
      this.setReferenceRotation();
    }
    this.el.style.width = width + "px";
    if(this.el.style.height != "" && this.el.style.height != null) {
      this.setCanvasSize(parseFloat(this.el.style.width), parseFloat(this.el.style.height));
    }
    return true;
  }
  
});

//NOTE: this is the old way of teamnames, refer to 42d-team_item.js

var TeamItemTplt = Class.create({
  CLASSDEF: {
      name:  'TeamItemTplt',
      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);}
    });
  },
  
  
  initialiseTemplatePane: function TextItem_initialiseTemplatePane() {
    log("TeamItem.initialiseTemplatePane()");
    //we dont show anything in the normal pane area... it appears in the teamname are
    
    this.cViewProcess.buildTemplatePane(this); //register that we have one...
    
    
  },
  
  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)) {
      o.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 TeamItemTpltSizing = Class.create({
  CLASSDEF: {
      name:  'TeamItemTpltSizing'
  },
  
  initialize: function TeamItemSizing_initialize(options) {
    this.total_height = options.total_height;
    this.spacing = options.spacing;
    if(options.sections instanceof Array) {
      this.sections = options.sections;
    } else {
      this.sections = [options.sections];
    }
  },
  
  serialize: function TeamItemSizing_serialize(queryComponents, prefix) {
    queryComponents.push(encodeURIComponent(prefix + "[total_height]") + "=" + encodeURIComponent(this.total_height));
    queryComponents.push(encodeURIComponent(prefix + "[spacing]") + "=" + encodeURIComponent(this.spacing));
    for(var i=0; i < this.sections.length; i++) {
      queryComponents.push(encodeURIComponent(prefix + "[sections][]") + "=" + encodeURIComponent(this.sections[i]));
    }
  },
  
  serializeToOptions: function TeamItemSizing_serializeToOptions() {
    var o = {};
    o.total_height=this.total_height;
    o.spacing=this.spacing;
    o.sections = [];
    for(var i=0; i < this.sections.length; i++) {
      o.sections.push(this.sections[i]);
    }
    return o;
  }
  
});
    

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

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

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

var TemplateTextItem = Class.create({
  CLASSDEF: {
      name:  'TemplateTextItem',
      parent: TextItem
  },
  
  initialize: function TemplateTextItem_initialize(id, cViewProcess, asset, options) {
    this.allowUnmatchedFont = true;
    TemplateTextItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    this.doCenter = false;
    this.itemType = 4;
    this.itemTypeName = "Text";
    this.allowHandleResizing = true;
    this.colors = asset.getTextTemplateColors(options.colors);
  },
  
  initialiseManagePane: function TemplateTextItem_initialiseManagePane() {
    log("TemplateTextItem.initialiseManagePane()");
    TemplateTextItem.parentClass.method("initialiseManagePane").call(this);
    var colorsEl = $("mp1_" + this.elId + "_colors");
    for(var i=0; i < this.colors.list.length; i++) {
      var el = $(document.createElement('div'));
      el.style.backgroundColor = this.colors.list[i].htmlColor();
      el.update('<span class="drop_arrow">&nbsp;</span>');
      this.bindColorClick(el, i);
      colorsEl.appendChild(el);
    }
  },
  
  initialiseTemplatePane: function TemplateTextItem_initialiseTemplatePane() {
    log("TemplateTextItem.initialiseTemplatePane()");
    TemplateTextItem.parentClass.method("initialiseTemplatePane").call(this);
    var colorsEl = $("mp_" + this.elId + "_colors");
    for(var i=0; i < this.colors.list.length; i++) {
      var el = $(document.createElement('li'));      
      el.update('<a onmouseout="d.colorMouseOut(this);" onmousemove="d.colorMouseMove(this);" class="color_panel_cell" href="#">' + 
        '<span style="background-color: ' + this.colors.list[i].htmlColor() + '; height: 20px;">&nbsp;</span><b>&nbsp;</b>' + 
        '</a>'
        );
      this.bindColorClick(el, i);
      colorsEl.appendChild(el);
    }
    this.editTextArea.style.width = (325 - (this.colors.list.length * 27)) + "px";
    this.editTextArea.show();
  },
  
  bindColorClick: function(el, idx) {
    var self = this;
    el.onclick = function() {
      self.setColorClick(el, idx);
      return false;
    };
  },
  
  setColorClick: function TemplateTextItem_setColorClick(el, idx) {
    var self = this;
    var color = this.colors.list[idx]; //.htmlColor();
    pwColorPicker.selectColor(this.cViewProcess.productProcess.id, el, color,  function(c, co) { self.colorCallback(c, co, el, idx); }, {});
  },
  
  colorCallback: function TemplateTextItem_colorCallback(color, colorObject, el, idx) {
    if(d.inSimpleMode()) {
      el.firstChild.firstChild.style.backgroundColor = color;
    } else {
      el.style.backgroundColor = color;
    }
    var newColor = new PikiColor(colorObject);
    newColor.id = this.colors.list[idx].id;
    this.colors.list[idx] = newColor;
    this.setDirty();
    this.setText();
  },
  
  serializeToOptions: function TemplateTextItem_serializeToOptions(o, excludeRotation) {
    o = Item.method("serializeToOptions").call(this,o);
    
    var realSize = this.getRealDims();
    o.rw = realSize.w;
    o.rh = realSize.h;
      
    o.font = this.fontFace;
                                                                                     
    o.b = this.isBold;
    o.i = this.isItalics;
      
    o.text =  this.text;
       
    o.colors = {};
    for(var i=0; i < this.colors.list.length; i++) {
      o.colors[this.colors.list[i].id] = this.colors.list[i].serialize();
    }

    if(excludeRotation != true) {
      if(this.rotateWidget!=null) {
        o.rot = this.rotateWidget.value;
      } else if(this.options.rot) { 
        o.rot = this.options.rot;
      }
    }

    if(this.maintainAspect) {
      o.ar = "1";
    } else {
      o.ar = "0";
    }
    
    return o;
  },
  
  setText: function TemplateTextItem_setText(doDelay, pass) {
    if(this.renderVersion == 0) { //use the pre-rendered image...
     // ImageItem.method("updateImageSrc").call(this);
      this.setSrc(this.asset.getWUrl());
    } else {
      TextItem.method("setText").call(this,doDelay, pass);
    }
  }
});


var TeamItem = Class.create({
  CLASSDEF: {
      name:  'TeamItem',
      parent: TextItem
  },
  
  initialize: function TeamItem_initialize(id, cViewProcess, asset, options) {
    if(options.al == null) options.al = "1";
    TeamItem.parentClass.constructor().call(this, id, cViewProcess, asset, options);
    this.itemType=6;
    this.cView.configuredProduct.getTeamNames().registerTeamNameItem(this);
    
    if(this.maxWidth == null) {
      this.maxWidth = 95;  
    }
    this.useMaxWidth = true;
    this.label = options.lbl;
    if(this.label == null) this.label = "Team Name";
    this.inputWidth = options.iw;
    if(this.inputWidth == null) this.inputWidth = 15;
    
    this.sourceTeamnameId = parseInt(options.src_tn_id, 10);
    
    this.defaultText = options.defaultText;
    
    this.setName(this.label);
    this.setFeature("tn_label", true);
    if(d.mode == DESIGNER_MODE_CONFIGURE) {
      this.setFeature("tn_size", true);
    }
  },
  
  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 + "_fc_tn_width").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.maxWidth,
      onSlide:function(v){ self.setMaxWidth(v);}
    });
    this.perContainer.hide();
    this.initFeature("tn_label");
    this.initFeature("tn_size");
    
    this.labelInputEl =  $("mp1_" + this.elId + "_tn_label");
    this.inputWidthEl =  $("mp1_" + this.elId + "_tn_size");
    
    $("mp1_" + this.elId + "_edit").update(ml("Edit teamnames"));
    updateToolTip($("mp1_" + this.elId + "_edit"), ml("Edit teamnames"));

    if(this.labelInputEl != null) {
      this.labelInputEl.value = this.label;
      this.labelInputEl.onkeyup = function() {
        self.label = self.labelInputEl.value;
        self.setName(self.label);
      }
      this.labelInputEl.onchange = function() {
        self.label = self.labelInputEl.value;
        self.setName(self.label);
      }
    }
    if(this.inputWidthEl != null) {
      this.inputWidthEl.value = this.inputWidth;
      this.inputWidthEl.onkeyup = function() {
        self.inputWidth = parseInt(self.inputWidthEl.value);
        if(isNaN(self.inputWidth) || (self.inputWidth <=0)) {
          self.inputWidth = 15;
        }
      }
      this.inputWidthEl.onchange = function() {
        self.inputWidth = parseInt(self.inputWidthEl.value);
        if(isNaN(self.inputWidth) || (self.inputWidth <=0)) {
          self.inputWidth = 15;
        }
      }
    }
  },
  
  
  initialiseTemplatePane: function TextItem_initialiseTemplatePane() {
    log("TeamItem.initialiseTemplatePane()");
    //we dont show anything in the normal pane area... it appears in the teamname are
    
    this.cViewProcess.buildTemplatePane(this); //register that we have one...
    
    
  },
  
  //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);
    
    if(this.cView.configuredProduct.getTeamNames().selectedTeamName!=null) {
      o.text = this.cView.configuredProduct.getTeamNames().selectedTeamName.getCellData(this.sourceTeamnameId);
    }
    if(this.cView.configuredProduct.serializingForSave != true && d.mode != DESIGNER_MODE_TEMPLATE) {
      o.mwb = 1; //add a border for max width
    }
    
    if(this.maxWidth != null) {
      o.mw = this.maxWidth;   
    }
    if(this.useMaxWidth != null) {
      o.umw = this.useMaxWidth ? "1" : "0"; 
    }
    
    o.src_tn_id = this.sourceTeamnameId;
    
    o.lbl = this.label;
    o.iw = this.inputWidth;
    
    
    return o;
  },
  
  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();
    Event.stop(event);
    return false;
  },
  
  //for now this will only be called on features team names do not support...
  allowFeature: function(feature) {
    return true;
  }
});



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

    this.id = options.id;
    this.sid = options.sid;
    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;
    this.templated = options.t;
    this.templateId = options.tid;
    //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.templateId != null) {
      var template = d.assets[this.templateId]  
      if(template == null) {
        alert("Unable to hasCost() object as template " + this.templateId + " cannot be found");
      } else {
        return template.hasCost();
      }
    } else {
      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(cart) {
    if(this.templateId != null) { //its a template item
      var template = d.assets[this.templateId]  
      if(template == null) {
        alert("Unable to price object as template " + this.templateId + " cannot be found");
      } else {
        return template.itemCost();
      }
    } else {
      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
        this.setUsageQty(cart);
        var qty = this.usageQty;

        var dec_price = d.chargeDecorationLibraries[this.decorationLibraryId];
        if(dec_price != null) {
          if(qty >= 0 && qty <= 4) {
            var rangeprice = dec_price.range1price;
          } else if (qty >= 5 && qty <= 12) {
            var rangeprice = dec_price.range2price;
          } else if (qty >= 13 && qty <= 24) {
            var rangeprice = dec_price.range3price;
          } else if (qty >= 25 && qty <= 48) {
            var rangeprice = dec_price.range4price;
          } else if (qty >= 49 && qty <= 72) {
            var rangeprice = dec_price.range5price;
          } else if (qty >= 73 && qty <= 144) {
            var rangeprice = dec_price.range6price;
          } else if (qty >= 145) {
            var rangeprice = dec_price.range7price;
          }
          
          rangeprice = rangeprice + (d.declibMarkup / 100.0) * rangeprice;
          rangeprice = rangeprice + d.aMarkupRate  * rangeprice;
          return rangeprice;
        } else {
          return 0;
        }
      }
    }
  },
  
  setUsageQty: function(cart) {
    if(cart == null) cart = d.cart; 
    var qty = 0;
    for (var i=0; i < cart.products.length; i++) {
      product = 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];
          
          if(this.templated) {
            for(var pk in area.processes) {
              var usedTemplates = area.processes[pk].getUsedTemplates();
              for(var ti = 0; ti < usedTemplates.length; ti++) {
                if(usedTemplates[ti].id == this.id) {
                  product_asset_qty++;
                }
              }
            }
          } else {
            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);
    };
    
    this.usageQty = qty;
  },
  
  applyScaleRefactor: function(mScaleRefactor, dims) {
   
    var mW = this.width * mScaleRefactor;
    var mH = this.height * mScaleRefactor;

    if(mW < dims.w) {
      dims.w = mW;
    }
    if(mH < dims.h) {
      dims.h = mH;
    }
    
    log(dims);
    return dims;
    
  },
  
  //if this is a template, we will get the template items then call the callback for each item
  getForAddToDesigner: function A_getForAddToDesigner(defaultOpts, processArea, callback) {
    if(this.templated) {
        //call the server to get the list of assets.....
        var self = this;
        
        new Ajax.Request(d.ajaxUrl("/ppr/shared/library/get_template_details/" + this.id), {
          method: 'get',
          evalJSON: true,
          onSuccess: function(transport) {
            var assetData = eval(transport.responseText);
            if(assetData == null) {
              alert("No Template Data Returned");
              return;
            }
            self.scaleDesignAssets(assetData, defaultOpts, processArea, callback);
            
          },
          
          onFailure: function() {
            alert("Server error occured");  
          }
        });
    } else {
        callback(this, defaultOpts);
    }
  },
  
  scaleDesignAssets: function(assetData, defaultOpts, processArea, callback) {
    
    //we now need to make an asset for each item...
    //this needs to be to scale to initialise the template
    var w = processArea.productProcess.fullWidth;
    var h = processArea.productProcess.fullHeight;
    var totalDims = this.scale(h, w);
    var opts = defaultOpts;
    
    if(opts.w != null) { //we have a desired width/height...
      totalDims = this.scale(opts.w, opts.h);
      delete opts.w;
      delete opts.h;
    }
    
    var mScaleRefactor = processArea.productProcess.getScaleRefactor();
    totalDims =  this.applyScaleRefactor(mScaleRefactor, totalDims); //we now have the dims of the whole template....
    var x = (processArea.productProcess.fullWidth - totalDims.w) / 2;
    var y = 0;// (processArea.productProcess.fullHeight - totalDims.h) / 2;
    
    if(opts.t != null) { //we have a desired top/left
      x = opts.l;
      y = opts.t;
      delete opts.l;
      delete opts.t;
    }
    
    //TODO: shift the z-index of each item above this opts.z so it will work...
    
    var z = (opts.z == null) ? 0 : opts.z;
    var rescale = parseFloat(totalDims.w) / parseFloat(this.width); 
    
    if(assetData.length == 0) { //its not a template...
      var newAsset;
      var opts = {
        t: y ,
        l: x ,
        w: this.width * rescale,
        h: this.height * rescale,
        taid: this.id,
        z: z
      };
      if(this.imageType == 2) {
        opts.emb="1";
        opts.stc = this.stitchCount;
        opts.cw = this.colorWays.length > 0 ? 0 : null;
      }

      if(!callback(this, opts)) {
        return;  
      } 
    } else {
    
      var templateRescale = this.width / this.metaData.width;
      var newAsset;
      for(var i=assetData.length-1; i >=0 ; i--) {
        var opts = {
          t: y + assetData[i].meta.y * rescale * templateRescale,
          l: x + assetData[i].meta.x * rescale * templateRescale,
          w: assetData[i].meta.w * rescale * templateRescale,
          h: assetData[i].meta.h * rescale * templateRescale,
          taid: this.id,
          z: z
        };
        z++;
        
        if(assetData[i].type == 202) {
          newAsset = d.addAssetObj(new TemplateTextAsset(assetData[i], d));
          opts.text = assetData[i].meta.text;
          opts.font = assetData[i].meta.font_face;
          opts.b = assetData[i].meta.slots[0].bold == "true" ? "1" : "0";
          opts.i = assetData[i].meta.slots[0].italics == "true" ? "1" : "0";
        } else {
          newAsset = d.addAsset(assetData[i]);
        }
          
        if(!callback(newAsset, opts)) {
          return;  
        } 
      }
    }
  },
  
  getTextTemplateColors: function(existingColors) {
    //loop through the defn finding colors and checking if in existing colors...
    var colors = new MapList();
    var key = '';
    for(var i=0; i < this.metaData.slot_count; i++) {
      var slotData = this.metaData.slots[i];
      if(slotData.fill != null) {
        if(slotData.fill.type == "cdrFountainFill") {
          for(var j=0; j < slotData.fill.shades.length; j++) {
            this.addColor(colors, i + ".f.s." + j, existingColors, slotData.fill.shades[j].color);
          }
        } else if(slotData.fill.type == "cdrUniformFill") {
          this.addColor(colors, i + ".f", existingColors, slotData.fill.color);
        }
      }
      if((slotData.effects != null)&&(slotData.effects.extrude != null)) {
        if((slotData.effects.extrude.shading == "cdrExtrudeSolidFill") || (slotData.effects.extrude.shading == "cdrExtrudeColorShading")) {
          this.addColor(colors, i + ".e.e.b", existingColors, slotData.effects.extrude.base_color);
        }
        if(slotData.effects.extrude.shading == "cdrExtrudeColorShading") {
          this.addColor(colors, i + ".e.e.s", existingColors, slotData.effects.extrude.shading_color);
        }
      }
      if((slotData.outline != null)&&(slotData.outline.width > 0)) {
        this.addColor(colors, i + ".o", existingColors, slotData.outline.color);
      }
    }
    return colors;
  },
  
  addColor: function(colors, key, existingColors, defn) {
    if((existingColors != null) && (existingColors[key] != null)) {
      colors.add(new PikiColor(existingColors[key], key))
    } else {
      colors.add(new PikiColor(defn, key))
    }
  },
  
  isVector: function() {
    if(this.imageType == 1) return true;
    if(this.imageType == 3 || this.imageType == 4) { //are all the components vector? 
      if(this.metaData != null && this.metaData.type == "vector") return true;
    }
    return false;
  }
});

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


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


var TemplateTextAsset = Class.create({
  CLASSDEF : {
      name:  'TemplateTextAsset',
      parent: Asset
  },
  initialize: function TT_initialize(options) {
    TemplateTextAsset.parentClass.constructor().call(this, options);
    this.url = d.pathPrefix + "/images/mp/text_icon.gif";
  },
  
  getItemType: function TT_getItemType() {
    return 4;
  }
});


var NoAsset = Class.create({
  CLASSDEF : {
      name:  'NoAsset',
      parent: Asset
  },
  initialize: function TT_initialize() {
    NoAsset.parentClass.constructor().call(this, {id:0, width:1, height:1, sURL: d.pathPrefix + "/images/trans.gif", tUrl: d.pathPrefix + "/images/trans.gif", url: d.pathPrefix + "/images/trans.gif" });
  },
  
  getItemType: function TT_getItemType() {
    return 5;
  }
});



/* 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="60" /></li>' +
				'<li id="mp1_[ID]_color_row" style="display:none;" class="colors"><label>' + ml('colors') + '</label><ol id="mp1_[ID]_colors" class="color_list"></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') + '">'+
				'<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 id="mp1_[ID]_per_container" class="lock_container" style="display: none;" tooltip="' + ml('If you allow personalization the field can be edited without opening the designer.') + '">'+
				'<ol>'+
					'<li>'+
					'<input type="checkbox" name="mp1_[ID]_personalize" id="mp1_[ID]_personalize" />'+
					'<label for="mp1_[ID]_personalize">' + ml('Allow Personalizion') + '</label>'+
					'</li>'+
					'<li id="mp1_[ID]_personalize_caption_container" style="display:none;">'+
					'<label for="mp1_[ID]_personalize_caption">' + ml('Caption') + '</label>'+
					'<input type="text" name="mp1_[ID]_personalize_caption" id="mp1_[ID]_personalize_caption" />'+
					'</li>'+
					'<li id="mp1_[ID]_allow_replace_clear_container" style="display:none;" class="replace">'+
					'<input type="checkbox" name="mp1_[ID]_allow_replace_clear" id="mp1_[ID]_allow_replace_clear" />'+
					'<label for="mp1_[ID]_allow_replace_clear">' + ml('Allow Replace') + '</label>'+
					'</li>'+
				'</ol>'+
			'</div>'+
		
			'<div class="et_container" id="mp1_[ID]_fc_effects_content">'+
				'<ul class="et_tabs">'+
					 '<li id="i_effects_[ID]" class="et_selected_tab"><a href="#" id="ic_effects_[ID]">' + ml("Effects") + '</a></li>' +
					'<li id="i_border_[ID]"><a href="#" id="ic_border_[ID]">' + ml("Border") + '</a></li>' +
				'</ul>'+
			
				'<div class="et_content">'+
					'<div id="i_effects_tab_[ID]" class="et_etab">' +
						//'<table cellspacing="0">' +
						  //'<tr>' +
							//'<td width="160">' +
							'<div class="et_elist">' +
								'<div class="et_scroll" id="i_effects_list_[ID]">' +
									'Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/>Some Effects<br/> ' +
								'</div>' +
							  '</div>' +
							//'</td>' +
							//'<td width="190">' +
							//  '<div >' +
								'<div class="et_props" id="i_aeffects_list_[ID]">' +
								  ml('No Effects Applied: Click on an effect from the list to the left to apply it to your image.')  +
								'</div>' +
							//  '</div>' +
							//'</td>' +
						  //'</tr>' +
						//'</table>' +
					'</div>' +
					'<div id="i_border_tab_[ID]" style="display:none;">' +
						//'<table>' +
						  //'<tr>' +
							//'<td width="340">' +
							  '<div class="et_list">' +
								'<div class="et_scroll" id="i_borders_list_[ID]">' +
								'Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/>Some Borders<br/> ' +
								'</div>' +
							  '</div>' +
							//'</td>' +
						  //'</tr>' +
						//'</table>' +
					'</div>' +
				'</div>'+
			'</div>'+
		 /* close of the advanced tab */
        '</div>'+
      '</div>' +
    '</div>';

/* text */
var type_1 = '<div id="mp1_[ID]" class="managepaneoff" stopdeselect="true">' +
      '<div class="managepane_header" id="mp1_[ID]_bar" style="cursor:pointer;">' +
        '<ul class="mp_title">' +
			'' +
			'<li id="mp1_[ID]_ico_c" class="icon"><span id="mp1_[ID]_ico_alert" class="alert_icon_none"></span><img id="mp1_[ID]_stimg" /></li>' +
			'<li class="title"><span class="managepane_title" id="mp1_[ID]_title"></span></li>' + 
		  '<li id="mp1_[ID]_fc_dim" class="dimension">' +
				'<span id="mp1_[ID]_dim_value"></span>' +
			'</li>'+
        '</ul>'+
        '<ul id="mp1_[ID]_controls" class="mp_control">'+
          '<li class="lock_icon" id="mp1_[ID]_lock_icon" tooltip="' + ml('This text is locked so it will not be able to be changed when users customise this design') + '">locked</li>'+
          '<li><a href="#" id="mp1_[ID]_edit" class="change" tooltip="' + ml('Edit text') + '">' + ml('edit text') + '</a></li>' +
          '<li class="up"><span id="mp1_[ID]_zup" tooltip="' + ml('Move this text higher up so it prints on top of other design elements') + '">up</span></li>'+
          '<li class="down"><span id="mp1_[ID]_zdown" tooltip="' + ml('Move this text lower down so it prints below other design elements') + '">down</span></li>' +   
		  '<li><a href="#" id="mp1_[ID]_remove" class="delete" tooltip="' + ml('Remove this text') + '">delete</a></li>' +
        '</ul>' + 
      '</div>' +
      '<div id="mp1_[ID]_body" style="display:none;" class="mp_body_below">' +
        /* ---- */
		'<div class="advanced_button" id="mp1_[ID]_fc_adv"><a href="#" id="mp1_[ID]_adv_link"><span>' + ml("advanced") + '<!--<img src="[PATH_PREFIX]/images/mp/arrow_down.gif" id="mp1_[ID]_adv" border="0"/>--></span></a></div>'+
        
		'<div class="managepane_body">' +
			'<div class="pane_alerts" id="mp1_[ID]_alerts" style="display:none;"><ol id="mp1_[ID]_a_error" class="error" style="display:none;"></ol><ol id="mp1_[ID]_a_warn" class="warn" style="display:none;"></ol><ol id="mp1_[ID]_a_notice" class="notice" style="display:none;"></ol></div>' +
			/*==========layer right stuff */
			'<div class="layer_container">' +
				'<div class="layer_right">' +
					'<ul class="layer_controls">' +
						
						/* ----move */
						'<li id="mp1_[ID]_fc_movement" class="movement">'+
							'<label>' + ml("position") + '</label>' +
							'<div>'+
								'<!--<img src="[PATH_PREFIX]/images/mp/movement.jpg" id="mp1_[ID]_move" style="display: block;" />-->'+
								'<ul>'+
									/* ----row 1 */
									'<li class="t_l"><a href="#" id="mp1_[ID]_move_0" tooltip="' + ml('Move text up and to the left') + '">&nbsp;</a></li>'+
									'<li class="t_c"><a href="#" id="mp1_[ID]_move_1" tooltip="' + ml('Move text up') + '">&nbsp;</a></li>'+
									'<li class="t_r"><a href="#" id="mp1_[ID]_move_2" tooltip="' + ml('Move text up and to the right') + '">&nbsp;</a></li>'+
									/* ----row 2 */
									'<li class="c_l"><a href="#" id="mp1_[ID]_move_3" tooltip="' + ml('Move text to the left') + '">&nbsp;</a></li>'+
									'<li class="c_c"><a href="#" id="mp1_[ID]_move_4">&nbsp;</a></li>'+
									'<li class="c_r"><a href="#" id="mp1_[ID]_move_5" tooltip="' + ml('Move text to the right') + '">&nbsp;</a></li>'+
									/* ----row 3 */
									'<li class="b_l"><a href="#" id="mp1_[ID]_move_6" tooltip="' + ml('Move text down and to the left') + '">&nbsp;</a></li>'+
									'<li class="b_c"><a href="#" id="mp1_[ID]_move_7" tooltip="' + ml('Move text down') + '">&nbsp;</a></li>'+
									'<li class="b_r"><a href="#" id="mp1_[ID]_move_8" tooltip="' + ml('Move text down and to the right') + '">&nbsp;</a></li>'+
								'</ul>'+
							'</div>'+
						'</li>'+
						
						/* ----size */
						'<li id="mp1_[ID]_fc_size_arrows" class="arrows">' +
							'<label>'+ml("size")+'</label>'+
							'<ol>' +
								'<li class="arrow_up"><a href="#" id="mp1_[ID]_sizeup" tooltip="' + ml('Increase text size') + '">&nbsp;</a></li>' +
								'<li class="arrow_down"><a href="#" id="mp1_[ID]_sizedown" tooltip="' + ml('Decrease text size') + '">&nbsp;</a></li>' +
							'</ol>' +
						'</li>' +
						
						/* ----rotate */
						'<li id="mp1_[ID]_fc_rotate" class="rotate">'+
							'<label>'+ml("rotate")+'</label>'+
							'<div id="mp1_[ID]_rotate_container" class="rotate_container">' +
								'<img src="[PATH_PREFIX]/images/mp/rotate_circle_with_line.png" id="mp1_[ID]_rotate" style="position: absolute; left: 15px; top: 13px;"/><br/>' +
								'<a class="rotate_left" id="mp1_[ID]_rotate_l" tooltip="' + ml('Rotate text to the left') + '">&nbsp;</a>' +
								'<a class="rotate_right" id="mp1_[ID]_rotate_r" tooltip="' + ml('Rotate text to the right') + '">&nbsp;</a>'+
								'<span class="degrees"><input type="text" value="0" id="mp1_[ID]_rotate_t"/>&deg;</span>'+
							'</div>' +
						
						'</li>' +
						/* align */
						'<li class="center_both">'+
							'<label>' + ml("align") + '</label>' +
							'<ol>'+
								'<li class="center_h"><a href="#" id="mp1_[ID]_center_h" tooltip="' + ml('Center the text horizontally') + '">&nbsp;</a></li>'+
								'<li class="center_v"><a href="#" id="mp1_[ID]_center_v" tooltip="' + ml('Center the text vertically') + '">&nbsp;</a></li>'+
								'<li class="center"><a href="#" id="mp1_[ID]_center" tooltip="' + ml('Center the text both horizontally and vertically') + '">&nbsp;</a></li>'+
							'</ol>'+
						'</li>'+
						
						/* teamname max width */
						'<li id="mp1_[ID]_fc_tn_width" style="display:none;" tooltip="' + ml('Specify the maximum width of the text to determine the final printing size of longer text') + '">' +
							'<label>'+ml("max 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" 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 class="mp_align">'+
								'<li class="mp_align_left" tooltip="' + ml('Left align text when it spans multiple lines') + '"><span id="mp1_[ID]_all" 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]_alc" 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]_alr" 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>' +
						
            /* teamname label */
            '<span id="mp1_[ID]_fc_tn_label" style="display:none;" tooltip="' + ml('The label used to match teamname data. If two teamnames have the same label they will use the same data.') + '" class="t_n_other">' +
              '<label>' + ml('label') + '</label><input id="mp1_[ID]_tn_label" type="text" size="10" />' +
            '</span>' +
            /* teamname size */
            '<span id="mp1_[ID]_fc_tn_size" style="display:none;" tooltip="' + ml('The size of the teamname data field.') + '" class="t_n_other">' +
              '<label>' + ml('field size') + '</label><input id="mp1_[ID]_tn_size" type="text" size="2" />' +
            '</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 id="mp1_[ID]_per_container" class="lock_container" style="display: none;" tooltip="' + ml('If you allow personalization the field can be edited without opening the designer.') + '">'+
				'<ol>'+
					'<li>'+
					'<input type="checkbox" name="mp1_[ID]_personalize" id="mp1_[ID]_personalize" />'+
					'<label for="mp1_[ID]_personalize">' + ml('Allow Personalizion') + '</label>'+
					'</li>'+
					'<li id="mp1_[ID]_personalize_caption_container" style="display:none;">'+
					'<label for="mp1_[ID]_personalize_caption" style="margin-left: 30px;">' + ml('Caption') + '</label>'+
					'<input type="text" name="mp1_[ID]_personalize_caption" id="mp1_[ID]_personalize_caption" />'+
					'</li>'+
					/* max width */
          '<li id="mp1_[ID]_fc_max_width" style="display:none;" tooltip="' + ml('Specify the maximum width of the text when displayed in personalize page') + '">' +
            '<label><input type="checkbox" name="mp1_[ID]_use_max_width" id="mp1_[ID]_use_max_width" />' +ml("max width")+'</label>'+
            '<div id="max_width_[ID]_t" style="width: 88px;" class="et_bar">' +
              '<div id="max_width_[ID]_s" class="et_grab"> </div>' +
            '</div>'+
          '</li>' +
				'</ol>'+
			'</div>'+
			
		
			'<div class="et_container">' +
				'<ul id="advanced_pane_tabs_[ID]" class="et_tabs">' +
				  '<li id="et_effects_[ID]" class="et_selected_tab"><a href="#" id="etc_effects_[ID]">' + ml("Effects") + '</a></li>' +
				  '<li id="et_gradient_[ID]" class="et_unselected_tab"><a href="#" id="etc_gradient_[ID]">' + ml("Gradient") + '</a></li>' +
				  '<li id="et_shape_[ID]" class="et_unselected_tab"><a href="#" id="etc_shape_[ID]">' + ml("Warp") + '</a></li>' +
				  '<li id="et_stroke_[ID]" class="et_unselected_tab"><a href="#" id="etc_stroke_[ID]">' + ml("Outline") + '</a></li>' +
				'</ul>' +
				'<div class="et_content">'+
					
					/* start transplanted content */
					
					 '<div id="ep_effects_[ID]" class="effects_pane">' +
					  /* effect type */
					  '<ul><li><label>effect type</label><select class="mp_select" id="ep_effecttype_[ID]"><option value="0">' + ml("None") + '</option><option value="1">' + ml("Shadow") + '</option><option value="2">' + ml("Glow") + '</option></select></li></ul>' +
					  '<div class="effects_container">'+
						'<ul>'+
						  /* shadow options */
						  '<li id="shadow_effects_[ID]" style="display:none;">' +
							'<label>offset</label>'+
							'<div class="pane_offset">'+
							  '<div id="mp_[ID]_offset" style="background: url([PATH_PREFIX]/images/mp/rotate_circle.gif) 0 0 no-repeat; height: 36px; width: 36px; " align="absmiddle">' +
								'<img id="mp_[ID]_offset_handle" src="[PATH_PREFIX]/images/sizer.gif" style="cursor: pointer; left: 15px; top: 15px; position: relative; " />'+
							  '</div>'+
							'</div>'+
						  '</li>'+
						  /* glow options */
						  '<li id="glow_effects_[ID]" style="display: none;">' +
							'<label>' + ml("glow strength") + '</label>' +
							'<div id="ep_g_str_[ID]_t" style="width: 88px; " class="et_bar">' +
							  '<div id="ep_g_str_[ID]_s" class="et_grab"> </div>' +
							'</div>' +
						  '</li>' +
	
						  /* color */
						  '<li>'+
							'<label>' + ml("color") + '</label><div class="mp_color_button"><div id="ep_effectcolor_[ID]"><span class="drop_arrow">&nbsp;</span></div></div>' +
						  '</li>'+
	
						  /* blur */
						  '<li>'+
							'<label>' + ml("blur") + '</label>'+
							'<div id="ep_b_str_[ID]_t" style="width: 88px;" class="et_bar">' +
							  '<div id="ep_b_str_[ID]_s" class="et_grab"> </div>' +
							'</div>'+
						  '</li>'+
						'</ul>'+
					  '</div>'+
					'</div>' +
	
					/* gradient */
					'<div id="ep_gradient_[ID]" style="display:none;" class="effects_pane">' +
					  '<ul>'+
						'<li><label>' + ml("gradient") + '</label><select class="mp_select" style="margin-bottom:1px;margin-top:2px;" id="ep_gradienttype_[ID]"><option value="0">None</option><option value="1">Vertical</option><option value="2">Horizontal</option></select></li>' +
						'<li><label>' + ml("color") + '</label><div class="mp_color_button"><div id="ep_gradient_color_[ID]"><span class="drop_arrow">&nbsp;</span></div></div></li>' +
					  '</ul>'+
					'</div>' +
	
					/* warp */
					'<div id="ep_shape_[ID]" style="display:none;" class="effects_pane">' +
					  '<ul>'+
						'<li><label>' + ml("Select Shape") + '</label><span id="warp_shape_select_[ID]" class="warp_shape_select"><select class="mp_select" id="ep_shape_sel_down_[ID]"></select></span></li>' +
						'<li id="ep_s_x_[ID]" class="mp_text_pane"><label>' + ml("width") + '</label>' +
						  '<div id="ep_x_str_[ID]_t" style="width:88px;" class="et_bar">' +
							'<div id="ep_x_str_[ID]_s" class="et_grab"> </div>' +
						  '</div>' +
						'</li>' +
						'<li id="ep_s_y_[ID]" class="mp_text_pane"><label>' + ml("height") + '</label>' +
						  '<div id="ep_y_str_[ID]_t" style=" width: 88px;" class="et_bar">' +
							'<div id="ep_y_str_[ID]_s" class="et_grab"> </div>' +
						  '</div>' +
						'</li>' +
					  '</ul>'+
					'</div>' +
	
					/* stroke */
					'<div id="ep_stroke_[ID]" style="display:none;" class="effects_pane">' +
					  '<ul>'+
						'<li>'+
						  '<label>width</label>'+
						  '<div id="ep_stroke_width_[ID]_t" style="width: 88px;" class="et_bar">' +
							'<div id="ep_stroke_width_[ID]_s" class="et_grab"> </div>' +
						  '</div>' + 
						'</li>' +
						'<li class="mp_text_pane">' +
						  '<label>' + ml("color") + '</label><div class="mp_color_button"><div id="ep_stroke_color_[ID]"><span class="drop_arrow">&nbsp;</span></div></div>' +
						'</li>'+
					  '</ul>'+
					'</div>' +
					/* end transplanted content */
				
				'</div>'+
			'</div>'+
		  /*-------*/
		'</div>'+
	  '</div>' +
      '<div id="mp1_[ID]_edit_text_container" class="edit_text_container" style="display: none;">' +
        '<textarea id="mp1_[ID]_edit_text" rows="5" cols="55" class="mp_textarea"></textarea>' +
        /*'<img src="[PATH_PREFIX]/images/mp/edit_text.gif" id="mp1_[ID]_save_edit"/>'+*/
        '<input type="button" class="button" value="' + ml('change text') + '" id="mp1_[ID]_save_edit" />'+
        /*'<img src="[PATH_PREFIX]/images/mp/cancel_text.gif" id="mp1_[ID]_cancel_edit"/>' +*/
        '<a href="#" class="cancel_button" id="mp1_[ID]_cancel_edit">' + ml('cancel') + '</a>'+
      '</div>' +
    '</div>';
    
    
/* placeholder */
var type_3 = '<div id="mp1_[ID]" class="managepaneoff" stopdeselect="true">' +
      '<div class="managepane_header" id="mp1_[ID]_bar" style="cursor:pointer;">' +
        '<ul class="mp_title">' +
          '' +
          '<li id="mp1_[ID]_ico_c" class="icon"><span id="mp1_[ID]_ico_alert" class="alert_icon_none"></span><img id="mp1_[ID]_stimg" /></li>' +
          '<li class="title"><span class="managepane_title" id="mp1_[ID]_title"></span></li>' + 
          '<li id="mp1_[ID]_fc_dim" class="dimension">' +
            '<span id="mp1_[ID]_dim_value"></span>' +
          '</li>'+
        '</ul>'+
        '<ul id="mp1_[ID]_controls" class="mp_control">'+
          '<li class="lock_icon" id="mp1_[ID]_lock_icon" tooltip="' + ml('This placeholder is locked so it will not be able to be changed when users customise this design') + '">locked</li>'+
          '<li><a href="#" id="mp1_[ID]_edit" class="change" tooltip="' + ml('Edit text') + '">' + ml('edit text') + '</a></li>' +
          '<li class="up"><span id="mp1_[ID]_zup" tooltip="' + ml('Move this placeholder higher up so it prints on top of other design elements') + '">up</span></li>'+
          '<li class="down"><span id="mp1_[ID]_zdown" tooltip="' + ml('Move this placeholder lower down so it prints below other design elements') + '">down</span></li>' +   
          '<li><a href="#" id="mp1_[ID]_remove" class="delete" tooltip="' + ml('Remove this placeholder') + '">delete</a></li>' +
        '</ul>' + 
      '</div>' +
      '<div id="mp1_[ID]_body" style="display:none;" class="mp_body_below">' +
        /* ---- */
		       
      '<div class="managepane_body">' +
        '<div class="pane_alerts" id="mp1_[ID]_alerts" style="display:none;"><ol id="mp1_[ID]_a_error" class="error" style="display:none;"></ol><ol id="mp1_[ID]_a_warn" class="warn" style="display:none;"></ol><ol id="mp1_[ID]_a_notice" class="notice" style="display:none;"></ol></div>' +
        /*==========layer right stuff */
        '<div class="layer_container">' +
          '<div class="layer_right">' +
            '<ul class="layer_controls">' +
              
              /* ----move */
              '<li id="mp1_[ID]_fc_movement" class="movement">'+
                '<label>' + ml("position") + '</label>' +
                '<div>'+
                  '<!--<img src="[PATH_PREFIX]/images/mp/movement.jpg" id="mp1_[ID]_move" style="display: block;" />-->'+
                  '<ul>'+
                    /* ----row 1 */
                    '<li class="t_l"><a href="#" id="mp1_[ID]_move_0" tooltip="' + ml('Move text up and to the left') + '">&nbsp;</a></li>'+
                    '<li class="t_c"><a href="#" id="mp1_[ID]_move_1" tooltip="' + ml('Move text up') + '">&nbsp;</a></li>'+
                    '<li class="t_r"><a href="#" id="mp1_[ID]_move_2" tooltip="' + ml('Move text up and to the right') + '">&nbsp;</a></li>'+
                    /* ----row 2 */
                    '<li class="c_l"><a href="#" id="mp1_[ID]_move_3" tooltip="' + ml('Move text to the left') + '">&nbsp;</a></li>'+
                    '<li class="c_c"><a href="#" id="mp1_[ID]_move_4">&nbsp;</a></li>'+
                    '<li class="c_r"><a href="#" id="mp1_[ID]_move_5" tooltip="' + ml('Move text to the right') + '">&nbsp;</a></li>'+
                    /* ----row 3 */
                    '<li class="b_l"><a href="#" id="mp1_[ID]_move_6" tooltip="' + ml('Move text down and to the left') + '">&nbsp;</a></li>'+
                    '<li class="b_c"><a href="#" id="mp1_[ID]_move_7" tooltip="' + ml('Move text down') + '">&nbsp;</a></li>'+
                    '<li class="b_r"><a href="#" id="mp1_[ID]_move_8" tooltip="' + ml('Move text down and to the right') + '">&nbsp;</a></li>'+
                  '</ul>'+
                '</div>'+
              '</li>'+
              
              /* size */
              '<li id="mp1_[ID]_fc_size_arrows" class="arrows">' +
                '<label>' + ml("size") + '</label>' +
                '<ol>'+
                   '<li class="arrow_up"><a href="#" id="mp1_[ID]_sizeup" tooltip="' + ml('Increase placeholder size') + '">&nbsp;</a></li>' +
                   '<li class="arrow_down"><a href="#" id="mp1_[ID]_sizedown" tooltip="' + ml('Decrease placeholder size') + '">&nbsp;</a></li>' +
                '</ol>'+
              '</li>' +
              
              /* align */
              '<li class="center_both">'+
                '<label>' + ml("align") + '</label>' +
                '<ol>'+
                  '<li class="center_h"><a href="#" id="mp1_[ID]_center_h" tooltip="' + ml('Center the text horizontally') + '">&nbsp;</a></li>'+
                  '<li class="center_v"><a href="#" id="mp1_[ID]_center_v" tooltip="' + ml('Center the text vertically') + '">&nbsp;</a></li>'+
                  '<li class="center"><a href="#" id="mp1_[ID]_center" tooltip="' + ml('Center the text both horizontally and vertically') + '">&nbsp;</a></li>'+
                '</ol>'+
              '</li>'+
            '</ul>' +
          '</div>' +
          '<div class="layer_left">' +
            '<div class="font_options">' + 
              '<label>' + ml('placeholder options') + '</label>'+
              /* ----color */
              '<div class="cell">'+
              '<ul class="placeholder_options">'+
                '<li class="mp_change_to_image" id="mp1_[ID]_convert_to_image" tooltip="' + ml('Convert to image') + '"><span>' + ml('Convert to image') + '</span><span>&nbsp;</span></li>' +
                '<li class="mp_change_to_text" id="mp1_[ID]_convert_to_text" tooltip="' + ml('Convert to text') + '"><span>' + ml('Convert to text') + '</span><span>&nbsp;</span></li>' +
               '</ul>'+
              '</div>' +
            '</div>' +
            '<div id="mp1_[ID]_lock_container" class="lock_container" style="display: none;" tooltip="' + ml('This will stop users customizing this design from changing this text') + '">'+
              '<ol>'+
                '<li>'+
                '<input type="checkbox" name="mp1_[ID]_lock" id="mp1_[ID]_lock" />'+
                '<label for="mp1_[ID]_lock">' + ml('lock this layer') + '</label>'+
                '</li>'+
              '</ol>'+
            '</div>' +
          '</div>' + /*close layer left */
        '</div>' + /*close layer container */
      '</div>' +
        
	  '</div>' +
      '<div id="mp1_[ID]_edit_text_container" class="edit_text_container" style="display: none;">' +
        '<textarea id="mp1_[ID]_edit_text" rows="5" cols="55" class="mp_textarea"></textarea>' +
        /*'<img src="[PATH_PREFIX]/images/mp/edit_text.gif" id="mp1_[ID]_save_edit"/>'+*/
        '<input type="button" class="button" value="' + ml('change text') + '" id="mp1_[ID]_save_edit" />'+
        /*'<img src="[PATH_PREFIX]/images/mp/cancel_text.gif" id="mp1_[ID]_cancel_edit"/>' +*/
        '<a href="#" class="cancel_button" id="mp1_[ID]_cancel_edit">' + ml('cancel') + '</a>'+
      '</div>' +
    '</div>';

    
/* template text */
var type_4 = '<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>'+
					'</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>'+
				'</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_font_selector" id="mp1_[ID]_font_button" 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>' +
						'</div>' +
						'<div class="cell">'+
						  '<div class="mp_color_button" tooltip="' + ml('Choose text color') + '" id="mp1_[ID]_colors"></div>' +
						'</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 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 id="mp1_[ID]_per_container" class="lock_container" style="display: none;" tooltip="' + ml('If you allow personalization the field can be edited without opening the designer.') + '">'+
          '<ol>'+
            '<li>'+
            '<input type="checkbox" name="mp1_[ID]_personalize" id="mp1_[ID]_personalize" />'+
            '<label for="mp1_[ID]_personalize">' + ml('Allow Personalizion') + '</label>'+
            '</li>'+
            '<li id="mp1_[ID]_personalize_caption_container" style="display:none;">'+
            '<label for="mp1_[ID]_personalize_caption" style="margin-left: 30px;">' + ml('Caption') + '</label>'+
            '<input type="text" name="mp1_[ID]_personalize_caption" id="mp1_[ID]_personalize_caption" />'+
            '</li>'+
          '</ol>'+
        '</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,
  type_1,
  type_3,
  type_4,
  type_1,
  type_1
];




var mtype_0 = '<li class="entry" id="mp1_[ID]">' +
					'<label id="mp_[ID]_caption">Image Options</label>' + 
					'<div class="personalize_image">' +
						'<a class="ps_image_box" href="#" id="mp_[ID]_a"><img id="mp_[ID]_img" src="/images/trans.gif" id=""/></a>' +
						'<div id="mp1_[ID]_color_row" style="display:none;" class="colors"><ol id="mp1_[ID]_colors" class="color_list"></ol></div>'+
						'<ul class="details">' +
							'<li id="mp_[ID]_ri_container"><a href="#" id="mp_[ID]_ri">replace image</a></li>' +
							'<li id="mp_[ID]_ci_container"><a href="#" id="mp_[ID]_ci">clear image</a></li>' +
						'</ul>' +
					'</div>' +
				'</li>';


var mtype_1 = '<div id="mp1_[ID]">TEXT HERE</div>';
var mtype_4 = '<li class="entry" id="mp1_[ID]">' +
            '<label id="mp_[ID]_caption">Add Text</label>' +
            '<div class="personalize_text">' + 
            '<input id="mp_[ID]_txt" type="text" size="12" style="display:none;"/><textarea id="mp_[ID]_txta" rows="2" cols="12" style="display:none;"></textarea>' +
              '<ul class="color_panel" id="mp_[ID]_colors">' +
              '</ul><a id="mp_[ID]_apply" class="colorbutton" style="display:none;"><span>apply</span></a>' +
            '</div>' +
          '</li>';

var mtypes = [
  mtype_0,
  mtype_4,
  mtype_4,
  mtype_1,
  mtype_4,
  mtype_4,
  mtype_4
];


var MultiSelect = Class.create({
  CLASSDEF: {
      name: 'MultiSelect',
      parent: Item
  },
  
  initialize: function MultiSelect_initialize(cViewArea) {
    this.cViewArea = cViewArea;
    this.items = new MapList(this);;
    this.el = null;
    this.allowHandleResizing = true;
    this.allowResize = true;
    this.noRotation = true;
    this.mouseDownEvent = this.mouseDown.bindAsEventListener(this); 
    this.eventResizeStart =  this.resizeStart.bindAsEventListener(this);
    this.eventResizeMouseUp =  this.resizeFinished.bindAsEventListener(this);
    this.eventResizeMouseMove =  this.resizeMove.bindAsEventListener(this);
    this.elId = "sel_" + d.getNextId();
  },
  
  addItem: function ms_addItem(item) {
    this.layoutBounds = null; //reset..
    if(item.multiSelected) {
      this.items.remove(item.id);
      item.setMultiSelected(false);
    } else {
      this.items.add(item);
      item.setMultiSelected(true);
    }
    this.toCanvasDims();
    this.initPositioningData();
  },
  
  showSelection: function ms_showSelection() {
    if(this.el == null) {
      this.buildSelection();
    } else {
      this.setBounds();
    }
    this.addHandles();
  },
  
  toCanvasDims: function ms_getBounds() {
    if(this.layoutBounds == null) {
      this.initPositioningData();
    }
    //we now have the bounds...
    return d.applyZoom(this.layoutBounds);
  },
  
  initPositioningData: function msIPD() {
    //find the max/min size each item can change
    //store the positions as % of overall selection size...
    var t,l,b,r;
    t=l=b=r=null;
    for(var i=0; i < this.items.list.length; i++) {
      var dims = this.items.list[i].toCanvasDims(0);
      if(t == null) {
        t = dims.t;
        l = dims.l;
        b = dims.t + dims.h;
        r = dims.l + dims.w;
      } else {
        if(t > dims.t) t = dims.t;
        if(b < dims.t + dims.h) b = dims.t + dims.h;
        if(l > dims.l) l = dims.l;
        if(r < dims.l + dims.w) r = dims.l + dims.w;
      }
      this.items.list[i].msSelDims = dims;
    }
    
    this.layoutBounds = { 
      t:t,
      l:l,
      w:r-l,
      h:b-t
    };
    this.layoutBounds.a = this.layoutBounds.h / this.layoutBounds.w;
    
    var minUp = 999999;
    var minDown = 999999;
    
    
    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      //convert to relative to selection
      item.msSelDims.t -= this.layoutBounds.t;
      item.msSelDims.l -= this.layoutBounds.l;
      
      //define as percent of selection
      item.msSelDims.pt = item.msSelDims.t / this.layoutBounds.h;
      item.msSelDims.ph = item.msSelDims.h / this.layoutBounds.h;
      item.msSelDims.pl = item.msSelDims.l / this.layoutBounds.w;
      item.msSelDims.pw = item.msSelDims.w / this.layoutBounds.w;
      
      //init size constraints...
      var sc = item.getSizeConstraints();
      if(sc != null) {
        //how much can we increase in size?
        var inc = (sc.max.h - item.msSelDims.h) / item.msSelDims.h;
        if(inc < minUp) {
          minUp = inc; 
        }
        inc = (item.msSelDims.h - sc.min.h) / item.msSelDims.h;
        if(inc < minDown) {
          minDown = inc; 
        }
      }
    }
    if((minUp != 999999)&&(minDown != 999999)) {
      this.constraints = {
        max: {w: (minUp + 1.0) * this.layoutBounds.w, h: (minUp + 1.0) * this.layoutBounds.h},
        min: {w: (1.0 - minDown) * this.layoutBounds.w, h: (1.0 - minDown) * this.layoutBounds.h}
      }
    } else {
      this.constraints = null;
    }
  },
  
  buildSelection: function ms_buildSelection() {
    this.el = document.createElement('div');
    this.el.style.zIndex = 1000;
    this.el.style.border = "1px solid #000000";
    this.el.style.position = "absolute";
    this.el.id = "d_" + this.elId;
    this.setBounds();
    
    this.cViewArea.canvas.appendChild(this.el);
    Event.observe(this.el, "mousedown", this.mouseDownEvent);
    this.enableDragging();
  },
  
  setBounds: function ms_setBounds() {
    var dims = this.toCanvasDims();
    this.el.style.top = dims.t + "px";
    this.el.style.left = dims.l + "px";
    this.el.style.width = dims.w + "px";
    this.el.style.height = dims.h + "px";
  },
  
  //since we are floating on top, we need to proxy everything below....
  mouseDown: function ms_mouseDown(event) {
    var pointer = this.cViewArea.configuredView.getLayoutMousePosition(event);
    var i = this.cViewArea.hitTest(pointer[0], pointer[1], true);
    if(i != null) { //are we trying to actuate selection?
      if(event.ctrlKey || event.altKey || event.shiftKey) {
        this.cViewArea.multiSelectItem(i);
        Event.stop(event);
        return;
      }
    }
    //just let drag handle this...
    this.selected = true;
  },
  
  moving: function ms_moving(dragable) {
    if(!this.isMoving) {
      this.moveStarted();
      this.isMoving = true;
      for(var i=0; i < this.items.list.length; i++) {
        var item = this.items.list[i];
        item.removeHandles();
      }
    }
    this.layoutBounds.t = parseFloat(this.el.style.top);
    this.layoutBounds.l = parseFloat(this.el.style.left);
    
    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      item.el.style.top = (this.layoutBounds.t + ( this.layoutBounds.h * item.msSelDims.pt)) + "px";
      item.el.style.left = (this.layoutBounds.l + ( this.layoutBounds.w * item.msSelDims.pl)) + "px";
    }
  },
  
  moveFinished: function Item_moveFinished() {
    log("moveFinished " + this.id);
    this.isMoving = false;

    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      item.itemSized();
      item.addHandles();
      item.checkPosition();
    }
    this.addHandles();
  },
  
  cleanup: function ms_cleanup() {
    for(var i=0; i < this.items.list.length; i++) {
      this.items.list[i].setMultiSelected(false);  
    }
    Event.stopObserving(this.el, "mousedown", this.mouseDownEvent);
    this.el.remove();
    this.removeHandles();
  },
  
  setHeight: function ms_setHeight(height) {
    var retVal = true;
    if(this.constraints != null) {
      //make sure the height/width is ok...  
      var cDims = d.applyZoom(this.constraints.max);
      if(height > cDims.h) {
        retVal = false;
        height = cDims.h;
      }
    }
    var width = height / this.layoutBounds.a;
    
    this.el.style.width = width + "px";
    this.el.style.height = height + "px";
    
    return retVal;
  },
  
  setWidth: function(width) {
    return false;
  },
  
  resizeStart: function Item_resizeStart(event) {
    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      item.removeHandles();  
    }
    MultiSelect.parentClass.method("resizeStart").call(this, event);
  },
  
  
  itemSized: function() {
    this.layoutBounds = { 
      t:parseFloat(this.el.style.top),
      l:parseFloat(this.el.style.left),
      w:parseFloat(this.el.style.width),
      h:parseFloat(this.el.style.height),
      a:this.layoutBounds.a
    };
    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      item.setHeight( this.layoutBounds.h * item.msSelDims.ph);
      item.setWidth( this.layoutBounds.w * item.msSelDims.pw);
      item.el.style.top = (this.layoutBounds.t + ( this.layoutBounds.h * item.msSelDims.pt)) + "px";
      item.el.style.left = (this.layoutBounds.l + ( this.layoutBounds.w * item.msSelDims.pl)) + "px";
      item.itemSized();
      item.sizeChanged();
    }
  },
  
  resizeFinished: function(event) {
    this.releaseResizing();
    for(var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      item.resizeFinished(event, true);
    }
  }
});

var Cart = Class.create({
    CLASSDEF: {
        name: 'Cart'
    },
  
  initialize: function Cart_initialize(options, ignoreLegacy, order) {
    this.products = [];
    this.productsById = new Hash();
    this.extras = [];
    this.lineItems = [];
    this.credits = [];
    //this.productsByUid = new Hash();
    this.derivedProductsByLinkId = {};
    this.id = -1;
    this.digitizedAssets = {};
    this.applyTax = true;
    this.usedClientIds = [];
    this.couponDiscount = 0;
    this.coupon = null;
    this.order = order;
    
    if (typeof(d) != "undefined") d.cart = this;
    // options only passed in order management system
    if (options) {
      this.id = options.id;
      this.invoiceId = options.invoice_id;
      this.is_testing = options.is_testing;
      
      
      
      if (options.final_discount) {
        this.finalDiscountLocked = true;
        this.finalDiscount = parseFloat(options.final_discount);
      } else {
        this.finalDiscountLocked = false;
        this.finalDiscount = 0;
      }
      this.legacyFinalDiscount = options.final_discount;
      this.finalDiscountType = options.final_discount_type;
      
      if (options.overriding_final_total) {
        this.setFinalTotal(parseFloat(options.overriding_final_total), true);
      } else {
        this.finalTotalLocked = false;
      }
      
      this.shippingTotalLegacy = options.shipping.total;
      this.shippingTotal = options.shipping.total;
      this.shippingDays = options.shipping.days;
      this.shippingMethod = options.shipping.method_name;
      this.shippingMethodId = options.shipping.id;
      this.shippingOverride = options.shipping.override;
      this.updateShippingInfo(options.shipping.info, false, true);
      
      
      this.applyTax = options.apply_tax;
      this.legacyApplyTaxToShipping = options.apply_tax_to_shipping;
      this.legacyTaxRate = options.taxes.rate;
      this.legacyTaxNames = options.taxes.names;         
      this.legacyTaxIds = options.taxes.tax_ids;
      if(this.applyTax) {
        this.legacyTaxKey = this.buildLegacyTaxKey();
      } else {
        this.legacyTaxKey = null;
      }
      if(options.live_tax_rate != null) {
        this.setTaxInfo(options.live_tax_rate, options.live_tax_names, options.live_tax_ids);
      } else {
        this.setTaxInfo(options.taxes.rate, options.taxes.names, options.taxes.tax_ids);
      }
      
      this.couponIncludesTax = options.coupon_inc_tax;
      
      if (options.coupon) {
        this.couponDiscount = options.coupon_discount;
        this.coupon = new Coupon(options.coupon);
      }
      
      if(options.gc_amount) {
        this.gcAmount = options.gc_amount;
        this.gcCodes = options.gc_codes;
      } else {
        this.gcAmount = 0.0;
        this.gcCodes = "";
      }
      
      var products = options.products;
      if (products) {
        for (var pid in products) {
          //if (!d.productsById[pid]) { // always update products to make sure they are up to date
            log('Adding product ' + pid);
            var p = new Product(pid, products[pid]);
            d.addProduct(p);
          //}
        }
      }
      var configuredProducts = options.configured_products;
      if (configuredProducts) {
        for (var cpid in configuredProducts) {
          log('Adding configured product ' + cpid);
          var cp = new ConfiguredProduct(cpid, configuredProducts[cpid], null, null, this, ignoreLegacy);
          this.add(cp);
        }
        for(var i=0; i < this.products.length; i++) {
          this.products[i].checkPricingLocked(null, ignoreLegacy);
        }
        this.initDigitizationStatusForProducts();
      }
      
      this.shippingLegacyKey = this.buildShippingLegacyKey();
    } else {
      this.taxRate = null;
      this.taxNames = '';
      this.finalDiscount = 0;
      this.shippingTotal = 0;
      this.shippingDays = 0;
      this.shippingMethod = '';
      this.shippingMethodId = null;
      this.shippingOverride = false;
      this.gcAmount = 0.0;
      this.gcCodes = "";
      this.couponIncludesTax = true;
    }
  },
  
  setTaxInfo: function Cart_setTaxInfo(rate, names, taxIds) {
    this.taxRate = rate;
    this.taxNames = names;
    this.taxIds = taxIds;
    d.tMod = this.getTaxRate();
  },
  
  getTaxRate: function Cart_getTaxRate() {
    if(d.useLegacyPrices && this.legacyTaxRate != null && this.legacyTaxKey == this.buildLegacyTaxKey()) {
      return this.legacyTaxRate;
    } else {
      return this.taxRate;
    }
  },
  
  getTaxNames: function Cart_getTaxNames() {
    if(d.useLegacyPrices && this.legacyTaxNames != null && this.legacyTaxKey == this.buildLegacyTaxKey()) {
      return this.legacyTaxNames;
    } else {
      return this.taxNames;
    }
  },   
  
  getTaxIds: function Cart_getTaxNames() {
    if(d.useLegacyPrices && this.legacyTaxNames != null && this.legacyTaxKey == this.buildLegacyTaxKey()) {
      return this.legacyTaxIds;
    } else {
      return this.taxIds;
    }
  },
  
  getApplyTaxToShipping: function Cart_getApplyTaxToShipping() {
    if(d.useLegacyPrices && this.legacyApplyTaxToShipping != null) {
      return this.legacyApplyTaxToShipping;
    } else {
      return orderManager.applyShippingTax;
    }
  },
  
  buildLegacyTaxKey: function Cart_buildLegacyTaxKey() {
    return this.getUserAddressLegacyKey();
  },
  
  getShippingTotal: function() {
    if(!this.shippingOverride && this.shippingLegacyKey != null && this.shippingLegacyKey == this.buildShippingLegacyKey() && d.useLegacyPrices) {
      return this.shippingTotalLegacy;
    } else {
      return this.shippingTotal;
    }
  },
  
  checkLegacyPrices: function Cart_checkLegacyPrices() {     
    this.resetLegacyOption();
    if (this.shippingInfo && hashSize(this.shippingInfo) > 0) {
      this.updateShippingInfo(this.shippingInfo, false, false);
    }
    if(this.getTaxRate() != this.taxRate && this.applyTax) {
      d.registerLegacyPrice(new LegacyPriceChange("Tax Rate", -2, d.round((this.getTaxRate() - 1.0) * 100.0), d.round((this.taxRate - 1.0) * 100.0)));
    }
  },
  
  //clear any caches that may e changed when legacy setting changes..
  resetLegacyOption: function() {
    for(var i=0; i < this.products.length;i++) {
      this.products[i].resetLegacyOption();
    }
  },
  
  hasShippingInfo: function Cart_hasShippingInfo() {
    return (this.shippingInfo && this.shippingInfo.size() > 0);
  },
  
  updateShippingInfo: function Cart_updateShippingInfo(info, initMethod, keepLegacy) {
    this.shippingInfo = info;
    this.shippingDiscontinued = true;
    var self = this;
    if (this.shippingInfo && this.shippingInfo.size() > 0) {
      this.shippingInfo.each(function(meth) {
          var id = meth.id;
          if (initMethod && self.shippingMethodId == null) {
            self.shippingMethodId = id;
            self.shippingMethod = meth.name;
            self.shippingTotal = meth.cost;
            self.shippingDays = meth.days;
          }
          if (self.shippingMethodId == id) {
            self.shippingDiscontinued = false;
            if(!self.shippingOverride && self.shippingLegacyKey != null && self.shippingLegacyKey == self.buildShippingLegacyKey()) {
              if(self.shippingTotalLegacy != meth.cost) {
                d.registerLegacyPrice(new LegacyPriceChange("Shipping (" +  self.shippingMethod + ")", -1, self.shippingTotalLegacy, meth.cost));
              }
            }
            if(!self.shippingOverride) {
              self.shippingTotal = meth.cost;
            }
          }
      });
    } else {
      self.shippingMethodId = null;
      self.shippingMethod = "No shipping method available";
      if (!self.shippingOverride) self.shippingTotal = 0;
      self.shippingDiscontinued = false;
    }
  },
  
  getUserAddressLegacyKey: function D_getUserAddressLegacyKey() {
    if(this.order != null && this.order.customer != null) return this.order.customer.getShippingKey();
    return "NA";
  },
  
  buildShippingLegacyKey: function() {
    var key = "meth=" + this.shippingMethodId;
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.addedToCart && p.doesShip() && p.product != null) {
        key += "&" + p.product.id + "&q=" + p.qty;
      }
    }
    key += "&addr=" + this.getUserAddressLegacyKey();
    return key;
  },
  
  isShippingOK: function Cart_isShippingOK() {
    var allDigital = this.isAllDigital();
    if ((allDigital && this.shippingMethodId == null) || (!allDigital && this.shippingMethodId != null)) {
      return true;
    } else {
      return false;
    }
  },
  
  isAllDigital: function Cart_isAllDigital() {
    var allDigital = true;
    this.products.each(function(p) {
        if (p.product) allDigital = false;
    });
    return allDigital;
  },
  
  // called in order management system, carts are generated locally
  initDesignerCart: function Cart_initDesignerForOrder() {
    var cartBody = $("cart_body");
    
    cartBody.update("");
    this.products.each(function(product) {
        new Insertion.Top(cartBody, product.cartRowHtml());
    });
  },
  
  initDigitizationStatusForProducts: function Cart_initDigitizationStatusForProducts() {
    var self = this;
    this.checkDigitizationsHaveProducts();
    $H(this.digitizedAssets).each(function(pair) {
        var status = pair.value.product.status;
        var cpIds = pair.value.product.for_cp_ids;
        cpIds.each(function(cpid) {
            var p = self.productsById[cpid];
            if (p) p.digStatus = status;
        });
    });
  },
  
  add: function Cart_add(product) {
    if(product.cp_type == CP_DIGITIZATION) {
      var assetDef = d.assets[product.asset_id];
      if(assetDef == null) {
        log("ERROR: unable to get asset " + product.asset_id + " when adding digitization cart item");
        return;
      }
      this.getDigitization(assetDef).product = product;
      this.derivedProductsByLinkId[product.asset_id] = product;
      this.extras.push(product);
    } else if (product.product || product.cp_type == CP_NEW || product.cp_type == CP_FREE_FORM) {
      this.lineItems.push(product);
    } else if (product.cp_type == CP_NEW_EXTRA || product.cp_type == CP_DIGITIZATION || product.cp_type == CP_EXTRA_CHARGE) {
      this.extras.push(product);
    } else if (product.cp_type == CP_CREDIT) {
      this.credits.push(product);
    }
    
    this.usedClientIds.push(product.clientId);
    
    this.products.push(product);
    if (product.id) this.productsById[product.id] = product;
  },
  
  hasLineItems: function Cart_hasLineItems() {
    for(var i=0; i<this.lineItems.size(); i++) {
      var p = this.lineItems[i];
      if (p.cp_type != CP_NEW && !p.deleted) return true;
    }
    return false;
  },
  
  recalcItemPosition: function Cart_recalcItemPosition() {
    var pos = 1;
    this.products.each(function(p) {
        if (p.product || p.cp_type == CP_NEW || p.cp_type == CP_GIFT_CERTIFICATE || p.cp_type == CP_FREE_FORM) {
          p.pos = pos;
          pos += 1;
        }
    });
  },
  
  getItemPosition: function Cart_getItemPosition(id) {
    var p = this.getProduct(id);
    if(p == null) return -1;
    if (!p.pos) this.recalcItemPosition();
    return (!p.pos) ? -1 : p.pos;
  },
  
  getProduct: function Cart_getProduct(id) {
    return this.productsById[id];
  },
  
  setValidProducts: function Cart_setValidProducts(ids) {
    var validProducts = [];
    var validProductsById = {};
    for(var i=0; i < ids.length; i++) {
      var pId = ids[i];
      validProducts.push(this.productsById[pId]);
      validProductsById[pId] = this.productsById[pId];
    }
    this.products = validProducts;
    this.productsById = validProductsById;
  },
  
  isEditable: function Cart_isEditable() {
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.product && p.editable == false) {
        return false;
      }
    }
    return true;
  },
  
  getFirstEditableProduct: function Cart_getFirstEditableProduct() {
    for(var i=0; i < this.products.length;i++) {
      if((this.products[i].addedToCart)&&(this.products[i].cp_type!=CP_DIGITIZATION)&&(this.products[i].cp_type!=CP_CREDIT)) {
        return this.products[i];
      }
    }
    return null;
  },
  
  getUnsavedProduct: function Cart_getUnsavedProduct() {
    for(var i=0; i < this.products.length;i++) {
      if((!this.products[i].addedToCart)&&(this.products[i].cp_type!=CP_DIGITIZATION)&&(this.products[i].cp_type!=CP_CREDIT)) {
        return this.products[i];
      }
    }
    return null;
  },
  
  copyProduct: function Cart_copyProduct(id) {
    var self = this;
    if((typeof(dontWarnBeforeLeaving) != 'undefined')&&(dontWarnBeforeLeaving == false)&&(!ignoreDirtyProducts)) { //current item is dirty...
      msgBox(ml("Save Changes?"), ml("The currently loaded item has unsaved changes. Do you want to save the item before copying?"), null, ml("Yes"), ml("No"), ml("Cancel"), function(result) {
          if(result == 0) { //yes
            d.currentCProduct.save(false, false, function() {
                self.copyProduct(id);
            });
          } else if(result == 1) { //no
            dontWarnBeforeLeaving = true;
            if(d.currentCProduct.addedToCart) {
              d.currentCProduct.rollBack();
            }
            self.copyProduct(id);
          } else {
            //cancel...
          }
      });
      return;
    }
    
    
    var product = this.productsById[id];
    var cp = this.getUnsavedProduct();
    if(cp != null) {
      cp.copy(product);
      cp.save(true, false);
    } else {
      
      var asyncKey = asyncStart("designer_container");
      var self = this;
      var t2 = new Ajax.Request(d.pathPrefix + "/designer/new_product?id=" + product.product.id, {asynchronous:true, evalScripts:true, onComplete: function D_onComplete(response) {
          asyncFinish(asyncKey);
          cp = self.getUnsavedProduct();
          if(cp != null) {
            self.copyProduct(id);
          } else {
            alert("ERROR: No new item was added");
          }
        }
       });
    }
  },
  
  removeProductAlt: function Cart_removeProductAlt(product, avoidConfirm) {
    if (typeof(product) == "number") product = this.productsById[product];
    if((avoidConfirm == true) || (confirm(ml("Are you sure you want to remove this product?")))) {
      if (product.id) this.productsById[product.id] = null;
      var idx = this.products.indexOf(product);
      if(idx != null) {
        this.products[idx] = null;
        this.products = this.products.compact();
      }
      if(product.cp_type == CP_CREDIT) {
        idx = this.credits.indexOf(product);
        if(idx != null) {
          this.credits[idx] = null;
          this.credits = this.credits.compact();
        }
      }
      if((product.cp_type == CP_DIGITIZATION)&&(product.asset_id != null)) {
        delete this.derivedProductsByLinkId[product.asset_id];
      }
      
     /* var cartRow = $("cart_" + id);
      var cartOptRow=$("cart_options_"+id);
      if(cartRow != null) {
        cartRow.remove();
        cartOptRow.remove();
      }
      */
      
      // remove associated items from extra charges
      if (product.cp_type == CP_DECORATED_PRODUCT) {
        this.products.each(function(p) {
            if (p.cp_type == CP_EXTRA_CHARGE) {
              var index = p.associatedItems.indexOf(product.id);
              if (index != -1) {
                p.associatedItems.splice(index, 1);
              }
            }
        });
      }
      
      product.remove();
      product.deleted = true;
    }
  },
  
  removeProduct: function Cart_removeProduct(id) {
    if(confirm(ml("Are you sure you want to remove this product?"))) {
      var product = this.productsById[id];
      this.productsById[id] = null;
      var idx = this.products.indexOf(product);
      if(idx != null) {
        this.products[idx] = null;
        this.products = this.products.compact();
      }
      var cartRow = $("cart_" + id);
      var cartOptRow=$("cart_options_"+id);
      if(cartRow != null) {
        cartRow.parentNode.removeChild(cartRow);
        cartOptRow.parentNode.removeChild(cartOptRow);
      }
      var aKey = asyncStart("m_cart_pane");
      var self = this;
      var t1 = new Ajax.Request(d.ajaxUrl(d.pathPrefix + "/designer/remove_product?from=designer&id=" + id), {asynchronous:true, evalScripts:true, 
      onComplete: function Cart_onComplete() { 
        d.notifyCartChanged();
        asyncFinish(aKey); 
        if(d.currentCProduct.id == id) { //we deleted the currently selected product....
          //why was below code existing? it would stop us deselecting the view/area/item....
          //d.currentCView = null;
          //d.currentCViewArea = null;
          var firstProduct = self.getFirstEditableProduct();
          if(firstProduct == null) {
            d.startNewCartItem(d.currentCProduct.product.id, true);
            $("m_cart").className="unselected_tab_hidden";
          } else {    
            d.selectConfiguredProduct(firstProduct.id, true, false);
          }
        /*
        why below? shouldnt i leave be if i didnt delete current product?
        } else if((self.products.length == 0) || (self.products.length==1 && self.getUnsavedProduct() != null)) {
          d.startNewCartItem(d.currentCProduct.product.id, true);
          $("m_cart").className="unselected_tab_hidden";*/
        }
        product.remove();
        self.updateCartPrice();
        try {
          if(updateCart) updateCart();
        } catch(e) {
          //doesnt matter (this would update cart at top of page.. must be there...)
        }
      }});
      
      
      //delete this.allProducts[product.id];
    }
  },
  
  updateCartPrice: function Cart_updateCartPrice(doItems) {
    log("updateCartPrice");
    var total = 0;
    var count = 0;
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.addedToCart && p.cp_type != CP_CREDIT) {
        total += p.getPrice();
        count++;
        if(doItems) {
          p.updateCartPrice(false);
        }
      }
    }
    if(this.tpEl==null) {
      this.tpEl = $("total_price");
    }
    if(this.tpCEL==null) {
      this.tpCEL = $("cart_total_container");
    }
    log("total price=" + total);
    if(this.tpEl!=null) {
      this.tpEl.innerHTML = d.formatPrice(total);
    }
    if(this.tpCEL!=null) {
      if(count <= 1) {
        this.tpCEL.style.display="none";
      } else {
        this.tpCEL.style.display="";
      }
    }
    this.checkLegacyPrices();
    this.total = total;
    log("updateCartPrice Done");
  },
  
  recalcPrices: function Cart_recalcPrices(order) {
    log("calcPrices");
    var viewMode = (order.mode == MODE_VIEW);
    if (!this.taxRate) this.taxRate = d.tMod;
    var total = 0, totalWholesale = 0, totalCost = 0, totalCredit = 0;
    var tax = 0, couponDiscount = 0, couponTax = 0.0;
    var taxRate = this.getTaxRate() - 1.0;
    for(var i=0; i < this.products.length;i++) {
      var p = this.products[i];
      if(p.addedToCart) {
        p.getPrice();
        totalWholesale += p.pricing.wsCost * p.qty;
        totalCost = p.pricing.cost * p.qty;
        if (p.cp_type == CP_CREDIT) {
          totalCredit -= p.finalTotal;
        } else {
          var up = (viewMode && p.legacyTotal != null) ? p.legacyTotal : p.finalTotal;
          total += up;
          if (p.getIncTax()) {
            tax += up * taxRate;
          }
        }
      }
    }
    var shippingTotal = this.getShippingTotal();
    if (this.getApplyTaxToShipping()) tax += shippingTotal * taxRate;
    
    var couponValue =  (this.coupon) ? this.coupon.getDiscountAmount(this) : { value: 0.0, tax: 0.0};
    
    this.couponDiscount = couponValue.value;
    this.couponDiscountTax = couponValue.tax;
    if(!this.applyTax) {
      this.couponDiscountTax = 0.0;
    }
    
    this.total = total.round();
    this.totalWholesale = totalWholesale.round();
    this.commission = this.total - this.totalWholesale;
    this.totalCost = totalCost.round();
    this.totalCredit = totalCredit.round();
    if (this.commission < 0) this.commission = 0;
    this.taxPrice = tax.round();
    tax = (this.applyTax) ? this.taxPrice : 0;
    this.calcFinalTotal = (tax + this.total + shippingTotal - this.couponDiscount - this.couponDiscountTax).round(); // - this.gcAmount  we dont include gc because thats a form of payment
    this.finalTotal = (this.finalTotalLocked) ? this.overridingFinalTotal.round() : (tax + this.total - this.totalCredit + shippingTotal - this.couponDiscount - this.couponDiscountTax - this.finalDiscount).round(); // - this.gcAmount we dont include gc because thats a form of payment
  },
  
  setFinalTotal: function Cart_setFinalTotal(price, lock) {
    this.overridingFinalTotal = price;
    this.finalTotalLocked = lock;
    this.setFinalDiscount(AMOUNT_TYPE_PERCENT, null, this.finalDiscountLocked);
  },
  
  setFinalDiscount: function Cart_setFinalDiscount(discountType, discount, locked) {
    var finalDiscountAlt;
    this.finalDiscountType = discountType;
    if (discount == null) {
      finalDiscountAlt = this.legacyFinalDiscount;
    } else {
      finalDiscountAlt = discount;
    }
    if (discountType == AMOUNT_TYPE_PERCENT) {
      finalDiscountAlt = d.round(finalDiscountAlt / 100);
    }
    this.finalDiscountLocked = locked;
    
    if (this.finalTotalLocked) {
      this.finalDiscount = this.calcFinalTotal - this.overridingFinalTotal;
      this.finalTotalLocked = false;
      this.finalDiscountLocked = true;
    } else if (this.finalDiscountLocked) {
      if (this.finalDiscountType == AMOUNT_TYPE_PERCENT) {
        this.finalDiscount = this.calcFinalTotal * finalDiscountAlt;
      } else {
        this.finalDiscount = finalDiscountAlt;
      }
    } else {
      this.finalDiscount = this.legacyFinalDiscount;
    }
  },
  
  getFinalDiscount: function Cart_getFinalDiscount(discountType) {
    if (this.finalTotalLocked) {
      this.finalDiscount = this.calcFinalTotal - this.overridingFinalTotal;
    }
    var disc;
    if (discountType === null || typeof(discountType) == 'undefined') discountType = this.finalDiscountType;
    if (discountType == AMOUNT_TYPE_PERCENT) {
      disc = d.round(this.finalDiscount / this.calcFinalTotal * 100.0);
    } else {
      disc = d.round(this.finalDiscount);
    }
    if (isNaN(disc)) disc = 0;
    return disc;
  },
  
  selectCartItem: function Cart_selectCartItem(cProduct) {
    if((d.mode != DESIGNER_MODE_CONFIGURE)&&(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)&&(d.mode != DESIGNER_MODE_TEMPLATE)) {
      for(var i=0; i < this.products.length;i++) {
        var p = this.products[i];
        if(p.addedToCart && p.cp_type != CP_GIFT_CERTIFICATE && p.cp_type != CP_EXTRA_CHARGE && p.cp_type != CP_CREDIT) {
          var el = $("cart_" + p.id);
          if(p == cProduct) {
            if(el != null) el.className="cp_details selected";
            if((d.mode != DESIGNER_MODE_AMEND)&&(p.hasCartOptions())) {
              el = $("cart_options_" + p.id);
              if(el != null) el.className="cp_options selected";
            }
          } else {
            if(el != null) el.className="cp_details";
            if((d.mode != DESIGNER_MODE_AMEND)&&(p.hasCartOptions())) {
              el = $("cart_options_" + p.id);
              if(el != null) el.className="cp_options";
            }
          }
        }
      }
    }
  },

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

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


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

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


var ConfiguredProduct = Class.create({
  CLASSDEF: {
      name: 'ConfiguredProduct'
  },
  
  initialize: function CP_initialize(id, options, type, useLegacy, cart, ignoreLegacy) {
    if (id) {
      this.id = id;
    } else {
      this.id = nextConfiguredProductId;
      nextConfiguredProductId --;
    }
    if (id < 0) this.clientId = id;
    
    
    this.isAutoPrice = true;
    this.discountType = AMOUNT_TYPE_PERCENT;
    this.totalLocked = false;
    this.discountLocked = false;
    this.editable = true;
    if (cart) {
      this.cart = cart;
    } else {
      this.cart = d.cart;
    }
    
    this.legacyPriceChanges = [];
    this.needToCalcDiscount = false; //this needs to be done once the undiscounted price is calculated...
    if (options) {
      this.okToSave = (options.ots != null) ? options.ots : true;
      this.options = options;
      this.cp_type = options.cp_type;
      
      if(options.tot != null) { //overriding total..
        this.totalLocked = true;
        this.overridingTotal = options.tot;
      }
      /*
      if(options.dt != null) { //discount type used... must be overriden.,.
        this.discountLocked = true;
        this.discountType = parseInt(options.dt, 10);
        this.typedOverridingDiscount = parseFloat(options.odis);
      }
      */
      this.legacyBulkDiscount = options.bdis;
      this.legacyBulkDiscountToBase = options.bdis_base;
      
      if (options.iad && options.odis) {
        this.discountLocked = false;
        this.legacyDiscount = parseFloat(options.odis);
        
        if(options.dt == null) { //legacy discount... stored as fixed amount...
          //if(this.isProductBased()) {
            this.needToCalcDiscount = true; //we will try and calc what the discount is as a %
          //} else {
         //   //these is no bulk discounting with non product based items...
         //   this.discountType = AMOUNT_TYPE_FIXED;
         // }
        } else {
          this.discountType = parseInt(options.dt);
        }
      } else if (!options.iad) {
        this.discountLocked = true;
        this.discountType = parseInt(options.dt);
        this.typedOverridingDiscount = parseFloat(options.odis);
      }
      
      
      
      //this.uid = nextConfiguredProductId;
      //nextConfiguredProductId ++;
      
      if (!this.clientId) this.clientId = options.cid;
      if(this.cartIndex == null) this.cartIndex = d.cart.products.length;
      
      if(options.q) {
        this.qty = parseInt(options.q, 10);
      } else {
        this.qty = 1;
      }
      if(this.options.lv) {
        this.layout_version = parseInt(this.options.lv, 10);
      } else {
        this.layout_version = 0;
      }
      this.discount = parseFloat(options.dis);
      this.overridingDiscount = this.discount;
      
      if (options.assets) {
        var self = this;
        options.assets.each(function(as) {
            d.addAsset(as);
        });
      }
      
      this.imageURL = options.iu + '?v=' + (new Date()).getTime();
      this.largeImageURL = options.iu2;
      
      this.processedBy = options.proc_by;
      this.processedDate = options.proc_date;
      this.shippedBy = options.ship_by;
      this.shippedDate = options.ship_date;
      
      this.views = {};
      this.product = d.productsById[options.p];
      this.nextItemId = 1; //this id is unique per configured product.... allows us to cache stuff on the server....
      this.brandId = options.b;
      if (options.markup) {
        this.markup = options.markup;
        this.markupMode = options.markup.mode;
        this.markupAmount = options.markup.amount;
      } else {
        this.markupMode = (d.brandMarkups[this.brandId]) ? d.brandMarkups[this.brandId].mode : 1;
        this.markupAmount = (d.brandMarkups[this.brandId]) ? d.brandMarkups[this.brandId].amount : 0;
      }
      
      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.legacyIncTax = options.inc_tax;
      if(this.legacyIncTax != null) {
        this.legacyIncTaxKey = this.calcLegacyIncTaxKey();
      }
      
      this.renderVersion = (options.lv == null) ? 0 : options.lv;
      this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
      this.reRenderChild = false; //track if children have made changes requiring rerendering
      this.price = null;
      this.addedToCart = options.added;
      
      this.isAutoBasePrice = (options.iabp == null) ? true : options.iabp;
      this.overridingBasePrice = (this.isAutoBasePrice) ? null : options.obp;
      this.legacyBasePrice = (!this.isAutoBasePrice) ? null : options.obp;
      this.isAutoSizePrice = (options.iasp == null) ? true : options.iasp;
      if(options.lfd != null) {
        this.legacyFieldData = {};
        for(k in options.lfd) {
          this.legacyFieldData[parseInt(k)] = {
            pricingType: parseInt(options.lfd[k].pt), 
            priceModifierType: parseInt(options.lfd[k].pmt), 
            percentOf:options.lfd[k].po, 
            defaultOn: (options.lfd[k].def_o == "1"),
            fieldType: parseInt(options.lfd[k].ft)
          };
        }
      } else {
        this.legacyFieldData = null;
      }

      if(this.cp_type == CP_DIGITIZATION) {
        this.asset_id = options.asset_id;
        this.price = options.price;
        this.for_cp_ids = (options.for_cp_ids) ? options.for_cp_ids : [];
        this.associatedItems = (options.associated_items) ? options.associated_items : [];
        var self = this;
        this.for_cp_ids.each(function(cpid) {
            if (self.associatedItems.indexOf(cpid) == -1) {
              self.associatedItems.push(cpid);
            }
        });
        this.status = options.status;
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
        //this.legacyPrice = options.op;
        this.rp = options.rp;
  //      this.primaryItemId = options.cpa;
      } else if (this.cp_type == CP_FREE_FORM) {
        this.name = options.n;
        this.description = options.desc;
        this.rrp = options.rrp;
        this.price = options.price;
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
      } else if (this.cp_type == CP_EXTRA_CHARGE) {
        this.name = options.n;
        this.rrp = options.rrp;
        this.price = options.price;
        this.associatedItems = (options.associated_items) ? options.associated_items : [];
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
        //this.legacyPrice = options.op;
        this.extraCharge = orderManager.extraChargesById[options.eid];
      } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
        this.price = options.price;
        this.name = options.n;
        this.overridingPrice = options.op;
        this.isAutoPrice = options.iap;
        this.legacyPrice = options.op;
      } else if (this.cp_type == CP_CREDIT) {
        this.rrp = options.rrp;
        this.price = options.price;
        this.isAutoPrice = true;
        this.discountLocked = true;
        this.discountType = AMOUNT_TYPE_PERCENT;
        this.typedOverridingDiscount = 0;
        this.dateModified = options.dm;
      } else {
        this.overridingUnspecSizePrice = (this.isAutoSizePrice) ? null : options.ussp;
        this.legacyUnspecSizePrice = (this.isAutoSizePrice) ? options.ussp : null;
        this.unspecSizeQty = options.ussq;
        
        this.legacyUnitPrice = options.up;
        this.legacyTotal = options.price;
        
        this.legacyDefaultDecorationPrice = options.lddp;
        
        this.customProductId = options.cp;
        if(this.customProductId != null) {
          this.customProduct = d.customProducts[this.customProductId];
        }
        
        
        
        
        
        
        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) {
            var cnt = 0;
            for(var j=0; j < fieldData.opts.length; j++) {
              var optData = fieldData.opts[j];
              var cOpt = cField.addSelectedOption(optData.id, optData.qty, null, optData.up, optData.iap, optData.ud, true);
              if(cOpt != null) {
                if(optData.sub.length == 0) {
                  cnt += 1;
                } else {
                  for(var k=0; k < optData.sub.length; k++) {
                    var subData = optData.sub[k];
                    cOpt.addSelectedOption(subData.id, subData.qty, subData.up, subData.iap, subData.ud);
                    cnt += 1;
                  }
                }
              }
            }
            if(cnt > 1) {
              cField.multiSelect = true; //force to true incase multi is turned off and this is a legacy order
            }
          } else {
            log("Dropping Configured Field " + fieldData.id);
          }
          
        }
        
        this.initFieldDefaults();
        
        
        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;
          }
        } else if(this.customProduct) {
          if(this.customProduct.teamNames != null && this.customProduct.teamNames.length > 0) {
            this.getTeamNames().load(this.customProduct.teamNames);
            this.usingTeamnames = true;
          } else {
            this.usingTeamnames = false;
          }
        }
        
        var views = options.v;
        
        
        if(this.customProduct != null) {
          if(views.length == 0 && this.customProduct) { //no views defined in configured product... use the views defined in the custom product....
            log("Loading views from custom product definition");
            views = this.customProduct.viewOptions;
          }
        }
        for(var i=0; i < views.length;i++) {
          var view = views[i];
          var productView = this.product.views.byId[view.id];
          if(productView==null) {
            log("Error loading custom product view: Product does not have view " + view.id);
          } else {
            log("init view:" + productView.id);
            this.views[productView.id] = new ConfiguredView(this, productView, view);
          }
        }
        if(options.def_proc == null && options.def_proc != -1) {
          if((this.customProduct != null)&&(this.customProduct.defaultProcessId != null)) {
            this.defaultProcessId = this.customProduct.defaultProcessId;
          } else if(d.defaultProcess == null) {
            this.defaultProcessId = this.product.getFirstProcessId(); 
          } else {
            this.defaultProcessId = d.defaultProcess.id;
          }
        } else {
          this.defaultProcessId = options.def_proc;
        }
        
        
        
        this.selectedColorId = options.color;
        if((this.selectedColorId == null) && (this.usingCustomProduct())) {
          this.selectedColorId = this.customProduct.defaultColor;
        }
        
        if(this.legacyBulkDiscount != null) {
          this.legacyBulkDiscountKey = this.generateBulkDiscountKey();
        } else {
          this.legacyBulkDiscountKey = null;
        }
        
      }
    } else {
      this.cp_type = (type) ? type : CP_NEW;
      
      if(this.cartIndex == null) {
        if (cart) this.cartIndex = cart.products.length;
        else this.cartIndex = d.cart.products.length;
      }
      this.options = null;
      this.okToSave = false;
      this.qty = 1;
      this.layout_version = 0;
      this.discount = 0;
      
      this.processedBy = null;
      this.processedDate = null;
      this.shippedBy = null;
      this.shippedDate = null;
      
      this.views = {};
      this.nextItemId = 1; //this id is unique per configured product.... allows us to cache stuff on the server....
      this.brandId = d.brandId;
      this.markupMode = (d.brandMarkups[this.brandId]) ? d.brandMarkups[this.brandId].mode : 1;
      this.markupAmount = (d.brandMarkups[this.brandId]) ? d.brandMarkups[this.brandId].amount : 0;
      
      this.renderVersion = 0;
      this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
      this.reRenderChild = false; //track if children have made changes requiring rerendering
      this.price = null;
      this.addedToCart = false;
      this.usingTeamnames = false;
      this.usesDecorationLibrary = null; // start as null, as we don't know yet, could be true or false    
      this.customProductId = null;
      this.selectedColorId = null;
      this.associatedItems = [];
      
      this.isAutoBasePrice = true;
      this.legacyBasePrice = null;
      
      this.isAutoSizePrice = true;
      this.unspecSizeQty = 0;
    }
    
    if (this.defaultProcessId == null) this.defaultProcessId = d.defaultProcessId;
    if(this.usingTeamnames) {
      if(this.customProduct) {
        this.resetPricing();
        this.getTeamNames().checkInitFromCustom();
      }
      this.getTeamNames().updateConfiguredProductFromTeamNames();
    }
    this.initProductLevelDesignItems();
    dnRunCallback("designer.lineitem.loaded", { lineItem: this});
  },
  
  initAsFreeForm: function CP_initAsFreeForm() {
    this.rrp = 0;
    this.cp_type = CP_FREE_FORM;
  },
  
  checkPricingLocked: function CP_checkPricingLocked(useLegacy, ignoreLegacy) {
    if (!ignoreLegacy && (useLegacy || d.useLegacyPrices) && this.legacyTotal != null) {
      d.forceLegacyPrices = true; //we need to init pricing/legacy keys
      this.getPrice();
      d.forceLegacyPrices = false;
      //var oldLegacySetting = d.useLegacyPrices;
      //d.useLegacyPrices = true;
      if(this.getPrice().round() != this.legacyTotal) {
        
        var canFix = false;
        if(this.defaultProcessId != -1) {
          log("Checking if changing default process can fix problem");
          var oldProcessId = this.defaultProcessId;
          this.defaultProcessId = -1;
          d.forceLegacyPrices = true; //we need to init pricing/legacy keys
          this.getPrice();
          d.forceLegacyPrices = false;
          if(this.getPrice().round() == this.legacyTotal) {
            canFix=true;
            log("Changing default process fixes problem");
          } else {
            this.defaultProcessId = oldProcessId;
          }
        }
        
        if(!canFix) {
          this.editable = false;
          if (!this.reported) {
            this.reportErrorProduct();
          }
        }
      }
      //d.useLegacyPrices = oldLegacySetting;
    }
  },
  
  updateCancelState: function CP_updateCancelState(options) {
    this.options = options;
    if(this.cp_type == CP_DIGITIZATION) { //we need to update the price data...
      this.price = options.price;
      this.rp = options.rp;
      this.rrp = null;
    }
  },
  
  reportErrorProduct: function CP_reportErrorProduct() {
    var p = this.pricingData;
    
  
    
    var data = {
      type: "OMS_LEGACY_PRICE",
      cartId: this.cart.id,
      invoiceId: this.cart.invoiceId,
      brandId: orderManager.brandId,
      basePrice: p.basePrice.toString(),
      decorationPrice: p.decorationPrice.toString(),
      fieldPrice: p.fieldPrice.toString(),
      legacyUnitPrice: this.legacyUnitPrice,
      legacyTotal: this.legacyTotal,
      price: this.getPrice().round(),
      discount: this.discountPrice.toString(),
      configuredProductId: this.id
    };
    if (p.fields != null && !p.fields.isEmpty()) {
      p.fields.list.each(function(f) {
          data['fields[' + f.id + '][name]'] = f.field.fieldDef.name;
          data['fields[' + f.id + '][legacyKey]'] = f.legacyID;
          data['fields[' + f.id + '][total]'] = (f.field.total) ? f.field.total.toString() : null;
          if (f.selectedOptions != null && !f.selectedOptions.isEmpty()) {
            f.selectedOptions.list.each(function(o) {
                data['fields[' + f.id + '][selectedOptions][' + o.option.optionId + '][name]'] = o.option.def.name;
                data['fields[' + f.id + '][selectedOptions][' + o.option.optionId + '][qty]'] = o.qty;
                data['fields[' + f.id + '][selectedOptions][' + o.option.optionId + '][total]'] = (o.total) ? o.total.toString() : null;
            });
          }
      });
    }
    if (p.views != null && !p.views.isEmpty()) {
      p.views.list.each(function(v) {
          data['views[' + v.id + '][name]'] = v.view.productView.name;
          data['views[' + v.id + '][total]'] = (v.total) ? v.total.toString() : null;
          if (v.areas != null && !v.areas.isEmpty()) {
            v.areas.list.each(function(a) {
                data['views[' + v.id + '][areas][' + a.id + '][name]'] = a.area.productArea.name;
                data['views[' + v.id + '][areas][' + a.id + '][total]'] = (a.total) ? a.total.toString() : null;
                if (a.processes != null && !a.processes.isEmpty()) {
                  a.processes.list.each(function(ps) {
                      data['views[' + v.id + '][areas][' + a.id + '][processes][' + ps.id + '][total]'] = (ps.total) ? ps.total.toString() : null;
                  });
                }
            });
          }
      });
    }
    
    orderManager.request('/manage/orders/report_client_error', '', function(manager, response) {}, false, data);
    
    this.reported = true;
  },
  
  isNew: function() {
    return (this.id < 0);
  },
  
  isPreDecProduct: function CP_isPreDecProduct() {
    return (this.customProductId);
  },
  
  isBlankProduct: function CP_isBlankProduct() {
    return (this.cp_type == CP_DECORATED_PRODUCT && !this.customProductId);
  },
  
  isNonDecProduct: function CP_isNonDecProduct() {
    return ((this.cp_type == CP_NON_DEC) || (this.product && !this.product.canDoDecorate));
  },
  
  isFreeFormProduct: function CP_isFreeFormProduct() {
    return (this.cp_type == CP_FREE_FORM);
  },
  
  isProductBased: function() {
     if(this.cp_type == CP_NEW_EXTRA || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_DIGITIZATION || this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_FREE_FORM || this.cp_type == CP_CREDIT) {
      return false;
    }
    return true;
  },
  
  //does this product get shipped?
  doesShip: function() {
    if(this.cp_type == CP_NEW_EXTRA || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_DIGITIZATION || this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_FREE_FORM || this.cp_type == CP_CREDIT) {
      return false;
    }
    return true;
  },
  
  saveCurrentStatus: function CP_saveCurrentStatus() {
    this.options = this.serializeToOptions();
  },
  
  serializeToOptions: function CP_serializeToOptions() {
    d.setUnitPrices=true; //we need to recalc pricing with this flag set to true to make sure unit/delta prices are set
    this.resetPricing(true);
    d.setUnitPrices=false;
      
    var opts = {};
    if (this.totalLocked) {
      opts.tot = this.overridingTotal;
    }
    opts.iad = !this.discountLocked;
    opts.dt = this.discountType;
    if (this.discountLocked) {
      opts.odis = this.typedOverridingDiscount;
    } else {
      opts.ldk = this.legacyDiscountKey;
      opts.odis = this.legacyDiscount;
    }
    
    opts.ots = this.okToSave;
    opts.cid = this.clientId;
    opts.cp_type = this.cp_type;
    opts.q = this.qty;
    opts.lv = this.layout_version;
    opts.dis = this.discount;
    opts.iu = this.imageURL;
    opts.iu2 = this.largeImageURL;
    opts.proc_by = this.processedBy;
    opts.proc_date = this.processedDate;
    opts.ship_by = this.shippedBy;
    opts.ship_date = this.shippedDate;
    opts.p = (this.product) ? this.product.id : null;
    opts.b = this.brandId;
    opts.added = this.addedToCart;
    opts.obp = this.overridingBasePrice;
    opts.iabp = this.isAutoBasePrice;
    opts.iasp = this.isAutoSizePrice;
    opts.markup = this.markup;
    
    if(this.legacyFieldData != null) {
      opts.lfd = this.serialiseFieldDataToObject();
    }
    
    if (this.cp_type == CP_NEW || this.cp_type == CP_NEW_EXTRA) {
      
    } else if (this.cp_type == CP_DIGITIZATION) {
      opts.asset_id = this.asset_id;
      opts.price = this.price;
      opts.for_cp_ids = this.for_cp_ids;
      opts.status = this.status;
      opts.op = this.overridingPrice;
      opts.iap = this.isAutoPrice;
      opts.rrp = this.rrp;    
      opts.rp = this.price;
    } else if (this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_FREE_FORM) {
      opts.n = this.name;
      opts.desc = this.description;
      opts.rrp = this.rrp;
      opts.price = this.price;
      opts.op = this.overridingPrice;
      opts.iap = this.isAutoPrice;
    } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
      opts.price = this.price;
      opts.n = this.name;
      opts.op = this.overridingPrice;
      opts.iap = this.isAutoPrice;
    } else {
      opts.utn = this.usingTeamnames;
      if(this.usingTeamnames) {
        opts.tn = [];
        this.getTeamNames().names.list.each(function(name) {
            opts.tn.push({
                o : name.optionId,
                s : name.subOptionId,
                n : name.names.names
            });
        });
      }
      
      opts.color = this.selectedColorId;
      opts.f = [];
      
      $H(this.cFields).each(function(pair) {
          var cField = pair.value;
          var opt = {
              id : cField.id,
              val : cField.fieldValue,
              opts : []
          };
          $H(cField.options).each(function(pair) {
              var cFieldOption = pair.value;
              var sopt = {
                id : cFieldOption.optionId,
                qty : cFieldOption.qty,
                up : cFieldOption.unitPrice,
                ud : cFieldOption.unitDelta,
                iap : cFieldOption.isAutoPrice,
                sub : []
              };
              $H(cFieldOption.subOptions).each(function(pair) {
                  var cFieldSubOption = pair.value;
                  sopt.sub.push({
                      id : cFieldSubOption.subOptionId,
                      qty : cFieldSubOption.qty,
                      up : cFieldSubOption.unitPrice,
                      ud : cFieldSubOption.unitDelta,
                      iap : cFieldSubOption.isAutoPrice
                  });
              });
              opt.opts.push(sopt);
          });
          opts.f.push(opt);
      });
      
      opts.cp = this.customProductId;
      
      opts.v = [];
      if (this.customProductId == null) {
        $H(this.views).each(function(pair) {
            var cView = pair.value;
            var vOpt = {
              id : cView.id,
              u : cView.startUrl,
              rv : cView.renderVersion,
              lrv : cView.loadedRenderVersion,
              a : []
            };
            $H(cView.areas).each(function(pair) {
                var cArea = pair.value;
                var aOpt = {
                  id : cArea.id,
                  rv : cArea.renderVersion,
                  c : {
                    bg : cArea.bgColor,
                    rv : cArea.renderVersion
                  },
                  p : [],
                  i : []
                };
                $H(cArea.processes).each(function(pair) {
                    var cProcess = pair.value;
                    var pOpt = {
                      p : cProcess.id,
                      odp : cProcess.overridingDecorationPrice,
                      ocp : cProcess.overridingClipartPrice,
                      uod : cProcess.usingOverridingDec,
                      uoc : cProcess.usingOverridingClipart,
                      lk : cProcess.legacyKey
                    };
                    aOpt.p.push(pOpt);
                    cProcess.items.list.each(function(item) {
                        aOpt.i.push({
                            id : item.id,
                            p : cProcess.id,
                            rv : item.renderVersion,
                            c : item.serializeToOptions()
                        });
                    });
                });
                $H(cArea.items).each()
                vOpt.a.push(aOpt);
            });
            opts.v.push(vOpt);
        });
      }
      
      opts.def_proc = this.defaultProcessId;
    }
    return opts;
  },
  
  toString: function CP_toString() {
    return this.getName();
  },
  
  getName: function CP_getName() {
    if (this.cp_type == CP_DIGITIZATION) {
      return "Digitizing Fee";
    } else if (this.cp_type == CP_EXTRA_CHARGE) {
      if (this.extraCharge) {
        return this.extraCharge.name;
      } else {
        return this.name;
      }
    } else if (this.cp_type == CP_GIFT_CERTIFICATE) {
      return this.name;
    } else if (this.product) {
      return this.product.name;
    } else {
      return this.name;
    }
  },
  
  getDescription: function CP_getDescription() {
    if (this.cp_type == CP_DIGITIZATION) {
      return this.status;
    } else if (this.cp_type == CP_EXTRA_CHARGE) {
      return this.extraCharge.description;
    } else if (this.cp_type == CP_FREE_FORM) {
      return this.description;
    } else {
      return '';
    }
  },
  
  initFields: function CP_initFields() {
    this.cFields = {};
    //load from configured product options first
    var fieldOpts = (this.options) ? this.options.f : null;
    if(fieldOpts==null) fieldOpts = []; //this shouldnt happen but DNC-2317 says it must....
    for(var i=0; i < fieldOpts.length; i++) {
      var fieldOpt = fieldOpts[i];
      var field = this.product.type.fields.byId[fieldOpt.id];
      if(field != null) {
        var pField = this.product.fields.byId[field.id];
        if((pField!=null)) { // if we check pField.used, then legacy field data wont load... &&(pField.used)) { //product may not support field....
          var cField = new ConfiguredField(field.id, this, fieldOpt);
          this.cFields[cField.id] = cField;
        }
      }
    }
    //now load all fields not in the options...
    for(var i=0; i < this.product.type.fields.list.length; i++) {
      var field = this.product.type.fields.list[i];
      if((this.cFields[field.id] == null)&&(!field.discontinued)) {
        var pField = this.product.fields.byId[field.id];
        if((pField!=null)&&(pField.used)) { //product may not support field....
          var cField = new ConfiguredField(field.id, this, {uDef: true});
          this.cFields[cField.id] = cField;
        }
      }
    }
  },
  
  initFieldDefaults: function CP_initFieldDefaults() {
    for(var k in this.cFields) {
      var cField = this.cFields[k];
      // init default size option
      if (cField.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
        for(var i=0; i < cField.productField.options.list.length; i++) {
          var pOpt = cField.productField.options.list[i];
          if(pOpt.selected) {
            if (!pOpt.def.isMulti) this.defaultSizeOptId = pOpt.id;
            if(pOpt.subs != null) {
              var anyDefault = null;
              var foundDefault = false;
              for(var k in pOpt.subs) {
                var sub = pOpt.subs[k];
                if(sub.selected) {
                  this.defaultSubSizeOptId = sub.id;
                  foundDefault = true;
                } else if (anyDefault == null) {
                  var selectedColor = this.getSelectedColor();
                  if(this.product.limitSizeColors) {
                    var type = this.product.type;
                    var ptField = type.fields.byId[cField.fieldDef.id];
                    var sizeColorCombinations = this.product.sizeColorCombinations;
                    var ptOpt = ptField.options.byId[pOpt.id];
                    var ptSubOpt = ptOpt.subs.byId[sub.id];
                    if (ptField.checkSizeColorCombination(sizeColorCombinations, selectedColor, ptOpt, ptSubOpt.id)) {
                      anyDefault = sub;
                    }
                  } else {
                    anyDefault = sub;
                  }
                }
              }
              if(!foundDefault && anyDefault!=null) {
                this.defaultSubSizeOptId = anyDefault.id;
              }
            }
          }
        }
      }
      cField.initToDefault();
    }
  },
  
  getFieldData: function CP_getFieldData(fieldId) {
    if(this.legacyFieldData == null) this.legacyFieldData = {};
    if(this.legacyFieldData[fieldId] == null) {
      this.legacyFieldData[fieldId] = this.product.type.fields.byId[fieldId].getFieldData();
    }
    return this.legacyFieldData[fieldId];
  },
  
  initFromDesignTemplate: function CP_initFromDesignTemplate(design, elements) {
    var templateAsset = d.addAsset(design);
    
    var selData = this.findFirstProcess(templateAsset.processes);
    if(selData == null) {
      alert("Unable to load an area that supports a process used to decorate the selected design");
      return;
    }
    
    var view = this.getView(selData.view.id, true);
    var area = view.getArea(selData.area.id, true);
    var processArea = area.processes[selData.process.id];
    var self = this;
    
    templateAsset.scaleDesignAssets(elements, {}, processArea, function(asset, opts) {
        var newItem = area.buildItemFromAsset(self.getNextItemId(), asset, processArea,  opts)
        processArea.addItem(newItem);
        return true;
    });
    
  }, 
  
  //teamnames appear as items when in template mode 
  initProductLevelDesignItems: function() {
    if((this.usingTeamnames)&&(d.mode == DESIGNER_MODE_TEMPLATE)) {
      this.teamNamesPanel = $(document.createElement("DIV"));
      this.teamNamesPanel.className = "teamnames_panel";
      //this.teamNamesPanel.hide();
      this.teamNamesPanel.update(this.teamNames.renderHtml());
      d.managePaneContainer.appendChild(this.teamNamesPanel);
      this.teamNames.bindColors();
    }
  },
  
  //called after the product has been selected...
  selected: function CP_selected() {
    this.firstViewId = this.getFirstSelectedViewId();
  },
  
  //find the first process in this product that supports a process in processIds
  findFirstProcess: function(processIds) {
    for(var i =0; i < this.product.views.list.size(); i++) {
      var pView = this.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];
          for(var l=0; l < processIds.length; l++) {
            if (allowed_p.id == processIds[l] || processIds[l] == -1) {
              return { process: allowed_p,  area: pArea,  view:pView};
              
            }
          }
        }
      }
    }
    return null;
  },
  
  getTeamNames: function() {
    if(this.teamNames == null) {
      this.teamNames = new TeamNames(this);
      this.teamNames.initNew([], true);
    }
    return this.teamNames;
  },
  
  //build the html displaying the views 
  updateViewListHtml: function CP_updateViewListHtml() {
    if ($("product_views") == null) return;
    var colorId =  this.getSelectedColorId();
    /*var colorId = 0;
    if(color!=null) {
      colorId = color.productChosenOptionId;
    }*/
    
    var customProduct = (this.usingCustomProduct() && !this.isCustomised()) ? this.customProduct : null;
    if(customProduct != null) {
      log("Using Custom Product to updateViewListHtml");
    } else {
      log("NOT Using Custom Product to updateViewListHtml: this.usingCustomProduct()=" + this.usingCustomProduct() + " this.isCustomised()=" + this.isCustomised());
    }
    var state = { needServer: false };
    var html = '';
    for(var i=0;i < this.product.views.list.size(); i++) {
      var pView = this.product.views.list[i];
      html += pView.buildSelectorHtml(colorId, this.getView(pView.id, false), customProduct, state);
    }
    $("product_views").innerHTML = html;
    if(state.needServer) {
      this.callServerToRenderThumbnails(); 
    }
  },
  
  getView: function CP_getView(viewId, createIfNotFound) {
    var view = this.views[viewId];
    if((view == null) && (createIfNotFound==true)) {
      var productView = this.product.views.byId[viewId];
      if(productView==null) {
        log("No defined view " + viewId);
      }
      view = new ConfiguredView(this,productView, {});
      this.views[viewId] = view;
    }
    return view;
  },
  
  //given a ProductViewAreaProcess, build/get the ConfiguredViewProcess for it...
  getCProcess: function C_getCProcess(productViewAreaProcess) {
    var cView = this.getView(productViewAreaProcess.viewArea.view.id, true);
    var cArea = cView.getArea(productViewAreaProcess.viewArea.id, true);
    return cArea.processes[productViewAreaProcess.id];
  },
  
  getFirstSelectedViewId: function CP_getFirstSelectedViewId() {
    var firstViewId = null;
    for(var i=0;i < this.product.views.list.size(); i++) {
      var pView = this.product.views.list[i];
      var cView = this.getView(pView.id, false);
      if((cView != null)&&(cView.isUsed())) {
        return pView.id;
      }
      if((firstViewId == null)&&(pView.allowDesign)) {
        firstViewId = pView.id;
      }
    }
    return firstViewId;
  },
  
  
  getNextItemId: function CP_getNextItemId() {
    return d.getNextItemId(); //proxy to designer to make ids cart wide...
  },
  
  registerServerItemId: function CP_registerServerItemId(id) {
    return d.registerServerItemId(id); //proxy to designer to make ids cart wide...
  },
  
  //get the configured size field
  getSelectedSize: function() {
    if(this.product.type.sizeField != null) {
      return this.cFields[this.product.type.sizeField.id];
    }
    return null;
  },
  
  getSelectedColor: function CP_getSelectedColor() {
    var f = this.product.type.colorField;
    if(f != null) {
      if (this.selectedColorId!=null) {
        var c = this.product.colors.byId[this.selectedColorId];
        if(c != null) return c;
        //no color, try discontinued colors
        c = this.product.discontinuedColors.byId[this.selectedColorId];
        if(c != null) return c;
      }
      //no color selected... lets get the default....
      this.selectedColorId = this.product.defaultColor.id;
      return this.product.defaultColor;
      
    } else {
      log("product has no color field");
    }
    return null;
  },
  
  getSelectedColorId: function CP_getSelectedColorId() {
    var sc = this.getSelectedColor();
    if(sc == null) {
      log("product has no color");
      return null;
    }
    return sc.id;
  },
  
  getAvailableColorList: function CP_getAvailableColorList(unavailableColors) {
    var colors = null;
    
    if(this.usingCustomProduct()) {
      colors = this.customProduct.getColors();
    } else {
      colors = this.product.colors;
    }
    var sizeField = this.getSelectedSize();
    var result = [];
    if((this.product.limitSizeColors)&&(sizeField != null)) {
      //check that the colors are available for the selected size(s)
      for(var i=0;i < colors.list.length; i++) {
        var pColor = colors.list[i];
        var allAvailable = true;
        var conflictSizes = [];
        for(var sizeId in sizeField.options) {
          var sizeChoice = sizeField.options[sizeId];  
          if(!sizeChoice.def.isMulti) {
            if(sizeChoice.pDef.subs == null && sizeChoice.qty > 0) {
              var sizeColors = this.product.sizeColorCombinations[sizeId];
              if(sizeColors != null) { 
                if(sizeColors[0][pColor.productChosenOptionId] != true) {
                  allAvailable = false;
                  conflictSizes.push(sizeChoice.def.value);
                  log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId); 
                  break;
                }
              } else {
                log("Error: size " + sizeId + " has no scc entries");
              }
            } else if(sizeChoice.subQty > 0) {
              for(var subSizeId in sizeChoice.subOptions) {
                var subSizeChoice = sizeChoice.subOptions[subSizeId];  
                if(subSizeChoice.qty > 0) {
                  var sizeColors = this.product.sizeColorCombinations[sizeId];
                  if (sizeColors != null) {
                    if (sizeColors[subSizeId] != null) {
                      if(sizeColors[subSizeId][pColor.productChosenOptionId] != true) {
                        allAvailable = false;
                        conflictSizes.push(sizeChoice.def.value + "-" + subSizeChoice.def.value);
                        log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId + "." + subSizeId); 
                        break;
                      }
                    } else if (sizeColors[0] != null){
                      if(sizeColors[0][pColor.productChosenOptionId] != true) {
                        allAvailable = false;
                        conflictSizes.push(sizeChoice.def.value + "-" + subSizeChoice.def.value);
                        log("color " + pColor.productChosenOptionId + " is not available in size " + sizeId + "." + subSizeId); 
                        break;
                      }
                    } else {
                      log("Error: size " + sizeId + "." + subSizeId + " has no scc entries");
                    }
                  } else {
                    log("Error: size " + sizeId + "." + subSizeId + " has no scc entries");
                  }
                }
              }
            }
          }
        }
        if(allAvailable) {
          result.push(pColor);
        } else if (unavailableColors) {
          unavailableColors.push({"color":pColor, "conflict_sizes":conflictSizes});
        }
      }
    } else {
      result = colors.list;
    }
    //check if amending.. only allow same color type so we cannot affect price...
    if(d.mode == DESIGNER_MODE_AMEND) {
      var curColorType = this.getSelectedColor().color_type;
      var result2 = [];
      for(var i=0; i < result.length; i++) {
        if(result[i].color_type == curColorType) {
          result2.push(result[i]);
        }
      }
      return result2;
    }
    
    return result;
  },
  /*
  we can now use the supplier_chosen_color_id to get images
  getSelectedProductColorId: function CP_getSelectedProductColorId() {
    var sc = this.getSelectedColor();
    if(sc == null) {
      log("product has no color");
      return null;
    }
    return sc.productChosenOptionId;
  },*/
  
  //the user has changed the color from the interface
  selectColor: function CP_selectColor(color) {
    var f = this.product.type.colorField;
    log("setting color to " + color.id);
    this.selectedColorId = color.id;
    this.setReRender();
    this.updateThumbnails();
    d.currentCView.setDesignerBackground();
    
    var sizeField = this.getSelectedSize();
    if((this.product.limitSizeColors)&&(sizeField != null)) { //need to rerender the size field because of color change...
      this.product.type.fields.byId[sizeField.id].setField(this);
    }
    
    for(var k in this.views) {
      view = this.views[k];
      if(view.isUsed()) {
        view.onColorChange(color);
      }
    }
    
    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 usedView = null; //find any used view for TEMPLATE_MODE matching
    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())) {
        usedView = oldViews[i];
      }
      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;
        }
      }     
    }
    if((moveResult == 1)&&(d.inSimpleMode(TEMPLATE_MODE_DESIGN))) {
      //we didnt get a match.. lets try and find the best match regardless of order....
      var bestView = product.getBestMatchedView(usedView);
      if(bestView == null) { //nothing matches...
        log("No matches in template mode");
        return 1;
      }
      var newCView = new ConfiguredView(this,bestView, {isTempView:true});
      return newCView.moveToView(usedView, true);//called with testOnly=true to test move
    }
    return moveResult; 
  },
  
  //when the product changes for this configured product... type and match up product data to be user friendly....
  setProduct: function CP_setProduct(product) {
    var oldProduct = this.product;
    this.product = product;
    if (oldProduct != product) {
      this.customProductId = null;
      if (this.options && this.options.markup) {
        this.options.markup = null;
        this.markupMode = d.brandMarkups[this.brandId].mode;
        this.markupAmount = d.brandMarkups[this.brandId].amount;
      }
      this.overridingPrice = null;
      this.isAutoPrice = true;
      this.overridingTotal = null;
      this.totalLocked = false;
      
      this.discountLocked = false;
      this.overridingDiscount = null;
      
      this.overridingBasePrice = null;
      this.isAutoBasePrice = true;
      //this.legacyBasePrice = null; //dont set to null because if custom reselects product matching key they will get legacy price again..
      this.isAutoSizePrice = true;
    }
    
    if(oldProduct != null) {
      
      //move the field choices across...
      this.selectedColorId = null;
      var oldFields = this.cFields;
      this.initFields();
      
     for(var i=0; i < this.product.type.fields.list.length; i++) {
        var fieldDef = this.product.type.fields.list[i];
        if(fieldDef.code != "C") {
          var newField = this.cFields[fieldDef.id];
          if(newField != null) {
            var oldFieldDef = oldProduct.type.fieldsByCode[fieldDef.code];
            if(oldFieldDef==null) { 
                oldFieldDef = oldProduct.type.fieldsByName[fieldDef.name];
                if(oldFieldDef != null) {
                  log("setProduct: field with name " + fieldDef.name + " is in both products");
                }
            } else{
              log("setProduct: field with code " + fieldDef.code + " is in both products");
            }
            if(oldFieldDef != null) {
              //field exists in both types..
              oldCField = oldFields[oldFieldDef.id];
              if(oldCField != null) {
                newField.initFromPreviousSelection(oldCField);
              } else {
                log("setProduct: field with code " + fieldDef.code + " is missing from oldProduct");
              }
            }
            newField.refreshHtml();
          }
        }
      }
      this.setQtyFromMulti();
      
      
      this.initFieldDefaults();
      
      //for each view that is used
      log("Setting Product" );
      log(product);
      log(oldProduct);
      
      
      //store old views in an ordered array
      var oldViews = [];
      var usedView = null;
      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())) {
          usedView = oldViews[i];
        }
        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 = {};
      var movedOk = false;
      //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);
          var mr = newCView.moveToView(oldViews[i]);
          if(mr != 1) movedOk = true;
          //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...
      
      if(!movedOk && d.inSimpleMode(TEMPLATE_MODE_DESIGN)) {
        log("Nothing moved in first pass... now using best match...");  
        var bestView = product.getBestMatchedView(usedView);
        if(bestView != null) {
          var newCView = this.getView(bestView.id, true);
          newCView.moveToView(usedView);
        }
      }
      
      //keep the oldViews arounf
      //anything in oldViews needs to be cleaned up...
      for(var i=0; i < oldViews.length; i++) {
        if(oldViews[i] != null) {
          oldViews[i].remove(); 
        }
      }
      this.stash = null; //oldViews; DONT STASH VIEWS ANYMORE: confusing to customers...
      
      
      
      
    } else {
      
      this.initFields();
      this.initFieldDefaults();
      this.selectedColorId = null;
    }
    this.imageURL = product.imageURL + '?v=' + (new Date()).getTime();
    this.largeImageURL = product.largeImageURL;
  },
  
  //called after moving views between products and the view thumbnails are rendered to add the warning against....
  afterProductChanged: function CP_afterProductChanged() {
    this.callServerToRenderThumbnails();
    for(var k in this.views) {
      this.views[k].validate();
    }
  },
  

  getPercentMarkup: function CP_getPercentMarkup(forcePercent) {
    var percentMarkup = 1;
    if((forcePercent == true) || (!this.usingCustomProductWithFixedMarkup() && this.markupMode == 0)) {
      percentMarkup =  1 + this.markupAmount / 100;
      log("Percent Markup=" + percentMarkup);
    }
    return percentMarkup;
  },
  
  getFixedMarkup: function CP_getFixedMarkup() {
    var fixedMarkup = 0;
    if(this.usingCustomProductWithFixedMarkup()) {
      fixedMarkup = this.customProduct.markup;
      log("Fixed Custom Product Markup=" + fixedMarkup);
    } else if(this.markupMode == 1) {
      fixedMarkup = this.markupAmount;
      log("Fixed Markup=" + fixedMarkup);
    } else if(this.markupMode != 0) {
      fixedMarkup = this.customProduct.markupAmount;
      log("Fallback Fixed Custom Product Markup=" + fixedMarkup);
    }
    return fixedMarkup;
  },
  
  registerLegacyPrice: function(legacyPrice, livePrice, obj) {
    if(legacyPrice != null && legacyPrice != livePrice) {
      legacyPrice = d.round(legacyPrice);
      livePrice = d.round(livePrice);
      if(legacyPrice != livePrice) {
        this.legacyPriceChanges.push({legacyPrice:legacyPrice, livePrice:livePrice, obj:obj, type:-1});
        d.registerLegacyPrice(this);
      }
    }
  },
  
  registerLegacyPriceDelta: function(legacyPriceDelta, livePriceDelta, obj, type) {
    if(legacyPriceDelta != null && legacyPriceDelta != livePriceDelta) {
      legacyPriceDelta = d.round(legacyPriceDelta);
      livePriceDelta = d.round(livePriceDelta);
      if(legacyPriceDelta != livePriceDelta) {
        this.legacyPriceChanges.push({legacyPriceDelta:legacyPriceDelta, livePriceDelta:livePriceDelta, obj:obj, type:type});
        d.registerLegacyPrice(this);
      }
    }
  },
  
  hasLegacyChanges: function() {
    return (this.legacyPriceChanges.length > 0);
  },
  
  getLegacyChanges: function() {
    return this.legacyPriceChanges;
  },
  
  //clear any caches that may e changed when legacy setting changes..
  resetLegacyOption: function() {
    for(var k in this.views) {
      this.views[k].resetLegacyOption();
    }
  },
  
  getColorType: function CP_getColorType() {
    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);
    }
    
    return colorType;
  },
  
  getUnitPrice: function CP_getUnitPrice(showMinQtyChangeNotice) {
    this.resetPricing(true, showMinQtyChangeNotice);
    return this.price;
  },
  
  resetPricing: function CP_getUnitPrice(force, showMinQtyChangeNotice) {
    log("resetPricing of " + this.id);
    
    var percentMarkup = this.getPercentMarkup();
    var fixedMarkup = this.getFixedMarkup();
    log("percentMarkup= " + percentMarkup + " fixedMarkup=" + fixedMarkup);
    log("Branding is :"+this.brandId);
    
    this.legacyPriceChanges = [];
    if (this.cp_type == CP_CREDIT) {
      this.pricing = priceFromRRP(this.price, false, percentMarkup, 0.0);
      
      this.price = this.pricing.getOverride();
      this.cost = 0.0;
      log("final unit pricing=" + this.pricing.toString());
    } else if (this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_FREE_FORM) {
      if(this.rrp == null) {
        this.rrp = this.price;
      }
      this.pricing = priceFromCost(this.rrp, d.commissionRate, percentMarkup, 0.0, this.overridingPrice, !this.isAutoPrice, d.legacyPrice(this.legacyPrice));
      
      this.pricing.round();
    
      this.pricing = d.roundPriceObject(this.pricing);
      
      this.price = this.pricing.getOverride();
      this.cost = this.pricing.wsCost;
      this.basePrice = this.pricing;
      log("final unit pricing=" + this.pricing.toString());
    } else if (this.cp_type == CP_DIGITIZATION) {
      if(this.rrp == null) {
        this.rrp = rpToRRP(this.rp, d.commissionRate, percentMarkup, 0.0);
      } 
      this.pricing = priceFromCost(this.rrp, d.commissionRate, percentMarkup, 0.0, this.overridingPrice, !this.isAutoPrice, d.legacyPrice(this.legacyPrice));
      
      this.pricing.round();
    
      //we need to apply rounding to match internet prices
      this.pricing = d.roundPriceObject(this.pricing);
      
      this.price = this.pricing.getOverride();
      this.cost = this.pricing.wsCost;
      this.basePrice = this.pricing;
      log("final unit pricing=" + this.pricing.toString());
    } else {
    
      var colorType = this.getColorType();
      
      var cost = 0;
      
      
      if(colorType == 0 || this.product.type.pricingType == PRICE_TYPE_FLAT) {
        cost = parseFloat(this.product.price[0]);
      } else {
        cost = parseFloat(this.product.price[colorType]);
      }
      
      log("cost=" + cost + ", d.commissionRate=" + d.commissionRate + ", percentMarkup=" + percentMarkup + ", this.overridingBasePrice=" + this.overridingBasePrice);
      var price = priceFromCost(cost, d.commissionRate, percentMarkup, fixedMarkup, this.overridingBasePrice, !this.isAutoBasePrice, d.legacyPrice(this.getLegacyBasePrice()));
      
      //dont register if we have a size field with fixed pricing (it will be registered separatly)
      if(this.getSelectedSize() == null || this.getFieldData(this.product.type.sizeField.id).priceModifierType != PRICE_MODIFY_FIXED) {
        this.registerLegacyPrice(this.legacyBasePrice, price.crp, this);
      }
      
      this.basePrice = price;
  
      var basePriceWithDecoration = price;
      
      
      log("basePrice=" + price.toString());
      
      //this.buildSizeOptions();
      
      var pricingData = {
        percentMarkup: percentMarkup,
        basePrice: price,
        //baseColorDelta: baseColorDelta,
        basePriceWithDecoration: price,
        //baseCostWithDecoration: cost,
        //unUsedDecorationCost: 0.0,
        // baseCost: cost,
        views: new MapList(),
        fields: new MapList(),
        fixedPrices: {},
        decorationPrice: priceZero(),
        digitizingFee: priceZero(),
        //decorationCost: priceZero(),
        fieldPrice: priceZero(),
        fieldPriceWithoutSizes: priceZero()
        //fieldCost: priceZero()
      };
      
      this.pricingData = pricingData;
      this.pricingData.fixedPrices[-1] = this.basePrice;
      
      
      
      var decorationPrice = priceZero();
      var fieldDecorationCost = 0.0;
      //init the base price to include the first area price that can use the selected process
      if((!this.hasDecorations()) && this.defaultProcessId != -1) { //as soon as we have started decorating we no longer use default prices...(d.userSelectedProcessId != -1 removed for BH)
        var foundDefaultPricing = false;
        var procs = [];
        if (this.defaultProcessId != null) procs.push(this.defaultProcessId);
        procs.push(this.product.getFirstProcessId());
        
        for(var a=0; a < 2; a++) {
          var dProcessId = procs[a];
          log("No Customising Done: Using default decoration prices from process " + dProcessId);
          for(var i =0; i < this.product.views.list.size(); i++) {
            var pView = this.product.views.list[i];
            var noUsageProcess = pView.getDefaultPricingProcess(dProcessId);
            if(noUsageProcess != null) {
              //we now need to store a configured process to represent this...
              var cProcess = this.getCProcess(noUsageProcess);
              var cView = cProcess.configuredViewArea.configuredView;
              var pricingState = {processes: {}};
              for(var i=0; i < processes.list.length; i++) {
                pricingState.processes[processes.list[i].id] = { totalUsed: 0, currentCount: 0} ;
              }
              this.defaultProcessId = dProcessId;
              pricingState.processes[this.defaultProcessId].totalUsed = 1;
              cProcess.defaultOn = true;
              decorationPrice = cView.calculatePrice(colorType, pricingData, percentMarkup, pricingState, noUsageProcess);
              pricingData.decorationPrice = decorationPrice;
              /*
              
                var cost = noUsageProcess.getDefaultDecorationPrice(colorType);
                log("Default Decoration Cost:" + cost);
              pricingData.basePriceWithDecoration += (cost * d.wsRate * percentMarkup);
              //pricingData.baseCostWithDecoration += cost;
                fieldDecorationCost = cost;
              */
              this.pricingState = pricingState;
              foundDefaultPricing = true;
              break;
            } else {
              log("noUsageProcess not found");
            }
          }
          if(!foundDefaultPricing) {
            log("No default pricing found for process " + dProcessId);
          } else {
             break;
          }
        }
        if(this.legacyDefaultDecorationPrice != null) {
          decorationPrice.legacy = d.legacyPrice(this.legacyDefaultDecorationPrice);  
          if(decorationPrice.legacy != decorationPrice.crp) {
            d.registerLegacyPrice(new LegacyPriceChange(this.getName() + ":Default Decoration Cost", -1, decorationPrice.legacy, decorationPrice.crp));
          }
        }
      } else {
        //update the decoration cost
        var pricingState = {processes: {}};
        for(var i=0; i < processes.list.length; i++) {
          pricingState.processes[processes.list[i].id] = { totalUsed: 0, currentCount: 0} ;
        }
        //get usage counts...(needed by dtg price tables...)
        for(var i =0; i < this.product.views.list.size(); i++) {
          var pView = this.product.views.list[i];
          var cView = this.views[pView.id];
          if((cView != null) && (cView.isUsed())) {
            for(var j=0; j < pView.areas.list.size(); j++) {
              var pArea =  pView.areas.list[j];
              var cArea = cView.areas[pArea.id];
              if((cArea != null) && (cArea.isUsed())) {
                for(var x=0; x < processes.list.length; x++) {
                  if(cArea.isUsed(processes.list[x].id)) {
                    pricingState.processes[processes.list[x].id].totalUsed++;
                  }
                }
              }
            }
          } 
        }
        this.pricingState = pricingState;
        
        for(var i =0; i < this.product.views.list.size(); i++) {
          var pView = this.product.views.list[i];
          var cView = this.views[pView.id];
          if((cView != null) && (cView.isUsed())) {
            decorationPrice = decorationPrice.add(cView.calculatePrice(colorType, pricingData, percentMarkup, pricingState));
          } 
        }
        pricingData.decorationPrice = decorationPrice;
      }
      log("decorationPrice=" + decorationPrice.toString());
      
      this.pricingData.fixedPrices[-2] = decorationPrice;
      
      var unitPrice = this.basePrice.add(decorationPrice); 
      
      var totalFieldPrice = this.fieldPrice(colorType, decorationPrice, this.basePrice, percentMarkup, pricingData, true); 
      
      
      //unitCost += fieldCost;
      //var fieldPrice = fieldCost * d.wsRate * percentMarkup;
      //pricingData.fieldCost = fieldCost;
      //unitPrice += fieldPrice;
      pricingData.fieldPrice = totalFieldPrice;
      log("totalFieldPrice=" + totalFieldPrice.toString());
      //round to full cents...
      //unitPrice = parseFloat(Math.round(unitPrice * 100.0) / 100);
      //unitCost = parseFloat(Math.round(unitCost * 100.0) / 100);
      //log("final unit price=" + unitPrice + " cost=" + unitCost);
      
      this.pricing = unitPrice.add(totalFieldPrice);
      this.pricing.round();
      
      this.pricing = d.roundPriceObject(this.pricing);
      
      this.price = this.pricing.getOverride();
      this.cost = this.pricing.wsCost;
      log("final unit pricing=" + this.pricing.toString());
    }
    this.undiscountedLineTotal = this.pricing.multiply(this.qty);
    
    if(this.needToCalcDiscount) { //backwards compatability...
      log("reverseEngineerDiscountRates");
      this.reverseEngineerDiscountRates();  
      this.needToCalcDiscount = false;
    }
    
    //check min qty & bundle size
    this.checkAndUpdateQty(showMinQtyChangeNotice);
    
    //we now have the undiscounted unit price... lets figure out discounting... get calculated amounts first
    this.bulkDiscountRate = this.getBulkDiscountPercent();
    
    
    
    
    var dPrice = this.pricing.getOverride();
    var dCost = this.pricing.wsCost;
    
    if(this.bulkDiscountRate == 0) {
      log("Product has no discount");
      this.bulkDiscount = 0.0;
    } else {
      if(this.getBulkDiscountToBase()) {
        dPrice = this.basePrice.getOverride();
        dCost = this.basePrice.wsCost;
        log("Discount applies to base (price=" + dPrice + " and cost=" + dCost + ")");
      } else {
        log("Discount applies to whole price (price=" + dPrice + " and cost=" + dCost + ")");
      }
      if(d.affiliateDiscounting) {
        log("applying discount of " + this.bulkDiscountRate + "% to " + dPrice + ", qty =" + this.qty);
        this.bulkDiscount =  Math.round(dPrice * this.bulkDiscountRate * this.qty) / 100.0 ;
      } else {
        log("applying discount of " + this.bulkDiscountRate + "% to " + dCost + ", qty =" + this.qty);
        this.bulkDiscount =  Math.round(dCost * this.bulkDiscountRate * this.qty) / 100.0 ;
      }
      log("before rounding this.bulkDiscount = " + this.bulkDiscount);
    }
    
    this.customerDiscount = (this.pricing.getOverride() * this.qty * cDisc / 100.0).round();
    if (cDisc + this.bulkDiscountRate > 100.0) {
      this.calculatedDiscountAmount = (this.pricing.getOverride() * this.qty).round();
      this.calculatedDiscountRate = 100;
    } else {
      this.calculatedDiscountAmount = this.bulkDiscount + this.customerDiscount;
      this.calculatedDiscountRate = d.round(this.calculatedDiscountAmount / (this.pricing.getOverride() * this.qty)  * 100.0);
    }
    log("this.calculatedDiscountRate  = " + this.calculatedDiscountRate );
    
    if (isNaN(this.calculatedDiscountRate)) this.calculatedDiscountRate = 0;
    
    
   
    
    //we know the calculated rates (allowing for legacy values)
    //now lets get the actual rates
    if(this.legacyDiscount && this.legacyDiscountKey == null) { //need to init this key after the price is calculated
      this.legacyDiscountKey = this.calcLegacyDiscountKey(); 
    }
        
    this.checkBulkDiscountLegacyChanges();
    
    var legacyDiscountAmount = null;
    
    if (this.totalLocked) {
      this.typedOverridingDiscount = this.discount = this.overridingDiscount = (this.price * this.qty - this.overridingTotal).round();
      this.discountType = AMOUNT_TYPE_FIXED;
      this.totalLocked = false;
      this.discountLocked = true;
      this.discountRate = d.round(this.discount / (this.pricing.getOverride() * this.qty)  * 100.0);
      
    } else if (this.discountLocked) {
      log("this.price=" + this.price);
      log("this.typedOverridingDiscount=" + this.typedOverridingDiscount);
      if (this.discountType == AMOUNT_TYPE_PERCENT) {
        this.discountRate = this.typedOverridingDiscount * 100.0;
        this.discount = this.overridingDiscount = this.pricing.getOverride() * this.qty * this.typedOverridingDiscount;
      } else {
        this.discount = this.overridingDiscount = this.typedOverridingDiscount;
        this.discountRate = d.round(this.discount / (this.pricing.getOverride() * this.qty)  * 100.0);
      }
    } else {
      //nothing getting overridden.. lets use calculates (or legacy) values
      var legacyDiscountKey = this.calcLegacyDiscountKey();
      if(this.legacyDiscountKey != null && legacyDiscountKey == this.legacyDiscountKey && d.useLegacyPrices) {

        //legacy discount is ALWAYS a %, we need the fixed amount....
        var ldPerc = this.legacyDiscount * 100.0;
        this.discount = legacyDiscountAmount =  Math.round(this.pricing.getOverride() * ldPerc * this.qty) / 100.0 ;
        
        //legacyDiscountAmount = this.discount = d.round(legacyDiscountAmount);
        this.discountRate = this.legacyDiscount * 100.0;
        if(this.discount != this.calculatedDiscountAmount) {
          log("Overriding the discount from " + this.calculatedDiscountAmount + " to legacy value " + this.discount); 
          d.registerLegacyPrice(new LegacyPriceChange(this.getName() + ":Discount Amount", -1, this.discount, this.calculatedDiscountAmount));
        }
      } else {
        this.discount = this.calculatedDiscountAmount;
        this.discountRate = this.calculatedDiscountRate;
      }
    }
    this.discount.round();
    
    //we need the discount as a price object
    this.discountPrice = priceFromCost(this.calculatedDiscountAmount, 0, 1, 0, this.overridingDiscount, this.totalLocked || this.discountLocked, legacyDiscountAmount);
    this.discountPrice.round();
    
    //now we have discounts, lets get the final price
    log("discount=" + this.discountPrice.toString());
    
    
    
    this.total = this.pricing.multiply(this.qty).minus(this.discountPrice);
    
    this.calculatedFinalTotal = (this.pricing.getOverride() * this.qty - this.calculatedDiscountAmount.round()).round();
    
    log("total=" + this.total.toString());
    if (this.totalLocked) {
      this.finalTotal = this.overridingTotal;
    } else if (this.discountLocked) {
      this.finalTotal = this.pricing.getOverride() * this.qty - this.overridingDiscount.round();
    } else {
      this.finalTotal = this.total.getOverride();
    }
    
    
    //this.pricingData = pricingData;
    return this.price;
  },
  
  fieldPrice: function(colorIdx, decorationPrice, basePrice, percentMarkup, pricingData, firstPass) {
    log("fieldPrice for colorIdx=" + colorIdx);
    var fixedMarkups = {};
    var percentMarkups = {};
    var finalMarkups = {};
    //sum defaults for the fields...
   
    var requireTwoPass = false;
    
    
    //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];
      var cField = this.cFields[pField.id];
      if(pField != null && pField.fieldDef != null && cField != null) { //if the objects exist, its valid to use (support legacy pricing)
        var fieldDef = pField.fieldDef;
        var removed = (!pField.used || fieldDef.discontinued);
        
        if(pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
          if(cField != null) {//null if discontinued and not used
            var fieldData = this.getFieldData(fieldDef.id);
            
            var priceModifierType = fieldData.priceModifierType;
            if (priceModifierType != PRICE_MODIFY_NONE) {
              var fieldPriceData = {
                id: fieldDef.id,
                type: 4,
                field: cField,
                selectedOptions: new MapList()
              };
              var legacyId = "";
              pricingData.fields.add(fieldPriceData);
              var fieldMarkup = 0.0;
              var fieldQty = 0;
              var fPrice = priceZero();
              var fieldColorIdx = (fieldData.pricingType == PRICE_TYPE_FLAT) ? 0 : colorIdx;
              if(fieldDef.typeOptions.list) {
                var selectedOptions = cField.getSelectedOptions(true);
                legacyId = (fieldData.pricingType == PRICE_TYPE_FLAT) ? "BP:p=" + basePrice.getOverride().toPrice() + "&SP:" : "BP:p=" + basePrice.getOverride().toPrice() + "&CT:c=" + colorIdx;
                var selectedOptionIds = [];
                
                for(var j=0; j < selectedOptions.length; j++) {
                  var cChoice = selectedOptions[j];
                  var priceModifierToUse = pField.usePriceDefaults ? cChoice.def.defaultPrices : cChoice.pDef.priceDelta;
                  var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
                  choicePrice = parseFloat(choicePrice);
                  if(removed) choicePrice = 0.0;
                  var overrideDelta = null;
                  var legacyDelta = null;
                  if(cChoice != null) {
                    overrideDelta = cChoice.getOverrideDelta(basePrice.getLegacy());
                    legacyDelta = cChoice.getLegacyDelta(basePrice.getLegacy(), legacyId);
                    log("overrideDelta=" + overrideDelta + ", legacyDelta=" + legacyDelta + ", cChoice.unitPrice=" + cChoice.unitPrice);
                    
                    
                  }
                  
                  var cPrice = null;
                  if(priceModifierType == PRICE_MODIFY_FIXED) {
                    cPrice = priceFromCost(choicePrice, d.commissionRate, percentMarkup, 0, overrideDelta, (overrideDelta != null), d.legacyPrice(legacyDelta));
                  } else {
                    cPrice = priceFromCost(choicePrice, 0, 1.0, 0, null,false, legacyDelta); //the price is a %... 
                  }
                  
                  if(cChoice != null) {
                    if(cChoice.isAutoPrice && (d.setUnitPrices || d.forceLegacyPrices)) { //update the legacy key and delta
                      cChoice.unitDelta = legacyDelta == null ? cPrice.getLegacy() : legacyDelta;
                      if(priceModifierType == PRICE_MODIFY_FIXED) {
                         cChoice.unitPrice = cChoice.unitDelta + basePrice.getLegacy();
                      }
                      cChoice.legacyKey = legacyId;
                    }
                  }
                  
                  if(legacyDelta != null && firstPass) { 
                    if((priceModifierType == PRICE_MODIFY_FIXED)&&(fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE)) {
                      this.registerLegacyPrice(legacyDelta + basePrice.getLegacy(), cPrice.crp + basePrice.crp, cChoice);
                    } else {
                      this.registerLegacyPriceDelta(legacyDelta , cPrice.crp , cChoice, priceModifierType);
                    }
                  }
      
                  var optPriceData = {
                    id: cChoice.optionId,
                    type: 5,
                    option: cChoice,
                    qty: null,
                    total: cPrice
                  };
                  if(cField.multiSelect || fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
                    selectedOptionIds.push("" + cChoice.optionId + "x" +  cChoice.qty);
                    if(priceModifierType == PRICE_MODIFY_FIXED) {
                      log("fieldPrice: multi select " + cChoice.def.name + ", qty=" + cChoice.qty + ":" + cPrice.toString());
                      cPrice = cPrice.multiply(parseFloat(cChoice.qty));  
                    }
                    fPrice = fPrice.add(cPrice);
                    fieldMarkup += choicePrice * cChoice.qty;
                    fieldQty += cChoice.qty;
                    optPriceData.qty = cChoice.qty;
                  } else {
                    fPrice = fPrice.add(cPrice);
                    fieldMarkup += choicePrice;
                    selectedOptionIds.push("" + cChoice.optionId);
                  }
                  log("fieldPrice: option " + cChoice.def.name + ":" + cPrice.toString());
                  fieldPriceData.selectedOptions.add(optPriceData);
                }
                selectedOptionIds = selectedOptionIds.sortBy( function(el) { return el;});
                //legacyId += "&o=" + selectedOptionIds.join(",");
                
                if (fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE && this.unspecSizeQty > 0) {
                  if(firstPass) {
                    requireTwoPass = true;
                  } else {
                    fieldQty += this.unspecSizeQty;
                    this.getUnspecSizePrice();
                    var unspecPrice = this.unspecSizePricing.minus(this.basePrice).multiply(this.unspecSizeQty);
                    fPrice = fPrice.add(unspecPrice);
                  }
                }
                
                if(cField.multiSelect || fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
                  if(fieldQty == 0) {
                    fPrice = priceZero();
                    fieldMarkup = 0;
                  } else {
                    fPrice = fPrice.divide(parseFloat(fieldQty));
                    fieldMarkup = fieldMarkup / fieldQty;
                  }
                  
                  log("fieldPrice: rounding multiSelect:" + fPrice.toString());
                }
              } else if(cField.isUsed()) {
                var priceModifierToUse = pField.usePriceDefaults ? fieldDef.priceModifier : pField.priceDelta;
                var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
                
                if(removed) choicePrice = 0.0;
                
                if(priceModifierType == PRICE_MODIFY_FIXED) {
                  fPrice = priceFromCost(choicePrice, d.commissionRate,percentMarkup, 0, null, false, cField.getLegacyDelta()); 
                } else {
                  fPrice = priceFromCost(choicePrice, 0, 1.0, 0, null,false, cField.getLegacyDelta()); //the price is a %... 
                }
                fieldMarkup += parseFloat(fPrice.getOverride());
                
                if(cField.getLegacyDelta() != null && firstPass) {
                  this.registerLegacyPriceDelta(fPrice.getLegacy() , fPrice.crp , cField, priceModifierType);
                }
                
                if(d.setUnitPrices) cField.unitDelta = fPrice.getLegacy(); //update to live pricing if no legacy
                
                legacyId = "USED";
              }
              fieldPriceData.legacyID = legacyId;
              fieldPriceData.total = fPrice; 
              if(priceModifierType == PRICE_MODIFY_FIXED) {
                fixedMarkups[fieldDef.id] = fPrice; //priceFromCost(fieldMarkup,percentMarkup, 0);
                this.pricingData.fixedPrices[fieldDef.id] = fPrice;
                log(fieldDef.name + " has a fixed markup of " + fPrice.toString());
              } else {
                percentMarkups[fieldDef.id] = fPrice; //fieldMarkup; use fPrice as it contains live and legacy prices
                log(fieldDef.name + " has a percent markup of " + fPrice.getLegacy() + "% ("  + fPrice.toString() + ")");
              }
            } else if (fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
              var fieldPriceData = {
                id: fieldDef.id,
                type: 4,
                field: cField,
                selectedOptions: new MapList()
              };
              var legacyId = "";
              pricingData.fields.add(fieldPriceData);
              var fieldQty = 0;
              var fPrice = priceZero();
              
                var selectedOptions = cField.getSelectedOptions(true);
                legacyId = "SP:";
                var selectedOptionIds = [];
                
                for(var j=0; j < selectedOptions.length; j++) {
                  var cChoice = selectedOptions[j];
                  
                  var overrideDelta = null;
                  var legacyDelta = null;
                  if(cChoice != null) {
                    overrideDelta = cChoice.getOverrideDelta(basePrice.getLegacy());
                    legacyDelta = cChoice.getLegacyDelta(basePrice.getLegacy(), legacyId);
                    log("overrideDelta=" + overrideDelta + ", legacyDelta=" + legacyDelta + ", cChoice.unitPrice=" + cChoice.unitPrice);
                    if(cChoice.isAutoPrice && (d.setUnitPrices || d.forceLegacyPrices)) { //update the legacy key and delta
                      cChoice.unitDelta = legacyDelta == null ? choicePrice : legacyDelta;
                      if(priceModifierType == PRICE_MODIFY_FIXED) {
                         cChoice.unitPrice = cChoice.unitDelta + basePrice.getLegacy();
                      }
                      cChoice.legacyKey = legacyId;
                    }
                  }
                  
                  var cPrice = priceFromCost(0, d.commissionRate, percentMarkup, 0, overrideDelta, (overrideDelta != null), d.legacyPrice(legacyDelta));
                  
                  if(legacyDelta != null && firstPass) {
                    this.registerLegacyPrice(legacyDelta + basePrice.crp, cPrice.crp + basePrice.crp, cChoice);
                  }
                  
                  var optPriceData = {
                    id: cChoice.optionId,
                    type: 5,
                    option: cChoice,
                    qty: null,
                    total: cPrice
                  };
                  
                  selectedOptionIds.push("" + cChoice.optionId + "x" +  cChoice.qty);
                  
                  log("fieldPrice: multi select " + cChoice.def.name + ", qty=" + cChoice.qty + ":" + cPrice.toString());
                  cPrice = cPrice.multiply(parseFloat(cChoice.qty));  
                  
                  fPrice = fPrice.add(cPrice);
                  fieldQty += cChoice.qty;
                  optPriceData.qty = cChoice.qty;
                  
                  log("fieldPrice: option " + cChoice.def.name + ":" + cPrice.toString());
                  fieldPriceData.selectedOptions.add(optPriceData);
                }
                selectedOptionIds = selectedOptionIds.sortBy( function(el) { return el;});
                //legacyId += "&o=" + selectedOptionIds.join(",");
                
                if (this.unspecSizeQty > 0) {
                  if(firstPass) {
                    requireTwoPass = true;
                  } else {
                    fieldQty += this.unspecSizeQty;
                    this.getUnspecSizePrice();
                    var unspecPrice = this.unspecSizePricing.minus(this.basePrice).multiply(this.unspecSizeQty);
                    fPrice = fPrice.add(unspecPrice);
                  }
                }
                
                if(fieldQty == 0) {
                  fPrice = priceZero();
                } else {
                  fPrice = fPrice.divide(parseFloat(fieldQty));
                }
                
                log("fieldPrice: rounding multiSelect:" + fPrice.toString());
              
              fieldPriceData.legacyID = legacyId;
              fieldPriceData.total = fPrice;
              
              fixedMarkups[fieldDef.id] = fPrice;
            }
          }
        }
      }
    }
    fixedMarkups[-1] = basePrice;   
    fixedMarkups[-2] = decorationPrice;  

    var totalFieldMarkup = priceZero();
    var totalFieldMarkupWithoutSizes = priceZero();
    
    //2nd pass: using price data from first pass, apply percents and add fixed
    
    for(var i=0; i < this.product.fields.list.size(); i++) {
      var pField = this.product.fields.list[i];
      var cField = this.cFields[pField.id];
      if(pField != null && pField.fieldDef != null && cField != null) { //if the objects exist, its valid to use (support legacy pricing)
        var fieldDef = pField.fieldDef;
        if(pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_COLOR) {
          if(cField != null) { //null if discontinued and not used
            var fieldData = this.getFieldData(fieldDef.id);
            var priceModifierType = fieldData.priceModifierType;
            if (priceModifierType != PRICE_MODIFY_NONE) {
              var fieldMarkup = priceZero();
              var fieldPriceData = pricingData.fields.byId[fieldDef.id];
              if(priceModifierType == PRICE_MODIFY_FIXED) {
                 fieldMarkup = fixedMarkups[fieldDef.id];
                 fieldMarkup.round();
              } else {
                var totalFieldQty = 0;
                if(fieldDef.typeOptions.list) {
                  for(var k=0; k < fieldPriceData.selectedOptions.list.length; k++) {
                    var optPriceData = fieldPriceData.selectedOptions.list[k];
                    var cPrice = priceZero();
                    var thisPercent = parseFloat(optPriceData.total.getLegacy()) / 100.0 ;
                    
                    //we need to apply a percentage to the selected fixed fields
                    for(var j=0; j < fieldData.percentOf.length; j++) {
                      var percentOfId = fieldDef.percentOf[j];
                      var fixedMarkupData = null;
                      if(percentOfId == -1) {
                        fixedMarkupData = { total: basePrice};
                      } else if(percentOfId == -2) {
                        fixedMarkupData = { total: decorationPrice};
                      } else {
                        fixedMarkupData = pricingData.fields.byId[percentOfId];
                      }
                      if(fixedMarkupData == null) {
                        log("Unable to get fixed markup for field " + percentOfId);
                      } else {
                        log("Adding " + (thisPercent * 100) + "% of " + fixedMarkupData.total.toString() + " for field " + percentOfId);
                        cPrice = cPrice.add(fixedMarkupData.total.multiply(thisPercent));
                      }
                    }
                    // cPrice now has the rrp in override.. update the cost/wsCost, rrp and crp of optPriceData.total
                    cPrice.override = optPriceData.total.override;
                    cPrice.isOverriden = optPriceData.total.isOverriden;

                    if(optPriceData.option.isAutoPrice && d.setUnitPrices) {
                       optPriceData.option.unitPrice = cPrice.getLegacy() + basePrice.getLegacy(); //update the unit price (for debug purposes on server)
                    }
      
                    
                    cPrice.round();
                    optPriceData.total = cPrice;
                    if(cField.multiSelect) {
                      log("fieldPrice: multi select " + optPriceData.option.def.name + ", qty=" + optPriceData.qty + ":" + cPrice.toString());
                      cPrice = cPrice.multiply(parseFloat(optPriceData.qty));  
                      totalFieldQty += optPriceData.qty;
                    }
                    
                    fieldMarkup = fieldMarkup.add(cPrice);
                  }
                  if(cField.multiSelect) {
                    fieldMarkup = fieldMarkup.divide(parseFloat(totalFieldQty));
                  }
                  fieldMarkup.round();
                  fieldPriceData.total = fieldMarkup;
                } else if(cField.isUsed()) {
                  
                  var thisPercent = parseFloat(fieldPriceData.total.getLegacy()) ;
                  
                  
                  for(var j=0; j < fieldData.percentOf.length; j++) {
                    var percentOfId = fieldData.percentOf[j];
                    var fixedMarkupData = null;
                    if(percentOfId == -1) {
                      fixedMarkupData = { total: basePrice};
                    } else if(percentOfId == -2) {
                      fixedMarkupData = { total: decorationPrice};
                    } else {
                      fixedMarkupData = pricingData.fields.byId[percentOfId];
                    }
                    if(fixedMarkupData == null) {
                      log("Unable to get fixed markup for field " + percentOfId);
                    } else {
                      log("Adding " + (thisPercent * 100) + "% of " + fixedMarkupData.total.toString() + " for field " + percentOfId);
                      fieldMarkup = fieldMarkup.add(fixedMarkupData.total.multiply(thisPercent/100.0));
                    }
                  }
                }
              }
 
              log("Calcing Field Markup, " + fieldDef.name + " adds $" + fieldMarkup.toString());
              totalFieldMarkup =  totalFieldMarkup.add(fieldMarkup);
              if (pField.fieldDef.fieldType != FIELD_TYPE_PRODUCT_SIZE) {
                totalFieldMarkupWithoutSizes = totalFieldMarkupWithoutSizes.add(fieldMarkup);
              }
              
            } else if (fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
              var fieldMarkup = fixedMarkups[fieldDef.id];
              fieldMarkup.round();
              
              log("Calcing Field Markup, " + fieldDef.name + " adds $" + fieldMarkup.toString());
              totalFieldMarkup =  totalFieldMarkup.add(fieldMarkup);
            }
          }
        }
      }
    }
    
    this.buildSizeOptions();
    
    if(requireTwoPass) {
      log("2nd field price pass");
      return this.fieldPrice(colorIdx, decorationPrice, basePrice, percentMarkup, pricingData, false);
    } else {
      pricingData.fieldPriceWithoutSizes = totalFieldMarkupWithoutSizes;
      return totalFieldMarkup;
    }
  },
  
  hasSizeField: function CP_hasSize() {
    return (this.getSelectedSize() != null);
  },
  
  supportMultiSizes: function CP_supportMultiSizes() {
    if(!this.hasSizeField()) return false;
    if(this.getSelectedSize().multiSelect) return true;//for oms + legacy
    var pSizeField = this.product.getSizeField(true);
    if(pSizeField == null || pSizeField.multiOption == null) return false;
    return true;
  },
  
  useMultiSizes: function CP_useMultiSizes() {
    if (this.supportMultiSizes()) {
      var pSizeField = this.product.getSizeField();
      cSizeField = this.cFields[pSizeField.id];
      if (!cSizeField.multiSelect) {
        cSizeField.setValue(pSizeField.multiOption.id + '', null, true);
      }
      return true;
    } else {
      return false;
    }
  },
  
  //assumes this.pricingData is up-to-date 
  buildSizeOptions: function CP_buildSizeOptions() {
    if(!this.hasSizeField()) return null;
    var pSizeField = this.product.getSizeField(true);
    if(pSizeField == null) return null;
    
    var percentMarkup = this.getPercentMarkup();
    
    var fieldData = this.getFieldData(pSizeField.id);
    
    var priceModifierType = fieldData.priceModifierType;
    
    var colorType = this.getColorType();
    
    var cField = this.cFields[pSizeField.id];
    
    var fieldColorIdx = 0;
    if(fieldData.pricingType != PRICE_TYPE_FLAT) {
      fieldColorIdx = colorType;
    }
    
    var legacyKey = (fieldData.pricingType == PRICE_TYPE_FLAT) ? "BP:p=" + this.pricingData.basePrice.getOverride().toPrice() + "&SP:" : "BP:p=" + this.pricingData.basePrice.getOverride().toPrice() + "&CT:c=" + fieldColorIdx;

    
    var options = new MapList();
    for(var i=0; i < pSizeField.options.list.length; i++) {
      var pOpt = pSizeField.options.list[i];
      if(!pOpt.def.isMulti) {
        var optData = {
          id: pOpt.id, 
          pOpt: pOpt,
          qty: 0,
          subs: new MapList()
        };
        
        
        
        cOpt = cField.options[pOpt.id];
        
        if(pOpt.def.subs != null) {
          for(var j=0; j < pOpt.def.subs.list.length; j++) {
            if (pOpt.subs != null) {
              var pSubOpt = pOpt.subs[pOpt.def.subs.list[j].id];
              if(pSubOpt != null) {
                cSubOpt = (cOpt == null) ? null : cOpt.subOptions[pSubOpt.id];
                var allowAdd = true;
                if(pSubOpt.def.discontinued || pSubOpt.discontinued || pOpt.discontinued || pOpt.def.discontinued || !pSizeField.used) {
                  allowAdd = false;
                  if(cSubOpt) { //its a discontinued sub option... lets see if we are using it...
                    allowAdd = true;
                  }
                }
                if(allowAdd) {
                  var subOptData = {
                    id: pSubOpt.id, 
                    pOpt: pSubOpt,
                    qty: 0
                  };
                  
                  this.addOptionPrice(subOptData, pSizeField, pSubOpt, cSubOpt, fieldColorIdx, percentMarkup, priceModifierType, legacyKey);
                  if (pSubOpt.id == this.defaultSubSizeOptId) this.defaultSizePrice = subOptData.price.rrp;
                  optData.subs.add(subOptData);
                }
              }
            }
          }
        }
        var allowAdd = true;
        if(pOpt.discontinued || pOpt.def.discontinued || !pSizeField.used) {
          allowAdd = false;
          if((cOpt != null)||(optData.subs.list.length > 0)) {
            allowAdd = true;
          }
        }
        if(allowAdd) {
          this.addOptionPrice(optData, pSizeField, pOpt, cOpt, fieldColorIdx, percentMarkup, priceModifierType, legacyKey);
          if (this.defaultSubSizeOptId == null && pOpt.id == this.defaultSizeOptId) this.defaultSizePrice = optData.price.rrp;
          options.add(optData);
        }
      }
    }
    
    log("=============buildSizeOptions================");
    options.list.sort(this.sortSizeOptions);
    
    log(options);
    this.sizeOptions = options;
    return options;
  },
  
  sortSizeOptions: function(a,b) {
    var aPos = (a.pOpt.def) ? a.pOpt.def.pos : null;
    var bPos = (b.pOpt.def) ? b.pOpt.def.pos : null;
    var result = aPos - bPos;
    
    if (typeof(result) == 'NaN') return 0;
    return result;
  },
  
  getUnspecSizePrice: function() {
    var percentMarkup = this.getPercentMarkup();
    var legacy = null;
    if(this.legacyUnspecSizePrice) {
      legacy = d.round(this.legacyUnspecSizePrice - this.basePrice.getLegacy() + this.basePrice.getOverride());
    }
    this.unspecSizePricing = priceFromCost(this.defaultSizePrice, d.commissionRate, percentMarkup, 0, this.overridingUnspecSizePrice, !this.isAutoSizePrice, d.legacyPrice(legacy));
    
    return this.unspecSizePricing.getOverride();
  },
  
  addOptionPrice: function(optData, pSizeField, pOpt, cOpt, fieldColorIdx, percentMarkup, priceModifierType, legacyKey) {
    //get the unit price of the option.. (base price + size delta)
      
    var priceModifierToUse = pSizeField.usePriceDefaults ? pOpt.def.defaultPrices : pOpt.priceDelta;
    var choicePrice = priceModifierToUse[fieldColorIdx]==null ? 0 : priceModifierToUse[fieldColorIdx];
    choicePrice = parseFloat(choicePrice);
    
    var overrideDelta = null;
    var legacyDelta = null;
    if(cOpt != null) {
      overrideDelta = cOpt.getOverrideDelta(this.basePrice.getLegacy());
      legacyDelta = cOpt.getLegacyDelta(this.basePrice.getLegacy(), legacyKey);
      optData.qty = cOpt.qty;
    }
    
    var fieldMarkup = null;
    if(priceModifierType == PRICE_MODIFY_NONE) {
      fieldMarkup = priceZero();
    } else if(priceModifierType == PRICE_MODIFY_FIXED) {
      fieldMarkup = priceFromCost(choicePrice, d.commissionRate, percentMarkup, 0, overrideDelta, (overrideDelta != null), d.legacyPrice(legacyDelta)); 
    } else {
      var fieldDef = pSizeField.fieldDef;
      var fieldData = this.getFieldData(fieldDef.id);
      fieldMarkup = priceZero();
      if(legacyDelta != null) legacyDelta /= 100.0;
      //we need to apply a percentage to the selected fixed fields
      for(var j=0; j < fieldData.percentOf.length; j++) {
        var percentOfId = fieldData.percentOf[j];
        var fixedMarkupData = this.pricingData.fixedPrices[percentOfId];
        if(fixedMarkupData == null) {
          log("Unable to get fixed markup for field " + percentOfId);
        } else {
          log("Adding " + choicePrice + "% or " + legacyDelta + " of " + fixedMarkupData.toString() + " for field " + percentOfId);
          //fieldMarkup = fieldMarkup.add(fixedMarkupData.markup(thisPercent));
          fieldMarkup = fieldMarkup.add(fixedMarkupData.multiply(choicePrice / 100.0, legacyDelta));
          //fieldMarkup += (fixedMarkup * thisPercent / 100.0);
        }
      }
      fieldMarkup.override = overrideDelta;
      fieldMarkup.isOverriden = (overrideDelta != null);
    }
    log("buildSizeOptions " + pOpt.def.name + ":" + fieldMarkup.toString());
    //we now have the rrp/crp for the field choice with override/legacy set as a delta...
    optData.price = this.basePrice.add(fieldMarkup); //add the base price to get a unit price....
    optData.price.round();
    
    return optData;
  },
  
  generateBulkDiscountKey: function CP_generateBulkDiscountKey() {
    var key = '';
    if (this.product) { 
      key = 'P:' + this.product.id + "&Q=" + this.qty;
    } else {
      key = "Q=" + this.qty;
    }
    return key;
  },
  
  generateBasePriceKey: function CP_generateBasePriceKey() {
    var key = '';
    if (this.product) {
      if(this.product.type.pricingType == PRICE_TYPE_FLAT) {
        key += 'SP';
      } else {
        var colorType = this.getColorType();
        key += 'CP:' + colorType;
      }
      
      key += '&P:' + this.product.id;
      
      return key;
    }
    return null;
  },
  
  getLegacyBasePrice: function CP_getLegacyBasePrice() {
    var legacyPrice = null;
    var key = this.generateBasePriceKey();
    if (this.legacyBasePriceKey == null) this.legacyBasePriceKey = key;
		if (d.forceLegacyPrices || (this.legacyBasePriceKey != null && this.legacyBasePriceKey == key)) {
		  legacyPrice = this.legacyBasePrice;
		}
		return legacyPrice;
  },
  
  getIncTax: function CP_getIncTax() {
    if(d.useLegacyPrices && this.legacyIncTax != null && this.legacyIncTaxKey == this.calcLegacyIncTaxKey()) {
      return this.legacyIncTax;
    } else if(this.product) {
      return !this.product.taxExempt;
    } else {
      return true;
    }
  },
  
  calcLegacyIncTaxKey: function CP_calcLegacyIncTaxKey() {
    if(this.product != null) {
      return this.product.id;
    } else {
      return "ALWAYS";
    }
  },
  
  calcLegacyDiscountKey: function CP_calcLegacyDiscountKey() {
    if(this.cp_type == CP_DIGITIZATION || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_CREDIT || this.cp_type == CP_FREE_FORM) {
      return 'Q:' + this.qty + '&C:' + cCustomerId + '&CE:' + cCustomerEdited + '&UP' + this.price.toPrice();
    } else {
      return "P:" + ((this.product) ? this.product.id : '') + '&Q:' + this.qty + '&C:' + cCustomerId + '&CE:' + cCustomerEdited + '&UP:' + this.price.toPrice();
    }
  },
  
  getDiscountType: function(useFraction) {
    if(this.discountLocked) return this.discountType;
    return AMOUNT_TYPE_PERCENT;
  },
  
  //get the discount as a % or $ based on how the user entered it. If the user has not overriden it, it was always be a %
  getDiscountAsType: function(useFraction) {
    this.resetPricing();
    if(this.discountLocked && this.discountType == AMOUNT_TYPE_FIXED) {
      return this.discount;
    } else {
      if(useFraction) {
        return this.discountRate / 100.0;
      } else {
        return this.discountRate;
      }
    }
  },
  
  reverseEngineerDiscountRates: function CP_reverseEngineerDiscountRates() {
    //we dont know the % rate, of the bulk discount rate... we have the final fixed discount amount and customer discount (cDisc)...
    if(this.legacyDiscount == 0.0) {
      this.legacyBulkDiscount = 0.0;
      this.legacyBulkDiscountToBase = false;
      this.discountType = AMOUNT_TYPE_PERCENT;
      return;
    }

    //what part of the fixed amount is the customer discount?
    var legacyUnitDiscount = this.legacyDiscount / this.qty;
    var cDiscAmount = 0.0;
    if(cDisc != 0.0) {
      cDiscAmount = d.round(this.price * cDisc / 100.0);  
    }
    var bulkDiscAmount = legacyUnitDiscount - cDiscAmount;
    
    log("reverseEngineerDiscountRates: this.legacyDiscount=" + this.legacyDiscount + " cDiscAmount=" + cDiscAmount + " bulkDiscAmount=" + bulkDiscAmount + " price=" + this.price);
    
    var liveBulkDiscountRate = this.getBulkDiscountPercent(); //this will get live because legacy bulk key not set yet
    var baseDiscountRate, fullDiscountRate;
    
    if(d.affiliateDiscounting) {
      baseDiscountRate =  d.round(bulkDiscAmount / (this.basePrice.getOverride()) * 100.0);
      fullDiscountRate =  d.round(bulkDiscAmount / (this.pricing.getOverride()) * 100.0);
    } else {
      baseDiscountRate =  d.round(bulkDiscAmount / (this.basePrice.wsCost) * 100.0);
      fullDiscountRate =  d.round(bulkDiscAmount / (this.pricing.wsCost) * 100.0);
    }
    
    //see if any rate matches...
    
    if(baseDiscountRate == liveBulkDiscountRate) {
      log("setting legacyBulkDiscountToBase = true (" + baseDiscountRate + " == " + liveBulkDiscountRate + ")");
      this.legacyBulkDiscount = baseDiscountRate;
      this.legacyBulkDiscountToBase = true;
    } else if(fullDiscountRate == liveBulkDiscountRate) {
      log("setting legacyBulkDiscountToBase = false (" + fullDiscountRate + " == " + liveBulkDiscountRate + ")");
      this.legacyBulkDiscount = fullDiscountRate;
      this.legacyBulkDiscountToBase = false;
    } else {
      this.legacyBulkDiscountToBase = this.getBulkDiscountToBase(); //default to live..
      if(this.legacyBulkDiscountToBase) {//use correct rate based on live settings
        this.legacyBulkDiscount = baseDiscountRate;
        log("fallback legacyBulkDiscountToBase = true (" + baseDiscountRate + " != " + liveBulkDiscountRate + " && " + fullDiscountRate + " != " + liveBulkDiscountRate + " )");
      } else {
        this.legacyBulkDiscount = fullDiscountRate;
        log("fallback legacyBulkDiscountToBase = false (" + baseDiscountRate + " != " + liveBulkDiscountRate + " && " + fullDiscountRate + " != " + liveBulkDiscountRate + " )");
      }
      
    }
    this.legacyBulkDiscountKey = this.generateBulkDiscountKey();
    
    //can we use the legacy values? (does rounded values match final values?)
    var legacyDiscountRate = d.round((this.legacyDiscount / this.qty) / this.price);
    var legacyDiscountAmount = this.price * legacyDiscountRate * this.qty;
    
    if(legacyDiscountAmount != this.legacyDiscount) {
      log("reverseEngineerDiscountRates: rounding error (" + legacyDiscountAmount + " !=  " + this.legacyDiscount + "): (legacyDiscountRate=" + legacyDiscountRate + ") setting to overridden discount");
      this.discountLocked = true;
      this.discountType = AMOUNT_TYPE_FIXED;
      this.typedOverridingDiscount = this.legacyDiscount;
      this.legacyDiscount = null;
    } else {
      log("reverseEngineerDiscountRates: no rounding error (" + legacyDiscountAmount + " ==  " + this.legacyDiscount + "): setting legacy discount to " + legacyDiscountRate);
      this.legacyDiscount = legacyDiscountRate;
      this.discountType = AMOUNT_TYPE_PERCENT;
    }
  },
  
  getDiscountAmount: function CP_getDiscount() {
    this.resetPricing();
    return this.discount;
  },
  
  getDiscountRate: function CP_getDiscountRate() {
    this.resetPricing();
    return this.discountRate;
  },

  getDiscountId: function CP_getDiscountId() {
    var discountId;
    if(this.product == null) return null;
    
    if(d.appLicMode == LIC_SOLO) {
      discountId = d.defaultDiscountId;
      this.discountToBase = false;
    } else if(this.product.discountId == -1) {
      discountId = this.product.type.discountId;
      this.discountToBase = this.product.type.discountToBase;
    } else {
      discountId = this.product.discountId;
      this.discountToBase = this.product.discountToBase;
    }
    return discountId;
  },
  
  //get the bulk discount percent, using legacy values if we should
  getBulkDiscountPercent: function CP_getBulkDiscountPercent() {
    if(d.useLegacyPrices && this.legacyBulkDiscount != null && this.legacyBulkDiscountKey == this.generateBulkDiscountKey()) {
      return this.legacyBulkDiscount;
    }
    if(this.product == null) return 0.0;
    
    var discountId = this.getDiscountId();
    if(discountId == null) {
       return 0.0;
    } else {
      return d.getDiscountPercent(discountId, this.qty);
    }
  },
  
  //get the bulk discount apply to base setting, using legacy values if we should
  getBulkDiscountToBase: function CP_getBulkDiscountToBase() {
    if(d.useLegacyPrices && this.legacyBulkDiscount != null && this.legacyBulkDiscountToBase != null && this.legacyBulkDiscountKey == this.generateBulkDiscountKey()) {
      return this.legacyBulkDiscountToBase;
    }
    if(this.product == null) return true;
    //not using legacy: find from live data
    if(d.appLicMode == LIC_SOLO) {
      return false;
    } else if(this.product.discountId == -1) {
      return this.product.type.discountToBase;
    } else {
      return this.product.discountToBase;
    }
  },
  
  checkBulkDiscountLegacyChanges: function CP_checkBulkDiscountLegacyChanges() {
    if(d.useLegacyPrices && this.legacyBulkDiscount != null && this.legacyBulkDiscountKey == this.generateBulkDiscountKey()) {
      var calculatedDiscountPercent = 0;
      var discountId = this.getDiscountId();
      if(discountId != null) {
        calculatedDiscountPercent = d.getDiscountPercent(discountId, this.qty);
      }
      if(this.legacyBulkDiscount != calculatedDiscountPercent) {
        log("Overriding the bulk discount from " + calculatedDiscountPercent + " to legacy value " + this.legacyBulkDiscount); 
        d.registerLegacyPrice(new LegacyPriceChange(this.getName() + ":Bulk Discount Rate", -2, this.legacyBulkDiscount, calculatedDiscountPercent));
      }
    }
  },
  
  getCalculatedDiscountRate:function CP_getCalculatedDiscountRate() {
    this.resetPricing();
    return this.calculatedDiscountRate;
  },
  
  getPrice: function CP_getPrice() {
    this.resetPricing();
    return this.finalTotal;
  },
  
  
  //get the line total without the customer discount, and (if !applyDiscount) without the bulk discount
  getRRP: function CP_getRRP(applyDiscount) {
    this.resetPricing();
    if(this.cp_type == CP_DIGITIZATION || this.cp_type == CP_GIFT_CERTIFICATE || this.cp_type == CP_CREDIT) {
      return this.price;
    } else if (this.cp_type == CP_EXTRA_CHARGE || this.cp_type == CP_FREE_FORM) {
      return (this.overridingPrice) ? this.overridingPrice : this.price;
    } else {
      if(applyDiscount) {
        return this.undiscountedLineTotal.getOverride() - this.bulkDiscount; //we want to include the bulk discount in the price
      } else {
        if(this.customerDiscount == null) this.customerDiscount = 0.0;
        return this.undiscountedLineTotal.getOverride() - this.customerDiscount; //we dont want customer discount in price
      }
    }
  },
  
  getCurPrice: function CP_getCurPrice() {
    this.getPrice() * d.cMod;
  },
  
  getCurDiscount: function CP_getCurDiscount() {
    this.discount * d.cMod;
  },
  
  getDigitizingFee: function CP_getDigitizingFee() {
    //get usage counts...(needed by dtg price tables...)
    var dfee = 0.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())) {
        for(var j=0; j < pView.areas.list.size(); j++) {
          var pArea =  pView.areas.list[j];
          var cArea = cView.areas[pArea.id];
          if((cArea != null) && (cArea.isUsed())) {
            for(var k in cArea.allItems) {
              var item = cArea.allItems[k];
              if((item.digitize)&&(item.digitizationFee == null)) {
                item.updateDigitizationCosts();
              }  
              if((item.digitize)&&(item.digitizationFee != null)) {
                dfee += item.digitizationFee;
              }
            }
          }
        }
      } 
    }
    return dfee;
  },
  
  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, skipReprice) {
    
    if(prefix==null) {
      prefix="c[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    if(skipReprice != true) {
      d.setUnitPrices=true; //we need to recalc pricing with this flag set to true to make sure unit/delta prices are set
      this.resetPricing(true);
      d.setUnitPrices=false;
    }
    log(this);
    this.serializingForSave = forSave;
    queryComponents.push(encodeURIComponent(prefix + "[cid]") + "=" + encodeURIComponent(d.cart.id));
    queryComponents.push(encodeURIComponent(prefix + "[p]") + "=" + encodeURIComponent(this.product.id));
    queryComponents.push(encodeURIComponent(prefix + "[q]") + "=" + encodeURIComponent(this.qty));
    queryComponents.push(encodeURIComponent(prefix + "[lv]") + "=" + encodeURIComponent(this.renderVersion));
    queryComponents.push(encodeURIComponent(prefix + "[c]") + "=" + encodeURIComponent(this.getSelectedColorId()));
    queryComponents.push(encodeURIComponent(prefix + "[def_proc]") + "=" + encodeURIComponent(this.defaultProcessId));

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


var LegacyPriceChange = Class.create({
  CLASSDEF: {
      name: 'LegacyPriceChange'
  },
  
  initialize: function LegacyPriceChange_initialize(caption, type, legacyPrice, newPrice) {
    this.id = caption;
    this.caption = caption;
    this.type = type;
    this.legacyPrice = legacyPrice;
    this.newPrice = newPrice;
  },
  
  hasLegacyChanges: function() {
    return true;
  },
  
  getLegacyChanges: function() {
    return [{legacyPrice:this.legacyPrice, livePrice:this.newPrice, obj:this, type:this.type}];
  },
  
  getName: function() {
    return this.caption;
  }
});

var nextConfiguredProductId = -2;

//a place to store selction info about a field.. handles multiselect
var ConfiguredField = Class.create({
  CLASSDEF: {
      name: 'ConfiguredField'
  },
  
  initialize: function CPF_initialize(id, configuredProduct, options) {
    this.id = id;
    this.configuredProduct = configuredProduct;
    this.options = {}; //hash of ConfiguredFieldOption objects
    
    this.usePriceDefaults = options.uDef;
    
    this.multiSelect = false;
    this.multiOption = null;
    this.productField = this.configuredProduct.product.fields.byId[this.id];
    this.fieldDef = this.productField.fieldDef;
    if(this.fieldDef==null) {
      return;
    }
    this.fieldValue = null;
    if(options != null) {
      if(options.val != null) {
        if(this.fieldDef.typeOptions.date) {
          try {
            this.fieldValue = new Date(options.val);
          } catch(e) {
            log("Error parsing date '" + options.val + "' when loading " + this.fieldDef.name);
          }
        } else {
          this.fieldValue = options.val;
        }
      }
    }
    if(options.ud != null) { //legacy price for non-option fields
      this.unitDelta = options.ud;  
    } else {
      this.unitDelta = null;
    }
      
  },
  
  getLegacyDelta: function() {
    if(d.forceLegacyPrices || d.useLegacyPricing) {
      return this.unitDelta;
    } else {
      return null;
    }
  },
  
  
  addSelectedOption: function CPF_addSelectedOption(optionId, qty, subOptionId, unitPrice, isAutoPrice, unitDelta, ignoreDefaultSub, legacyKey) {
    var opt = this.options[optionId];
    var subQty = 0;
    if(subOptionId != null) {
      subQty = qty;
      qty = 1;
    }
    if(opt == null) {
      opt = new ConfiguredFieldOption(this, optionId, qty, unitPrice, isAutoPrice, unitDelta);
      if((opt.def == null)||(opt.pDef == null)) {
        log("Dropping option " + optionId + " as it has no def");
        return null;
      }
      this.options[optionId] = opt;
    } else {
      opt.qty = qty;
      if (unitPrice != null) {
        opt.unitPrice = unitPrice;
        opt.unitDelta = unitDelta;
        opt.isAutoPrice = isAutoPrice;
      }
    }
    if(legacyKey != null) {
      opt.legacyKey = legacyKey;  
    }
    
    if(opt.def.isMulti) {
      this.multiSelect = true;
      this.multiOption = opt;
    } else if(subOptionId != null) {
      opt.addSelectedOption(subOptionId, subQty, unitPrice, isAutoPrice, unitDelta, legacyKey);
    } else {
      //should we select the default sub option?
      if(opt.pDef.subs != null && !ignoreDefaultSub) {
        if(this.fieldDef.typeOptions.singleSelect) {
          opt.initToDefault(true);
        }
      }
      
    }
    return opt;
  },
  
  //used when qty get set to 0 on sub option: remove the option...
  cleanEmptyOptions: function CPF_cleanEmptyOptions() {
    for(var k in this.options) {
      var opt = this.options[k];
      if((opt.qty == 0)||( (opt.pDef.subs != null) && (opt.subQty == 0)) ) {
        delete this.options[k];
      }
    }
  },
  
  initToDefault: function CPF_initToDefault() {
    if(this.fieldDef.typeOptions.list) {
      var unitPrice = null;
      var isAutoPrice = null;
      var unitDelta = null;
      var ignoreDefaultSub = null;
      
      
      
      if(hashFirstElement(this.options) == null && this.configuredProduct.unspecSizeQty == 0) { //no selected option...
        
        if(!this.configuredProduct.isNew()) {
          //we are initializing a default value for an existing product... must be a new field... make legacy price 0
          unitPrice = 0.0;
          isAutoPrice = true;
          unitDelta = 0.0;
        }
        
        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 && !pOpt.discontinued) {
            log("Adding default option " + pOpt.def.name);
            var opt = this.addSelectedOption(pOpt.id, 1, null, unitPrice, isAutoPrice, unitDelta, ignoreDefaultSub);
            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.usesBundles()) {
        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)&&(this.multiOption != null)) {
      return this.multiOption;
    } else {
      for(var k in this.options) {
        return this.options[k]; //just return the first option
      }
      //no option selected... lets add the default
      if (useDefault == true) {
        return this.addSelectedOption(this.productField.defaultOption.id, 1);
        //return this.productField.defaultOption;
      }
      return null;
    }
  },
  
  
  //called when user changes field value from interface
  setValue: function CPF_setValue(value, forOMS) {
    if(this.fieldDef.typeOptions.list) { //the value is an option....
      if(value == null && this.fieldDef.fieldType == FIELD_TYPE_LIST_MULTISELECT) {
        //multi values from the form field
        this.options = {}; //reset as we will reload all from el
        this.multiSelect = false;
        this.multiOption = null;
    
        var el = $(this.iId('value'));
        if(el != null) {
          for(var i=0; i < el.length; i++) {
            if(el.options[i].selected) {
              var ids = this.extractOptIds(el.options[i].value);
              this.addSelectedOption(ids[0], 1, ids[1]);
            }
          }
        }
      } else {
        this.setListValue(value, forOMS);
        if(value.indexOf("-") == -1) { //not a sub option...
          var sizeColorCombinations = null;
          if(this.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE && this.configuredProduct.product.limitSizeColors) {
            sizeColorCombinations = this.configuredProduct.product.sizeColorCombinations;
          }
          if(!forOMS || this.fieldDef.fieldType != FIELD_TYPE_PRODUCT_SIZE) { //size field in oms does not use select boxes...
            $(this.iId('sub_value_container', false)).update(this.fieldDef.renderSubSelect(this.configuredProduct, this.productField, this, sizeColorCombinations, forOMS));
          }
        }
      }
      if(this.fieldDef.fieldType == FIELD_TYPE_PRODUCT_SIZE && this.configuredProduct.product.limitSizeColors) {
        //we need to rerender the color list as the size selection could have changed available colors..
        this.configuredProduct.product.type.buildColorPanel(this.configuredProduct);
      }
    } else if(this.fieldDef.fieldType == FIELD_TYPE_TEXT_BOX || this.fieldDef.fieldType == FIELD_TYPE_TEXT_AREA)  {
      this.fieldValue = value;
    } else if(this.fieldDef.fieldType == FIELD_TYPE_FILE || this.fieldDef.fieldType == FIELD_TYPE_IMAGE)  {
      //check that that file is valid
      var el = $(this.iId('value'));
      if(!this.validFile(el.value)) {
        alert(ml("You can only upload files of type: %s", this.fieldDef.customOption("extensions", '')));
        el.value = "";
        return;
      }
      
      if(!d.allowForms) {
        closePopup('field_upload_form_popup' + this.fieldDef.id);
      }
      //we need to submit the form field after popping up the upload status bar...
    
      checkAsyncActions(); 
      startAsyncAction(); 
      startTrackingUpload("CF"); 
      $("field_upload_form" + this.fieldDef.id).submit();
      popup("field_upload_popup");
      

      
    } else if((this.fieldDef.fieldType == FIELD_TYPE_DATE)||(this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME)) {
      this.fieldValue = this.loadDateFromInputs(true, (this.fieldDef.fieldType == FIELD_TYPE_DATE_TIME));
    } else if(this.fieldDef.fieldType == FIELD_TYPE_TIME) {
      this.fieldValue = this.loadDateFromInputs(false, true);
    }
    
    if (!forOMS) {
      d.track("change-" + this.fieldDef.name);
      this.configuredProduct.product.type.updatePrice();
      d.itemChanged();
    }
  },
  
  loadDateFromInputs: function CPF_loadDateFromInputs(do_date, do_time) {
    var date = new Date();
    var selVal = null;
    try {
      if(do_date) {
        //month
        selVal = $FV(this.iId("date_1"));
        if(selVal == null || selVal == "") return null;
        date.setMonth(selVal);
        //day
        selVal = $FV(this.iId("date_0"));
        if(selVal == null || selVal == "" || isNaN(selVal)) return null;
        var iVal = parseInt(selVal, 10);
        if(iVal < 1 || iVal > 31) return null;
        date.setDate(iVal);
        //year
        selVal = $FV(this.iId("date_2"));
        if(selVal == null || selVal == "") return null;
        date.setFullYear(selVal);
      }
      if(do_time) {
        var timeFormat = parseInt(this.customOption("time_format", '0'), 10);
        //hour
        var timeMod = 0;
        if(timeFormat == 0) {
          timeMod = ($FV(this.iId("date_5")) == "AM")? 0 : 12;
          selVal = $FV(this.iId("date_3"));
        } else {
          selVal = $FV(this.iId("date_6"));
        }
        if(selVal == null || selVal == "" || isNaN(selVal)) return null;
        var iVal = parseInt(selVal, 10);
        if(iVal < 0 || iVal > 23) return null;
        date.setHours(iVal + timeMod);
        
        //minute
        selVal = $FV(this.iId("date_4"));
        if(selVal == null || selVal == "" || isNaN(selVal)) return null;
        var iVal = parseInt(selVal, 10);
        if(iVal < 0 || iVal > 59) return null;
        date.setMinutes(iVal);
      }
      
    } catch(e) {
      log("Exception when loading date:" + e.getMessage());
      return null;
    }
    return date;
  },
  
  validFile: function CPF_validFile(filename) {
    var ext = this.fieldDef.customOption("extensions", null);
    if((ext == null)||(ext.length==0)) return true;
    var exts = ext.split(' ');
    if(exts.length == 0) return true;
    var cleanExt = [];
    for(var i=0; i < exts.length; i++) {
      var mExt = exts[i].split(",");
      for(var j=0; j < mExt.length; j++) {
        var cExt = mExt[j];
        if(cExt != '') {
          if(cExt[0] == '.') {
            cExt = cExt.substr(1, cExt.length);
          }
          cleanExt.push(cExt);
        }
      }
    }
    for(var i=0; i < cleanExt.length; i++) {
      var ext = cleanExt[i];
      var extLength = ext.length;
      if((filename.length > extLength + 1)&&(filename[filename.length - extLength - 1] == '.')) {
        var fileTest = filename.substr(filename.length - extLength, extLength);
        if(fileTest.toLowerCase() == ext.toLowerCase()) {
          return true;
        }
      }
    }
    return false;
  },
  
  uploadFinished: function CPF_uploadFinished(fileData) {
    closePopup("field_upload_popup");
    if(fileData.error != null) {
      alert(fileData.error);
      return;
    }
    this.fieldValue = fileData;
    this.fieldValue.isWorkingVersion = true;
    this.fieldDef.setField(this.configuredProduct); //rerender the field so it now shows the uploaded file...

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

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


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

var ConfiguredFieldSubOption = Class.create({
  CLASSDEF: {
      name: 'ConfiguredFieldSubOption'
  },
  
  initialize: function CPFSO_initialize(option, subOptionId, qty, unitPrice, isAutoPrice, unitDelta) {
    this.option = option;
    this.field = option.field;
    this.subOptionId = subOptionId;
    this.qty = qty;
    this.unitPrice = unitPrice;
    this.unitDelta = unitDelta;
    this.isAutoPrice = (typeof(isAutoPrice) === 'undefined' || isAutoPrice === null) ? true : isAutoPrice;
    this.legacyKey = null;
    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));
    if(this.unitPrice != null) queryComponents.push(encodeURIComponent(prefix + "[up]") + "=" + encodeURIComponent(this.unitPrice));
    if(this.unitDelta != null) queryComponents.push(encodeURIComponent(prefix + "[ud]") + "=" + encodeURIComponent(this.unitDelta));
    queryComponents.push(encodeURIComponent(prefix + "[iap]") + "=" + encodeURIComponent(this.isAutoPrice));
  },
  
  generatePriceKey: function() {
    return null;
  },
  
  getLegacyDelta: function(baseCrp, legacyKey) {
    if((this.unitPrice == null)&&(this.unitDelta == null)) return null;
    if(this.isAutoPrice) {
      if(this.unitDelta == null) {
        this.unitDelta = this.unitPrice - baseCrp;
      }
      if(d.forceLegacyPrices || (d.useLegacyPrices && legacyKey == this.legacyKey)) {
        return d.round(this.unitDelta);
      } else {
        return null;
      }
    } else {
      return null;
    }
  },
  
  getOverrideDelta: function(baseCrp) {
    if((this.unitPrice == null)&&(this.unitDelta == null)) return null;
    if(!this.isAutoPrice) {
      if(this.unitDelta == null) {
        this.unitDelta = this.unitPrice - baseCrp;
      }
      return d.round(this.unitDelta);
    } else {
      return null;
    }
  },
  
  getName: function() {
    return this.option.getName() + "/" + this.def.name;
  }
});


//represents the used view... contains a list of LayoutManagers for each area...
var ConfiguredView = Class.create({
  CLASSDEF: {
      name: 'ConfiguredView'
  },
  
  initialize: function CPV_initialize(configuredProduct, productView, options) {
    this.configuredProduct = configuredProduct;
    this.productView = productView;
    this.id = (options.id == null) ? productView.id : options.id;
    this.startUrl = options.u;
    this.isTempView = (options.isTempView == true);
    
    
    this.dirty = false;
    this.renderVersion = (options.rv == null) ? 0 : options.rv;
    this.loadedRenderVersion = (options.lrv == null) ? this.renderVersion : options.lrv;
    this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
    this.reRenderChild = false; //track if children have made changes requiring rerendering
    
    this.selectedArea = null;
    
    this.areas = {};
    
    this.nextZIndex =2;
    
    this.viewBackgroundClickedEvent = this.viewBackgroundClicked.bindAsEventListener(this);
    this.desPanel = null; //the designers panel view where areas are placed inside of...... 
    this.desBgImage = null; //the designers background image for this view... 
    this.initBgPanel();
    
    
    var areas = options.a;
    if(areas!=null) {
      for(var i=0; i < areas.length; i++) {
        var area = areas[i];
        var lm = this.getArea(area.id, true);
        if(lm != null) {
          lm.loadItems(area.i);
          if (area.p) lm.updateProcesses(area.p); // update overriding prices
          area.c.rv = area.rv; //put the render version into the configuration..
          lm.loadConfiguration(area.c);
        }
      }
    }
  },
  
  toString: function() {
    return this.configuredProduct.toString() + ":" + this.productView.name;
  },
  
  initBgPanel: function CPV_initBgPanel() {
    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...
      var w = d.designPane.readAttribute('image_width');
      var h = d.designPane.readAttribute('image_height');
      if((w != null)&&(h!=null)&&(w < 400 || h < 400)) {
        this.desBgImage.style.width = w + 'px';
        this.desBgImage.style.height = h + 'px';
      } else {
        this.desBgImage.style.width="400px";
        this.desBgImage.style.height="400px";
      }
      
      this.checkBgImage();
      
     // this.setBgPosition();
      this.desPanel.appendChild(this.desBgImage);
     
    } else {
       //this is the load image.. its correct (in therory)..
      this.currentBGImageUrl = this.desBgImage.getAttribute("bg-image-url");
      this.checkBgImage();
      //this.currentBGImageUrl = this.getDesignerBackgroundURL();
    }
    log("initBgPanel:" + this.toString());
    Event.observe(this.desBgImage, "mousedown", this.viewBackgroundClickedEvent);
    
  },
  
  getArea: function CPV_getArea(areaId, createIfNotFound) {
    var area = this.areas[areaId];
    if((area == null) && (createIfNotFound == true)) {
      var productArea = this.productView.areas.byId[areaId];
      if(productArea != null) {
        area = new ConfiguredViewArea(this, productArea);
        this.areas[areaId] = area;
        //area.updateOverlay(); MAP: DNC-910: removed because getArea call would update overlay with incorrect color.. why is this needed?
      }
    }
    return area;
  },
  
  //allowStartUrl: a view can have a start url when loading existing configured items/custom products
  //allowScale: should we lookup and layout modifiers to see if the scale changes...
  getViewURL: function CPV_getViewURL(size, allowStartUrl, allowScale, state) {
    
    //get the selected color id
    var colorId =  this.configuredProduct.getSelectedColorId();
    //var colorId = (color==null) ? 0 : color.productChosenOptionId;
    var scale = "1";
    //get the size layout modifier...
    if(allowScale) {
      var mods = this.configuredProduct.getLayoutModifiers();
      scale = mods.s;
      scale = new String(scale).replace(".", "_");
    }
    if(size != 1) { //its not for the designer background...
      if((d.inSimpleMode(TEMPLATE_MODE_DESIGN))&&(this.isUsed())&&(!this.hasChildRenderVersion())) {
        //get the area process used...
        var areaId = this.getFirstAreaId();
        var area = this.getArea(areaId, true);
        var process = area.findFirstProcess();
        var designId = area.getUsedTemplateAssetIds()[0];
        
        //return "/image_gen/design_product?design_id=" + designId + "&aprocess_id=" + process.productProcess.spid + "&px_size=75&pcolor=" + colorId;
        
        return "/dpimages/" + designId + "/0/0/0/" + process.productProcess.spid + "/0/" + colorId + "/100/0/prod.jpg"
      }
    }
    if((d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)||((allowStartUrl==true)&&(!this.hasChildRenderVersion()))) {
      if(this.configuredProduct.usingCustomProduct()) {
        var url = this.configuredProduct.customProduct.getViewURL(this.id, size, colorId, scale);
        if(url != null) {
          return url;
        }
      }
    }
    if((allowStartUrl==true)&&(!this.isModified())&&(this.startUrl!=null)&&(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      var url = this.startUrl.replace(d.layoutViewUrlCID, colorId);
      url = url.replace(d.layoutViewUrlS, size );
      return url.replace(d.layoutViewUrlSc, scale );
    } else if(allowStartUrl && this.isModified()) {
      if(this.needsReRendering() || this.viewSrc == null) {
        if(state != null) {
          state.needServer = true;  
        }
      } else {
        log("getViewURL: Using viewSrc: " + this.viewSrc);
        return this.viewSrc;
      }
    }
    return this.productView.getViewURL(size, colorId, scale);
    
  },
  
  getDesignerBackgroundURL: function CPV_getDesignerBackgroundURL() {
    var allowStartUrl = (d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT);
    return this.getViewURL(1, allowStartUrl, true);
  },
  
  //make sure the background image is correct for the current color/product
  //will also create the image element if not set...
  checkBgImage: function CPV_checkBgImage() {
    if(this.isTempView) return;
    var url = this.getDesignerBackgroundURL();
    if(this.currentBGImageUrl != url) {
      setImageUrl(d.designPane, this.desBgImage, url);
      this.desBgImage.setAttribute("bg-image-url", url);
      this.currentBGImageUrl = url;
    }
  },
  
  //used to set the postion of the background image without transition effect
  setBgPosition: function() {
    if(this.isTempView) return;
    if((d.currentCanvasType==0)||(!this.selectedArea.canZoom())) { //LAYOUT
      this.desPanel.style.top="0px";
      this.desPanel.style.left="0px";
      var w = d.designPane.readAttribute('image_width');
      var h = d.designPane.readAttribute('image_height');
      if((w != null)&&(h!=null)&&(w < 400 || h < 400)) {
        this.desBgImage.style.width = w + 'px';
        this.desBgImage.style.height = h + 'px';
        this.bgWidth = w;
        this.bgHeight = h;
      } else {
        this.desBgImage.style.width="400px";
        this.desBgImage.style.height="400px";
        this.bgWidth = 400;
        this.bgHeight = 400;
      }
      this.bgLeft = 0;
      this.bgTop = 0;
      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;
    var reScale = this.selectedArea.productArea.reScale;
    
    log("transitionToArea: left=" + left + ", top=" + top + ", width=" + width + ", height=" +height + ", reScale=" + reScale);
    
    if(d.mode == DESIGNER_MODE_TEMPLATE) {
      //lets try and zoom to just the design location...
      var bbScale = this.selectedArea.getDesignZoomScale();
      log("getDesignZoomScale"); 
      log(bbScale);
      left = bbScale.l;
      top = bbScale.t;
      width = bbScale.w;
      height = bbScale.h;
      reScale = bbScale.reScale;
    } 
    
    //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, reScale);
    this.setCanvasZoomStyle(false);
  },
  
  setCanvasZoomStyle: function(zoom) {
    for(var k in this.areas) {
      this.areas[k].setCanvasZoomStyle(zoom);  
    }
  },
  
  transitionTo: function(left, top, rescale) {
    log("transitionTo(" + 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)&&(cArea.isUsed())) { //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 null;
    }
    return fallbackArea.id;//no areas used.. get the first one...
  },
  
  isUsed: function CPV_isUsed(processId) {
    return (this.findFirstUsedArea(processId) != null);
  },
  
  findFirstUsedArea: function(processId) {
    for(var k in this.areas) {
      var area = this.areas[k];
      if(area.isUsed(processId)) {
        return area;
      }
    }
    return null;
  },
  
  //when in template mode we show all panels always
  showPanels: function() {
    //this.prepareAreas();
    //we show all areas at once....
    for(var k in this.areas) {
      var area =  this.areas[k];
      if(area.isUsed()) {
        //area.showManagementPanels();
        area.show(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(!d.inSimpleMode(TEMPLATE_MODE_CP)) {
      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);
    }
    if(!doItems) this.setCanvasZoomStyle((d.currentCanvasType==0));
  },
  
  //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);
      this.setCanvasZoomStyle(true);
    } else {
      //this.prepareAreas(false); //as only the 
      //this.selectedArea.prepareCanvas(true); //we want to scale the selected areas items correctly...
      this.transitionToArea(this.selectedArea);
      this.setCanvasZoomStyle(false);
    }
    
  },
  
  getNextZIndex: function CPV_getNextZIndex() {
    return this.nextZIndex+=5;
  },
  
  //stop hammering the server by queuing updates..
  queueViewUpdate: function CPV_queueViewUpdate(timeout) {
    if(timeout == null) timeout = 1000;
    if(this.vuto != null) {
      window.clearTimeout(this.vuto);
    }
    if((!this.isUsed())||(this.configuredProduct.isNonDecProduct())) {
      
      if(this.configuredProduct.product.hasDerivedViews) {
        //need to blank all the views..
        log("queueViewUpdate:!this.isUsed():hasDerivedViews");
        this.configuredProduct.updateThumbnails();
      } else {
        var url = this.getViewURL(d.getThumbnailSize(), 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.clientId, 3, {
            subKey: "view_" + self.id,
            mode: 0,
            urlFunction: function() { return d.pathPrefix + "/designer/save_product?preview=1&lid=" + self.id + "&lcpid=" + self.configuredProduct.id} ,
            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)});
      //}
    }, timeout);
  },
  
  //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(d.getThumbnailSize(), 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)) {
        if((d.mode != DESIGNER_MODE_TEMPLATE) || (this.getArea(area.id) != null && this.getArea(area.id).isUsed())) {
          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();
    }
  },

  hasChildRenderVersion: function() {
    for(var k in this.areas) {
      var area = this.areas[k];
      if(area.isUsed()) {
        if(area.renderVersion > 0) {
          return true;  
        }
      }
      return false;
    }
  },
  
  //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(moveResult < mR) {
          moveResult = mR;
        }
        hasMatchedArea = true;
        oldAreas[i] = null;
      } else {
        log("Old Area is null");
      }
    }
    if((moveResult == 1)&&(d.inSimpleMode(TEMPLATE_MODE_DESIGN))) { //nothing matched... get best match....
      moveResult = this.moveTemplateToView(oldView, testOnly);
      if(moveResult==-1) moveResult = 0; //there was nothing to move.... thats a 'pass'
    }
    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...
  },
  
  moveTemplateToView: function CPV_moveTemplateToView(oldView, testOnly) {
    //there should only be one area used... find it...
    var oldArea = oldView.findFirstUsedArea(null);
    if(oldArea == null) {
      log("No used areas");
      return -1;
    }
    //find the first target area that supports the processes used in old area
    var procUsageData = oldArea.getProcessUsage();
    log(procUsageData);
    
    var score = this.productView.getProcessMatchScore(procUsageData);
    
    if(score.area == null) { //cant move anything... dont!
      log("No bestArea");
      return -2;
    }
    log("moving to " + score.area.name + " with score of " + score.score);
    //move to target area
    var destArea = this.getArea(score.area.id, true);
    return destArea.moveElements(oldArea, testOnly);
  },
  
  getProcessUsage: function() {
    var area = this.findFirstUsedArea(null);
    if(area == null) {
      log("No used areas");
      return null;
    }
    return area.getProcessUsage();
  },
  
  
  validate: function CPV_validate() {
    for(var k in this.areas) {
      this.areas[k].validate();
    }
  },
  
  resetLegacyOption: function() {
    for(var k in this.areas) {
      this.areas[k].resetLegacyOption();
    }
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns total this views costs to decorate
  calculatePrice: function CPV_calculatePrice(colorType, priceData, percentMarkup, pricingState, forProductViewAreaProcess) {
    var viewTotal = priceZero();
    var viewPriceData = {
      id: this.productView.id,
      type: 1,
      view: this,
      areas: new MapList()
    };
    for(var j=0; j < this.productView.areas.list.size(); j++) {
      var pArea =  this.productView.areas.list[j];
      if(forProductViewAreaProcess != null) {
        if(pArea.id == forProductViewAreaProcess.viewArea.id) {
          var cArea = this.getArea(forProductViewAreaProcess.viewArea.id, true);
          viewTotal = cArea.calculatePrice(colorType, priceData, viewPriceData, percentMarkup, pricingState,forProductViewAreaProcess);
          break;
        }
      } else {
        var cArea = this.areas[pArea.id];
        if((cArea != null) && (cArea.isUsed())) {
          viewTotal = viewTotal.add(cArea.calculatePrice(colorType, priceData, viewPriceData, percentMarkup, pricingState));
        }
      }
    }
    if(viewTotal.cost > 0 || viewTotal.isOverriden) {
      viewPriceData.total = viewTotal;// * percentMarkup;
      priceData.views.add(viewPriceData);
    }
    return viewTotal;
  },
  
  serialize: function CPV_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix="v[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[rv]") + "=" + encodeURIComponent(this.renderVersion));
    for(var k in this.areas) {
      area = this.areas[k];
      if(area.isUsed()) {
        var p = prefix + "[a][" + area.id + "]";
        area.serialize(queryComponents, p);
      }
    }
    return queryComponents.join('&');
  },
  
  setErrors: function CPV_setErrors(errors) {
    if(errors==null) errors = {};
    for(var k in this.areas) {
      area = this.areas[k];
      if(area.isUsed()) {
        area.setErrors(errors.areas[area.id]);
      }
    }
  },
  
  onColorChange: function CPV_onColorChange(color) {
    for(var k in this.areas) {
      area = this.areas[k];
      if(area.isUsed()) {
        area.onColorChange(color);
      }
    }
  }
});

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.multiSelection = null;
    
    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";
      if(d.mode!=DESIGNER_MODE_TEMPLATE) {
        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);
      //if(d.mode!=DESIGNER_MODE_TEMPLATE) {
        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);
        //if(d.mode!=DESIGNER_MODE_TEMPLATE) {
          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";
        if(d.mode!=DESIGNER_MODE_TEMPLATE) {
          this.canvas2.style.border="solid 1px black";
        }
        this.canvas2.style.overflow ="hidden";
        this.canvas2.style.display = "none";
        this.canvas2.style.zIndex = this.zIndex-1;
        this.configuredView.desPanel.appendChild(this.canvas2);
        this.canvas2 = $(this.canvas2);
      }
      //this.vCanvas = Raphael(this.canvas, 400, 400); TODO: change to VML/SVG rendering...
      
    }
  },
  
  toString: function() {
    return this.configuredView.toString() + ":" + this.productArea.name;
  },
  
  getName: function() {
    return this.toString();  
  },
  
  //load from server through hashmap
  loadItems: function CPVA_loadItems(itemOptions) {
    for(var i=0; i < itemOptions.length; i++) {
      var itemData = itemOptions[i];
      var itemConfig = itemData.c;
    
      itemConfig.rv = itemData.rv; //put the render version into the config map
      var item = null;
      
      var id = (itemData.id == null) ? this.configuredProduct.getNextItemId() : this.configuredProduct.registerServerItemId(itemData.id);
      var processId = itemData.p;
      
      var cProcess = this.processes[processId];
      var itemType = parseInt(itemConfig.it, 10); 
      if(cProcess != null) {//the process may have been removed ..
        if(itemType == 0) {
          var asset = d.assets[parseInt(itemConfig.aid, 10)];
          if(asset == null) {
            alert("ERROR: An image is missing from " + this.getName());
            item = null;
          } else {
            item = this.buildItemFromAsset(id, asset,  cProcess, itemConfig);
          }
        } else if(itemType == 1) {
          item = this.buildItemFromAsset(id, new TextAsset({}),  cProcess, itemConfig);
        } else if(itemType == 2) {
          item = this.buildItemFromAsset(id, new TeamNameAsset({}),  cProcess, itemConfig);
        } else if(itemType == 3) { //placeholder
          item = this.buildItemFromAsset(id, new PlaceholderAsset({}),  cProcess, itemConfig);
        } else if(itemType == 4) { //template text
          item = this.buildItemFromAsset(id, d.assets[parseInt(itemConfig.aid, 10)],  cProcess, itemConfig);
        } else if(itemType == 6) {
          item = this.buildItemFromAsset(id, new TeamNameAsset({}),  cProcess, itemConfig);
        }
        if(item != null) {
          cProcess.addItem(item);
        }
      }
    }  
  },
  
  buildItemFromAsset: function(id, asset, process,  itemConfig) {
    var itemType = asset.getItemType();
    if(itemType==0) { //normal image
      return new ImageItem(id, process, asset, itemConfig);
    } else if(itemType==1) { //text
      return new TextItem(id, process, asset, itemConfig);
    } else if(itemType==2) { //old team name
      return new TeamItemTplt(id, process, asset, itemConfig);
    } else if(itemType==3) { //placeholder
      return new PlaceholderItem(id, process, asset, itemConfig);
    } else if(itemType==4) { //text
      return new TemplateTextItem(id, process, asset, itemConfig);
    } else if(itemType==6) { //team name
      return new TeamItem(id, process, asset, itemConfig);
    }
    
  },
  
  isUsed: function CPVA_isUsed(processId) {
    if((this.bgColor != null)&&(this.bgColor.type != "transparent")) {
      return true;
    }

    if(processId == null) {
      for(var k in this.processes) {
        if(this.processes[k].isUsed()) {
          return true;
        }
      }
      return false;
    } else {
      var cProcess = this.processes[processId];
      if((cProcess == null)||(!cProcess.isUsed())) return false;
      return true;
    }
  },
  
  registerItem: function CPVA_registerItem(item) {
    this.allItems[item.id] = item;
    this.itemCount ++;
    if (item.itemVisible()) this.visibleItemCount ++;
  },
  
  deRegisterItem: function CPVA_deRegisterItem(item) {
    this.itemCount --;
    if (item.itemVisible()) this.visibleItemCount --;
    delete this.allItems[item.id];
  },
    
  //called once the items have been added 
  loadConfiguration: function CPVA_loadConfiguration(configuration) {
    this.bgColor = new PikiColor(configuration.bg); //Color;
    this.layoutScale = 1;
    if(configuration.rv != null) {
      this.renderVersion = configuration.rv;
    }
  },
  
  //init the ConfiguredViewProcesses
  initProcesses: function CPVA_initProcesses() {
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      this.managePane = $(document.createElement("DIV"));
      this.managePane.id = "mp_" + d.getNextId();
      this.managePane.hide();
      d.managePaneContainer.appendChild(this.managePane);
      //get the list of available processes
    }
    this.processes = {};
    for(var i=0; i < this.productArea.processes.list.size(); i++) {
      var process = this.productArea.processes.list[i];
      this.processes[process.id] = new ConfiguredViewProcess(this, process);
    }
    
  },
  
  updateProcesses: function CPVA_updateProcesses(processOptions) {
    var self = this;
    processOptions.each(function(p) {
        cProcess = self.processes[p.p];
        if (cProcess) {
          cProcess.usingOverridingDec = p.uod;
          cProcess.usingOverridingClipart = p.uoc;
          cProcess.overridingDecorationPrice = p.odp;
          cProcess.overridingClipartPrice = p.ocp;
          cProcess.usingOverridingStitchCount = p.uosc;
          cProcess.overridingStitchCount = p.osc;
          cProcess.colorCount = p.cc;
          cProcess.colorNames = (p.cs == null) ? [] : p.cs;
          cProcess.updateManagePaneSettings();
        }
    });
  },
  
  //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(d.mode!=DESIGNER_MODE_TEMPLATE) {
      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";
        }
      }
    }
  },
  
  setCanvasZoomStyle: function(zoom) {
    if(d.mode==DESIGNER_MODE_TEMPLATE) {
      if(zoom) {
        this.canvas.className = "zoomable";
        if(this.canvas2 != null) {
          this.canvas2.className = "zoomable";
        }
        if(this.overlay != null) {
          this.overlay.className = "zoomable";
        }
      } else {
        this.canvas.className = "";
        if(this.canvas2 != null) {
          this.canvas2.className = "";
        }
        if(this.overlay != null) {
          this.overlay.className = "";
        }
      }
    }
  },
  
  //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()");
    if(d.mode!=DESIGNER_MODE_TEMPLATE) {
      for(var k in this.processes) {
        this.processes[k].hidePanel();
      }
    }
  },
  
  resetLegacyOption: function() {
    this.allowedProcesses = null;
    this.updateAllowedProcesses();
    for(var k in this.processes) {
      this.processes[k].resetLegacyOption();
    }
  },
  
  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);
      }
    }
    if(d.useLegacyPrices) { //check that allow processes cover processes used...
      for(var k in this.processes) {
        var cProcess = this.processes[k];
        if(cProcess.isUsed()) {
          if(this.allowedProcesses[cProcess.id] != true) {
            //add back in the process....
            this.allowedProcesses[cProcess.id] = true;
            d.registerLegacyPrice(new LegacyPriceChange(ml("%s is no longer available", [cProcess.getName()]), -4, "On", "Off"));
          }
        }
      }
    }
    return this.allowedProcesses;
  },
  
  //get the allowed item types for all allowed processes on this area
  getAllAllowedItemTypes: function CPVA_getAllAllowedItemTypes() {
    var allowedProcs = this.getAllowedProcesses();
    var allowed = {
      image: { count:0, single:null},
      text: { count:0, single:null},
      teamname: { count:0, single:null}
    };
    for(var i=0; i < this.productArea.processes.list.length; i++) {
      var pvap = this.productArea.processes.list[i];
      if(allowedProcs[pvap.id]) { //can be used on this area
        var proc = pvap.process;
        if(proc.allowImages) {
          allowed.image.count ++;
          allowed.image.single = pvap.id;
        }
        if(proc.allowText) {
          allowed.text.count ++;
          allowed.text.single = pvap.id;
        }
        if(proc.allowTeamNames) {
          allowed.teamname.count ++;
          allowed.teamname.single = pvap.id;
        }
      }
    }
    return allowed;
  },
  
  //get allowed processes based on what processes are currently used
  getAllowedProcesses: function CPVA_getAllowedProcesses() {
    if(this.allowedProcesses==null) {
      this.updateAllowedProcesses();
    }
    return this.allowedProcesses;
  },
  
  addNewItem: function CPVA_addNewItem(processId, asset, initData, fromOMS) {
    if(processId == null) { //find a matching process...
      for(var i=0; i < asset.processes.length;i++) {
        if(this.processes[asset.processes[i]]!=null) {
          processId = asset.processes[i];
          break;
        }
      }
      if(processId == null) {
        alert(ml("Unable to add image to product as it does not support the decoration process used by the image"));
        return;
      }
    }
    
    
    var process = this.processes[processId];
    var itemType = asset.getItemType();
    var newItem = null;
    newItem = this.buildItemFromAsset(this.configuredProduct.getNextItemId(), asset,  process, initData);
    return this.insertNewItem(newItem, fromOMS);
  },
  
  insertNewItem: function CPVA_insertNewItem(newItem, inBackground) {
    var wasUsing = newItem.cViewProcess.isUsed();
    newItem.cViewProcess.addNewItem(newItem);
    if(inBackground != true) {
      this.showManagementPanels();
      if(!wasUsing) {
        //we are using a process for the first time.. lets update what process we can now use..
        this.updateAllowedProcesses();
      }
      this.setReRender(true);
      d.checkCopyPasteState();
    }
    return newItem;
  },
  
  pasteItems: function CPVA_pasteItems(items) {
    for(var i=0; i < items.length; i++) {
      var newItem = items[i];
      var process = this.processes[processId];
      
    }
  },
  
  mouseDown: function CPVA_mouseDown(event) {
    log("mouseDown:" + this.toString());
    if(!this.selected) {
      if(event == null) {
        log("area isnt selected..event is null..not passing through....");
        return false;
      }
      var pointer = this.configuredView.getLayoutMousePosition(event);
      log("area isnt selected..passing through....");
      this.configuredView.proxyMouseDown(pointer[0],pointer[1], event);
      return false;
    } else {
      if(event == null) {
        log("area is selected..event is null..not selectAreaFromMouse....");
        return false;
      }
      var pointer = this.configuredView.getLayoutMousePosition(event);
      //click on area that did not hit an item...
      
      this.configuredView.selectAreaFromMouse(pointer[0],pointer[1],event);
    }
  },
  
  overlayMouseDown: function CPVA_overlayMouseDown(event) {
    log("overlayMouseDown:" + this.toString());
    if(!this.selected) {
      log("Area No Selected: Proxy through mouseDown");
      return this.mouseDown(event);
    }
    var pointer = this.configuredView.getLayoutMousePosition(event);
    //var dims = this.rel({l:pointer[0], t: pointer[1]});
    
    var i = this.hitTest(pointer[0], pointer[1]);
    if(i != null) {
      log("hit test found item");
      i.select();
      i.dragable.initDrag(event);
      Event.stop(event);
    } else {
      this.configuredView.selectAreaFromMouse(pointer[0],pointer[1],event);
    }
    return false;
  },
  
  removeNewItem: function CPVA_removeNewItem(item) {
    this.items[this.items.indexOf(item)] = null;
    this.items = this.items.compact();
    this.itemsById[item.id] = null;
    this.showManagementPanels();
    d.checkCopyPasteState();
  },
  
  tbTransformed: function CPVA_tbTransformed(options) {
    var item = this.allItems[options.id];
    if(item!=null) {
      item.tbTransformed(options);
    } else {
      log("tbTransformed: unable to get item " + options.id);
    }
  },
  
  setBgColor: function CPVA_setBgColor() {
    if(this.productArea.canSetBgColor) {
      if((this.bgColor == null) || (this.bgColor.type == "transparent")) {
        this.canvas.style.backgroundColor = "";
      } else {
        this.canvas.style.backgroundColor = this.bgColor.htmlColor();
      }
    } 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};
    //}
  },
  
  //get the bounding box of the elements in this area in designer/layout scale px
  getDesignerBoundingBox: function(useLayoutScale) {
    var l = t = 99999999;
    var r = b = -1;
    for(var k in this.allItems) {
      var dims = this.allItems[k].getCanvasDims(useLayoutScale);
      if(dims.t < t) t = dims.t;
      if(dims.l < l) l = dims.l;
      if(dims.w + dims.l > r) r = dims.w + dims.l;
      if(dims.h + dims.t > b) b = dims.h + dims.t;
    }
    
    return { t:t, l: l, w: (r - l), h: (b-t)};
    
  },
  
  //get zoom data required to zoom to the bounding box of the designs in this area
  getDesignZoomScale: function() {
    var bb = this.getDesignerBoundingBox(true);
    
    log("getDesignerBoundingBox");
    log(bb);
    
    
    if(bb.h > bb.w) {
      bb.reScale = 380.0 / parseFloat(bb.h);
    } else {
      bb.reScale = 380.0 / parseFloat(bb.w);
    }
    
    var dH = bb.h * bb.reScale;
    var dW = bb.w * bb.reScale;
    var dL = (400.0 - dW)/2.0;
    var dT = (400.0 - dH)/2.0;
    
    var left = 0 - (bb.reScale * (this.productArea.l + bb.l))  + dL ;
    var top = 0 - (bb.reScale * (this.productArea.t + bb.t))  + dT;
    var width = bb.reScale * 380.0 + dL*2.0;
    var height = bb.reScale * 380.0 + dT*2.0;
    
    return { l: left, t:top, w:width, h:height, reScale: bb.reScale};
    
  },
  
  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();
    if(d.mode!=DESIGNER_MODE_TEMPLATE) {
      
      var bgcDiv = $("canvas_bg_color");
      if(this.productArea.canSetBgColor) {
        var button = $("bg_color_button");
        if((this.bgColor==null)||(this.bgColor.type=="transparent")) {
          button.style.backgroundColor = "";
          button.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
        } else {
          button.style.backgroundColor = this.bgColor.htmlColor();
          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();
    }
    if(d.mode!=DESIGNER_MODE_TEMPLATE) {
      this.managePane.hide();
    }
    if(this.selectedItem) {
      this.selectedItem.deSelect();
    }
    if(this.overlay!= null) {
      this.overlay.hide();
    }
  },
  
  selectItem: function CPVA_selectItem(item) {
    if(this.multiSelection != null) {
      this.multiSelection.cleanup();
      this.multiSelection = null;
    }
    if(this.selectedItem) {
      this.selectedItem.deSelect();
    } else {
      log("Area.selectItem: no existing selection");
    }
    this.selectedItem = item;
    d.checkCopyPasteState();
  },
  
  findAndSelectItem: function CPVA_findAndSelectItem() {
    var proc = this.findFirstProcess();

    

    if((proc == null) || (!proc.hasVisibleItems())) {
      return false;
    }
    log("findAndSelectItem");
    proc.visibleItems.list[0].select();
    return true;
  },
  
  findFirstProcess: function() {
    for(var k in this.processes) {
      if(this.processes[k].isUsed()) {
        return this.processes[k];
      }
    }
    return null;
  },
  
  getProcessUsage: function() {
    var procsUsed = {};
    for(var k in this.processes) {
      if(this.processes[k].isUsed()) {
        procsUsed[k] = this.processes[k].items.list.length;
      }
    }
    return procsUsed;
  },
  
  multiSelectItem: function CPVA_multiSelectItem(item) {
    if(this.multiSelection == null) {
      this.multiSelection = new MultiSelect(this);
    }
    if(this.selectedItem) {
      this.selectedItem.deSelect();
      this.multiSelection.addItem(this.selectedItem);
      this.selectedItem = null;
    }
    this.multiSelection.addItem(item);
    this.multiSelection.showSelection();
  },
  
  checkNoOptions: function CPVA_checkNoOptions() {
    if(d.mode==DESIGNER_MODE_TEMPLATE) return;
    log("checkNoOptions.itemCount:" + this.visibleItemCount);
    if(this.visibleItemCount == 0) {
      $("no_items").show();
      $("has_items").hide();
    } else {
      $("no_items").hide();
      $("has_items").show();
    }
  },
  
  getUsedTemplateAssetIds: function() {
    var foundIdsHash = {};
    for(var k in this.allItems) {
      if(this.allItems[k].templateAssetId != null) {
        foundIdsHash[this.allItems[k].templateAssetId] = true;
      }
    }
    var foundIds = [];
    for(var k in foundIdsHash) {
       foundIds.push(k);
    }
    return foundIds;
  },
  
  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) {
      queryComponents.push(encodeURIComponent(prefix + "[bg]") + "=transparent");
    } else {
      queryComponents.push(encodeURIComponent(prefix + "[bg]") + "=" + this.bgColor.serialize());
    }
    
    for(var k in this.processes) {
      var item = this.processes[k];
      if(item.needToSerialize()) {
        var p = prefix + "[p][" + k + "]";
        item.serialize(queryComponents, p);
      }
    }
    
    for(var k in this.allItems) {
      var item = this.allItems[k];
      var p = prefix + "[i][" + item.id + "]";
      item.serialize(queryComponents, p);
    }
    return queryComponents.join('&');
  },
  
  remove: function CPVA_remove() {
    for(var k in this.processes) {
      this.processes[k].remove();
    }
    
    if(this.canvas != null) {
      Event.stopObserving(this.canvas, "mousedown", this.eventMouseDown);
    }
    if(this.overlay != null) {
      Event.stopObserving(this.overlay, "mousedown", this.overlayMouseDownEvent);
    }
    
    if(this.canvas != null) {
      this.canvas.parentNode.removeChild(this.canvas);
    }
    if(this.canvas2 != null) {
      this.canvas2.parentNode.removeChild(this.canvas2);
    }
  },
  

  
  hitTest: function CPVA_hitTest(x,y, avoidMultiSelected) {
    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(avoidMultiSelected && bestItem != null && bestItem.multiSelected && !this.allItems[k].multiSelected) {
          bestItem = this.allItems[k];
          bestZIndex = bestItem.zIndex;
        } 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);
    }
    var all_ok = true;
    var any_ok = false;
    //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);
    
    /*if(d.mode == DESIGNER_MODE_TEMPLATE) {
      //we know the aspect size... we want it as big as possble within this product area...
      var nd = new NoAsset().scaleDims(sDims.height,sDims.width,this.productArea.dH,this.productArea.dW);
      sDims = {
        width: nd.w,
        height: nd.h,
        left: (this.productArea.dW - nd.w) / 2,
        top: (this.productArea.dH - nd.h) / 2
      }
      log(sDims);
    }*/
    
    var x1 = sDims.left; //left gap
    var x2 = src.productArea.dW - (sDims.left + sDims.width); //right gap
    
    var pLeft = parseFloat(x1) / parseFloat(src.productArea.dW) * 100.0;
    var pRight = parseFloat(x2) / parseFloat(src.productArea.dW) * 100.0;
    
    var horizontalGapScale = null;
    if(pLeft <= 2 && pRight <= 2) { //the left/right gap is less then 2% of width... as good as centered...
      horizontalGapScale = 0.5;
    } else {
      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?
    if((itemsToCopy != null)&&(itemsToCopy.length > 0)) { //if the product was changed between copy/paste
      if(itemsToCopy[0].cViewArea == this) {
        selfCopy=true;
      }
    }
    
    
    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(!d.inSimpleMode(TEMPLATE_MODE_DESIGN) && 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);
        }
        any_ok = true;
      } else {
        log("Cannot use item for process " + item.cViewProcess.id);
        all_ok = false;
        //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);
    if(all_ok) {
      return 0;
    } else if(any_ok) {
      return 2;
    } else {
      return 1;
    }
  },
  
  
  copyTo: function CPVA_copyTo(destArea, copyState) {
    var existingCount = hashSize(destArea.allItems);
    var thisCount = copyState.length;
    destArea.moveElements(this, false, true, copyState);
    destArea.showManagementPanels();
    destArea.setReRender(true);
   // this.setReRender(true);
    var newCount = hashSize(destArea.allItems);
    return [newCount-existingCount, thisCount];
  },
  
  getCopyState: function CPVA_getCopyState() {
    var srcArray = []; //make a copy of the src becuase copy() will add it to src.allItems causing an infiniate loop
    for(var k in this.allItems) {
      srcArray.push(this.allItems[k]);
    }
    return srcArray;
  },
  
  //called after the items have all moved to another product and everything is setup again
  validate: function CPVA_validate() {
    for(var k in this.allItems) {
      var item = this.allItems[k];
      item.validate();
    }
  },
  
  getMinCanvasSize: function CPVA_getMinCanvasSize() {
    var top = -1;
    var bottom = -1;
    var left = -1;
    var right = -1;
    for(var k in this.allItems) {
      var item = this.allItems[k];
      var dims = item.getCanvasDims();
      if(dims.t < top || top == -1) {
        top = dims.t;
      }
      if(dims.t + dims.h > bottom || bottom == -1) {
        bottom = dims.t + dims.h;
      }
      if(dims.l < left || left == -1) {
        left = dims.l;
      }
      if(dims.l + dims.w > right || right == -1) {
        right = dims.l + dims.w;
      }
    }
    
    //we now have the max dims...
    return {top : top, left: left, width: right - left, height: bottom - top};
  },
  
  testIfCanMove: function CPVA_testIfCanMove(src) {
    var allowedProcesses = this.getAllowedProcesses();
    var all_ok = true;
    var any_ok = false;
    for(var k in src.allItems) {
      var item = src.allItems[k];
      if(allowedProcesses[item.cViewProcess.id]) {
        any_ok = true ;
      } else {
        all_ok = false;
      }
    }
    //we move to the higher number so 'any match' overrides 'no match' or 'all matches'
    if(all_ok) {
      return 0;
    } else if(any_ok) {
      return 2;
    } else {
      return 1;
    }
  },
  
  selectBackgroundColor: function CPVA_selectBackgroundColor(processId) {
    if((processId == null)||(processId == -1)) {
      processId = null;
      //try find the first process that is used 
      for(var i=0; i < processes.list.size(); i++) {
        var process = processes.list[i];
        var cProcess = this.processes[process.id];
        if(cProcess != null && cProcess.isUsed()) {
          processId = process.id;
          break;
        } else if(cProcess != null) {
          if(processId == null) processId = process.id; //fallback...
        }
      }
    }
    var self = this;
    var button = $("bg_color_button");
    pwColorPicker.selectColor(processId, button, self.bgColor, 
    //showColorPicker(button, 
      function(c, co) { 
        if(c=="Transparent") {
          self.bgColor = new PikiColor("transparent"); 
          button.style.backgroundColor = "";
          button.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
          self.canvas.style.backgroundColor = "";
        } else {
          self.bgColor = new PikiColor(co);
          button.style.backgroundColor = c;
          button.style.backgroundImage = "";
          self.canvas.style.backgroundColor = c;
        }
        self.setReRender();
      }, {allow_transparency:true});
    
  },
  
  setChildReRender: function CPVA_setChildReRender(queueUpdate) {
    this.reRenderChild = true;
    this.renderVersion ++;
    this.configuredView.setChildReRender(queueUpdate);
  },
  
  setReRender: function CPVA_setReRender(queueUpdate) {
    this.reRender = true;
    this.renderVersion ++;
    this.configuredView.setChildReRender(queueUpdate);
  },
  
  clearReRender: function CPVA_clearReRender() {
    this.reRenderChild = false;
    this.reRender = false;
    for(var k in this.allItems) {
      this.allItems[k].clearReRender();
    }
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns total this views costs to decorate
  calculatePrice: function CPVA_calculatePrice(colorType, fullPriceData, priceData, percentMarkup, pricingState, forProductViewAreaProcess) {
    var areaTotal = priceZero();
    var areaPriceData = {
      id: this.productArea.id,
      type: 2,
      area: this,
      processes: new MapList()
    };
    var bgCalc = false;
    var allowedProcs = this.getAllowedProcesses();
    for(var i=0; i < processes.list.size(); i++) {
      var process = processes.list[i];
      if(allowedProcs[process.id]) {
        var cProcess = this.processes[process.id];
        if(forProductViewAreaProcess != null) {
          if(forProductViewAreaProcess.id == process.id) {
            areaTotal = cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup, pricingState);
            break;
          }
        } else {
          if(cProcess != null) cProcess.onForBG = false;
          if(cProcess != null && cProcess.isUsed()) {
            areaTotal = areaTotal.add(cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup, pricingState));
          } else if ((this.bgColor != null)&&(this.bgColor.type != "transparent")&&(cProcess != null)) {
            if (!bgCalc) {
              if (this.configuredProduct.defaultProcessId == process.id) {// || (this.configuredProduct.defaultProcessId == null || this.configuredProduct.defaultProcessId == -1)) {
                bgCalc = true;
                cProcess.onForBG = true;
                areaTotal = areaTotal.add(cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup, pricingState));
              }
            }
          }
        }
      }
    }
    if(areaTotal.cost > 0 || areaTotal.isOverriden) {
      areaPriceData.total = areaTotal;
      priceData.areas.add(areaPriceData);
    }
    return areaTotal;
  },
  
  setErrors: function CPVA_setErrors(errors) {
    if(errors==null) errors = {};
    if(this.lastErrors != null) {
      //clear all errors from last errors not in these errors before setting these errors...
      for(var k in this.lastErrors) {
        if(errors[k] == null) {
          var error = this.lastErrors[k];
          var item = this.allItems[error.cause];
          if(item != null) { //it may have been deleted...
            item.removeAlert(0, error.type);
          } else {
            this.configuredProduct.setAlertIcons(); //it may need to be recalced...
          }
        }
      }
    }
    for(var k in errors) {
      var error = errors[k];
      var item = this.allItems[error.cause];
      if(item != null) {
        var message = error.message == null ? ml(error.type) : error.message;
        if(this.lastErrors!=null && this.lastErrors[k] != null) { //same error...
          item.updateAlertMessage(0, error.type, "", message);
        } else {
          item.addAlert(0, error.type, "alert_" + error.type, "", message, true);
        }
      }
    }
    this.lastErrors = errors;
  },
  
  getItems: function CPVA_getItems(process, typeid) {
    if(typeof typeid == 'number') {
      typeid = [typeid];
    }
    var items = [];
    $H(this.allItems).each(function(pair) {
        item = pair.value;
        if(item.cViewProcess == process) {
          for(var i=0; i < typeid.length; i++) {
            if (item.itemType == typeid[i]) {
              items.push(item);
            }
          }
        }
    });
    
    return items;
  },
  
  onColorChange: function CPVA_onColorChange(color) {
    for(var k in this.processes) {
      process = this.processes[k];
      if(process.isUsed()) {
        process.onColorChange(color);
      }
    }
  }
      
});

var ConfiguredViewProcess = Class.create({
  CLASSDEF: {
      name: 'ConfiguredViewProcess'
  },
  
  initialize: function CPVAP_initialize(configuredViewArea, productProcess) {
    this.id = productProcess.id;
    this.configuredViewArea = configuredViewArea;
    this.configuredProduct = configuredViewArea.configuredProduct;
    this.productProcess = productProcess;
    //build the div that will contain the 
    this.htmlId = d.getNextId();
    this.hasImageItem = false;
    this.hasTextItem = false;
    
    var process = this.getProcess();
    if(process == null) return;
    
    if (process.isScreenPrinting()) {
      this.colorCount = process.defaultColors;
      this.colorNames = [];
    }
    
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      this.managePaneFull = $(document.createElement("DIV"));
      this.managePaneFull.id = "mp_c_" + this.htmlId;
      this.managePaneId = this.managePaneFull.id;
      
      this.managePaneFull.style.display = "none";
      
      if(d.mode == DESIGNER_MODE_TEMPLATE) {
        
        if(d.templateMode == TEMPLATE_MODE_DESIGN) {
          this.managePaneFull.innerHTML = '<div id="mp_t_c_' + this.htmlId + '"><h4>' + ml('Edit Text') + '</h4><ul id="mp_t_' + this.htmlId + '" class="ul_add text_items"></ul></div><div id="mp_i_c_' + this.htmlId + '"><h4>' + ml('Select Images') + '</h4><ul id="mp_i_' + this.htmlId + '" class="ul_add image_items"></ul></div>';
        } else {
          this.managePaneFull.innerHTML = '<ul id="mp_t_' + this.htmlId + '" class="ul_add text_items"></ul><ul id="mp_i_' + this.htmlId + '" class="ul_add image_items"></ul>';
        }
      } else {
        this.managePaneFull.className="manage_pane_width";
        this.managePaneFull.innerHTML = '<h3 class="manage_pane_header" style="display:none;" id="mp_h_' + this.htmlId + '">' + productProcess.process.name + '</h3>' +
        '<div id="mp_s_' + this.htmlId + '" class="manage_pane_settings"></div>' +
        '<div id="mp_' + this.htmlId + '" class="manage_pane_body"></div>';
      }
        
      configuredViewArea.managePane.appendChild(this.managePaneFull);
      if(d.mode == DESIGNER_MODE_TEMPLATE) {
        this.managePaneText = $('mp_t_' + this.htmlId);
        this.managePaneTextContainer = $('mp_t_c_' + this.htmlId);
        this.managePaneImage = $('mp_i_' + this.htmlId);
        this.managePaneImageContainer = $('mp_i_c_' + this.htmlId);
      } else {
        this.managePane = $('mp_' + this.htmlId);
        this.managePaneHeader = $('mp_h_' + this.htmlId);
        this.managePaneSettings = $('mp_s_' + this.htmlId);
        this.updateManagePaneSettings();
      }
    }
    this.items = new MapList();
    this.visibleItems = new MapList();
    this.enabled = false;
    
    this.legacyKey = null;
    this.usingOverridingDec = false;
    this.usingOverridingClipart = false;
    this.overridingDecorationPrice = null;
    this.overridingClipartPrice = null;
    this.overridingStitchCount = false;
    var self = this;
    this.clipArtObj = {
      getName: function() {
        return self.getName() + ": Clipart Charge"; 
      }
    }
    this.decObj = {
      getName: function() {
        return self.getName() + ": Decoration Charge"; 
      }
    }

  },
  
  getProcess: function() {
    return processes.byId[this.id];
  },
  
  getName: function() {
    return this.configuredViewArea.getName() + ": " + this.productProcess.getName();
  },
  
  reSortZOrder: function CPVAP_reSortZOrder() {
    
    this.items.resort();
    if(d.mode != DESIGNER_MODE_TEMPLATE) {
      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) {
            if(item.titleZDown) item.titleZDown.className="off";
            item.isFirstZ = true;
          }
        } else if(item.isFirstZ) {
          if(item.titleZDown) item.titleZDown.className=null;
          item.isFirstZ = false;
        }
        
        if(i==this.items.list.size()-1) {
          if(!item.isLastZ) {
            if(item.titleZUp) item.titleZUp.className="off";
            item.isLastZ = true;
          }
        } else if(item.isLastZ) {
          if(item.titleZUp) item.titleZUp.className=null;
          item.isLastZ = false;
        }
      }
    }
  },
  
  onColorChange: function CPVAP_onColorChange(color) {
    this.updateColorCount();
  },
  
  updateColorCount: function CPVAP_updateColorCount(count) {
    if (!this.getProcess().isScreenPrinting()) return;
    
    var maxCount = this.getProcess().getMaxColors(this.configuredProduct.getColorType());
    if (count == null) {
      count = Math.min(this.colorCount, maxCount);
    } else {
      count = parseInt(count);
    }
    if (typeof(count) != 'NaN' && count > 0 && count <= maxCount) {
      this.colorCount = count;
      this.updateManagePaneSettings();
      this.configuredProduct.product.type.updatePrice();
    }
  },
  
  updateManagePaneSettings: function CPVAP_updateManagePaneSettings() {
    var process = this.getProcess();
    var html = '';
    
    if (process.isScreenPrinting() && this.colorCount > 0) {
      var useWhiteBase = this.getProcess().useWhiteBase(this.configuredProduct.getColorType());
      html = "<ul class='scr_list'>";
      html += "<li>";
      html += '<label>Number of colors in area</label>';
      
      html += '<select id="mp_s_cc_' + this.htmlId + '">';
      var maxCount = this.getProcess().getMaxColors(this.configuredProduct.getColorType());
      for(var i=1; i<=maxCount; i++) {
        html += '<option value="' + i + '"' + ((i == this.colorCount) ? ' selected="selected"' : '') + '>' + i + '</option>';
      }
      html += '</select> ';
      
      if (useWhiteBase) {
        html += ml('plus white base');
      }
      
      html += "</li><li>";
      html += 'Color names i.e. "Red" or "PMS 485"';
      var needColorCount = this.colorCount - this.colorNames.size();
      if (needColorCount > 0) {
        for(var i=0; i<needColorCount; i++) {
          this.colorNames.push('');
        }
      }
      html+="<ul class='scr_color_list'>";
      for(var i=0; i<this.colorCount; i++) {
        html += '<li><label class="w_15">#' + (i+1) + '</label><input type="text" id="mp_s_cn_' + this.htmlId + '_' + i + '" pos="' + i + '" value="' + this.colorNames[i] + '" size="7" /></li>';
      }
      html+="</ul></li></ul>";
      
      if (this.managePaneSettings) this.managePaneSettings.update(html);
      
      var self = this;
      $('mp_s_cc_' + this.htmlId).onchange = function() {
        self.updateColorCount(this.value);
      };
      
      for(var i=0; i<this.colorCount; i++) {
        $('mp_s_cn_' + this.htmlId + '_' + i).onkeyup = function() {
          self.colorNames[this.getAttribute('pos')] = this.value;
        };
      }
    }
  },
  
  initItems: function CPVAP_initItems() {
    var items = null;
    if(d.mode == DESIGNER_MODE_TEMPLATE) {
      items = [];
      //sort by text then images...
      for(var i=0; i < this.items.list.length; i++) {
        var item = this.items.list[i];
        if(item.itemType == 0) {
          items.push(item);
        }
      }
      for(var i=0; i < this.items.list.length; i++) {
        var item = this.items.list[i];
        if(item.itemType != 0) {
          items.push(item);
        }
      }
    } else {
      items = this.items.list;
    }
    
    for(var i=0; i < items.length; i++) {
      var item = items[i];
      log("initing item:" + item.id);
      if(d.mode == DESIGNER_MODE_TEMPLATE) {
        item.initialiseTemplatePane();
        item.addToDesigner(false);
      } else {
        //if (!$('mp1_' + item.elId)) { // will be created in initialiseManagePane
          item.initialiseManagePane();
          item.addToDesigner(false);
        //}
      }
      item.showPanel();
      log("inited item:" + item.id);
    }
    this.reSortZOrder();
    if((d.mode == DESIGNER_MODE_TEMPLATE)&&(d.templateMode == TEMPLATE_MODE_DESIGN)) {
      //hide text/image container if none exists
      if(!this.hasImageItem) {
        $('mp_i_c_' + this.htmlId).hide();
      }
      if(!this.hasTextItem) {
        $('mp_t_c_' + this.htmlId).hide();
      }
    }
  },
  
  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);
    if(d.mode == DESIGNER_MODE_TEMPLATE) {
      newItem.initialiseTemplatePane();
    } else {
      newItem.initialiseManagePane();
    }
    newItem.addToDesigner(false);
    newItem.showPanel();
    this.reSortZOrder();
    d.currentProductType.updatePrice(true);
  },
  
  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(true);
  },
  
  isOverridingPrices: function() {
    return (this.overridingDecorationPrice || this.overridingClipartPrice);
  },
  
  isUsed: function CPVAP_isUsed() {
    return (this.items.list.size() > 0);//(this.isOverridingPrices() || (this.items.list.size() > 0));
  },
  
  needToSerialize: function CPVAP_needToSerialize() {
    return (this.isUsed() || this.onForBG || this.isOverridingPrices());
  },
  
  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(this.managePaneHeader != null) {
      if(useHeader) {
        this.managePaneHeader.style.display="";
      } else {
        this.managePaneHeader.style.display="none";
      }
    }
    if(this.managePaneFull != null) {
      if(onlyIfHasItems && !this.isUsed()) {
        this.managePaneFull.style.display="none";
      } else {
        this.managePaneFull.style.display="";
      }
    }
  },
  
  hidePanel: function CPVAP_hidePanel() {
    if(this.managePaneFull != null) {
      this.managePaneFull.style.display="none";
    }
  },
  
  select: function CPVAP_select() {
    
  },
  
  remove: function CPVAP_remove() {
    for(var i=0;i<this.items.list.length;i++) {
      this.items.list[i].del();
    }
    if(this.managePaneFull != null) {
      this.managePaneFull.parentNode.removeChild(this.managePaneFull);
      this.managePaneFull = null;
    }
  },
  
  getNextZOrder: function CPVAP_getNextZOrder() {
    var maxZ = -1;
    for(var i=0;i<this.items.list.size();i++) {
      if(this.items.list[i].zIndex > maxZ) {
        maxZ = this.items.list[i].zIndex;
      }
    }
    return maxZ+1;
  },
  
  buildManagePane: function CPVAP_buildManagePane(item) {
    // return if existed
    var html = types[item.itemType];
    var re = new RegExp("\\[ID\\]", "g");
    html = html.replace(re,item.elId);

    var x = new Insertion.Top(this.managePane, html);
    
    
    
    //html = type_min.replace(re,item.id);
    //x = new Insertion.Top(this.managePanes[0], html);
  },
  
  buildTemplatePane: function CPVAP_buildTemplatePane(item) {
    var html = mtypes[item.itemType] ;
    var re = new RegExp("\\[ID\\]", "g");
    html = html.replace(re,item.elId);

    if(item.itemType == 1 || item.itemType == 4) { //text
      var x = new Insertion.Top(this.managePaneText, html);
      this.hasTextItem = true;
    } else if(item.itemType == 2 || item.itemType == 6) { //teamname
      this.hasTeamNames = true;
    } else {
      var x = new Insertion.Top(this.managePaneImage, html);
      this.hasImageItem = true;
    }
    
    
    //html = type_min.replace(re,item.id);
    //x = new Insertion.Top(this.managePanes[0], html);
  },
  
  getManagePaneContainer: function(itemType) {
    if(d.mode == DESIGNER_MODE_TEMPLATE) {
      if(itemType == 1 || itemType == 4) { //text
        return this.managePaneText;
      } else {
        return this.managePaneImage;
      }
    } else {
      return this.managePane;
    }
  },
  
  generateDecorationPriceKey: function(currentAreaCount, stitchCount) {
    var colorType = this.configuredProduct.getColorType();
    if(this.productProcess.productTypeProcess.priceMode == 0) { //lwd
      key = "LWD:CT=" + colorType;
      
    } else if(this.productProcess.productTypeProcess.priceMode == 1) { //single price
      key = "SP";
      
    } else { //lookup tables
      var tmpColorType = this.productProcess.process.usesColorTypes ? colorType : 0;
      if(this.productProcess.process.usesStitchCounts) {
        if (stitchCount == null) stitchCount = this.getStitchCount();
        key = "PT:Q=" + this.configuredProduct.qty + "&SC=" + stitchCount;
        
      } else if (this.productProcess.process.isScreenPrinting()) {
        key = "PT:Q=" + this.configuredProduct.qty + "&CC=" + this.colorCount;
        
      } else {
        key = "PT:Q=" + this.configuredProduct.qty + "&CT=" + tmpColorType + "&AC=" + currentAreaCount;
      }
    }
    return key;
  },
  
  generateClipartPriceKey: function(usedAssetIds) {
    return usedAssetIds.join(",");
  },
  
  getStitchCount: function(calced) {
    var stitchCount = 0;
    var incompleteStitchCount = false; //do we have a stitch count for all the items?
    
    if(this.productProcess.process.isWilcomEMB()) {
      if (this.usingOverridingStitchCount && !calced) {
        return this.overridingStitchCount;
      }
      
      //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 && !isNaN(item.stitchCount)) {
              stitchCount += item.stitchCount;
            } else {
              log("Found incomplete stitch count");
              incompleteStitchCount = true;
            }
          } else if(item.isSavedDigitized) {
            if(item.stitchCount != null && !isNaN(item.stitchCount)) {
              stitchCount += item.stitchCount;
            } else {
              log("Found incomplete stitch count");
              incompleteStitchCount = true;
            }
          }
        }
      }
      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;
        }
      }
    }
    
    return stitchCount;
  },
  
  calcDecPrice: function CPVAP_calcDecPrice(processPriceState, colorType, stitchCount, areaCount, colorCount) {
    var pricingData = this.productProcess.getPricingData();
    
    if (processPriceState == null) processPriceState = {};
    var cost = 0;
    
    if(this.productProcess.productTypeProcess.priceMode == 0) { //lwd
      cost = pricingData.prices[colorType];
      processPriceState.priceType = colorType;
      processPriceState.priceDelta = pricingData.prices[colorType] - pricingData.prices[0];
      
    } else if(this.productProcess.productTypeProcess.priceMode == 1) { //single price
      cost = pricingData.prices[0];
      processPriceState.priceType = -1;
      processPriceState.priceDelta = 0;
      
    } else { //lookup tables
      processPriceState.priceType = -2;
      
      var priceTable = pricingData.priceTable;
      var colorType = this.productProcess.process.usesColorTypes ? colorType : 0;
      if(this.productProcess.process.usesStitchCounts) {
        var embPriceData = priceTable.calcPrice(stitchCount, this.configuredProduct.qty, colorType);
      
        processPriceState.stitchCount = stitchCount;
        cost = embPriceData[0];
        processPriceState.threadCountLower = embPriceData[1];
        processPriceState.threadCountUpper = embPriceData[2];
        processPriceState.priceDelta = 0;
      } else if (this.productProcess.process.isScreenPrinting()) {
        var tablePriceData = null;
        tablePriceData = priceTable.calcPrice(colorCount, this.configuredProduct.qty, 0, this.productProcess.process.useWhiteBase(colorType));
        cost = tablePriceData[0];
        log("priceTable.calcPrice, cost = " + cost);
      } else {
        var tablePriceData = null;
        tablePriceData = priceTable.calcPrice(areaCount, this.configuredProduct.qty, colorType);
        cost = tablePriceData[0];
        log("priceTable.calcPrice, cost = " + cost);
      }
    }
    
    return cost;
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns [decTotal, assetTotal] this views costs to decorate in price objects
  calculatePrice: function CPVAP_calculatePrice(colorType, fullPriceData, priceData, percentMarkup, pricingState) {
    
    var processPriceState = pricingState.processes[this.productProcess.process.id];
    processPriceState.currentCount ++;
    var processPriceData = {
      id: this.productProcess.process.id,
      type: 3,
      process: this,
      priceType: 0,
      extras: []
    };
    processPriceData.calcedStitchCount = this.getStitchCount(true);
    var stitchCount = this.getStitchCount();
    var cost = this.calcDecPrice(processPriceState, colorType, stitchCount, processPriceState.currentCount, this.colorCount);
    
    var decKey = this.generateDecorationPriceKey(processPriceState.currentCount, stitchCount);
    if(d.forceLegacyPrices) this.legacyKey = decKey;
    
    var usedAssetIds = [];
    var decLibCost = 0;
    
    //add decoration library asset charges
    for (var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      if(item.asset != null && item.asset.templateId == null) {
        if(item.asset.decorationLibraryId != 0) {
          log('trying to calculate cost from process');
          var itemcost = item.asset.itemCost(this.configuredProduct.cart);
          log("item declib cost " + itemcost);
          if(itemcost > 0) {
            usedAssetIds.push(item.asset.id);
            decLibCost += itemcost;
            log('dec lib charge is now ' + decLibCost);
          }
        }
      }
    }
    
    var usedTemplates = this.getUsedTemplates();
    for (var i=0; i < usedTemplates.length; i++) {
      var template = usedTemplates[i];
      
      if(template.decorationLibraryId != 0) {
        log('trying to calculate cost from process');
        var itemcost = template.itemCost(this.configuredProduct.cart);
        log("item declib cost " + itemcost);
        if(itemcost > 0) {
          usedAssetIds.push(template.id);
          decLibCost += itemcost;
          log('dec lib charge is now ' + decLibCost);
        }
      }

    }
    
    
    usedAssetIds = usedAssetIds.sortBy( function(el) { el });
    
    var clipartKey = this.generateClipartPriceKey(usedAssetIds);
    if(d.forceLegacyPrices) this.legacyCKey = clipartKey;
    
    log("decKey=" + decKey + ", legacyKey=" +this.legacyKey + ", legacyCKey=" + this.legacyCKey + ", clipartKey=" + clipartKey);
    
    
    var oDecPrice = null;
    var oClipArtPrice = null;
    var lDecPrice = null;
    var lClipArtPrice = null;
    if((!this.usingOverridingDec) && (decKey == this.legacyKey) && (d.useLegacyPrices)) {
      log("using legacy dec price " + this.overridingDecorationPrice);
      lDecPrice = this.overridingDecorationPrice;
    } else if(!this.usingOverridingDec) {
      log("not using legacy dec prices");
    } else {
      log("using override dec price " + this.overridingDecorationPrice);
      oDecPrice = this.overridingDecorationPrice;
    }
    if((!this.usingOverridingClipart) && (clipartKey == this.legacyCKey) && (d.useLegacyPrices)) {
      log("using legacy clipart price " + this.overridingClipartPrice);
      lClipArtPrice = this.overridingClipartPrice;
    } else if(!this.usingOverridingClipart) {
      log("not using legacy clipart prices");
    } else {
      log("using override clipart price " + this.overridingClipartPrice);
      oClipArtPrice = this.overridingClipartPrice;
    }
    
    
    
    var price = priceFromCost(cost, d.commissionRate, percentMarkup, 0, oDecPrice, this.usingOverridingDec, lDecPrice);
    var clipArtPrice = priceFromCost(decLibCost, d.commissionRate, percentMarkup, 0, oClipArtPrice, this.usingOverridingClipart, lClipArtPrice);
    
    if(lDecPrice != null) {
      this.configuredProduct.registerLegacyPrice(lDecPrice, price.crp, this.decObj);
    }
    if(lClipArtPrice != null) {
      this.configuredProduct.registerLegacyPrice(lClipArtPrice, clipArtPrice.crp, this.clipArtObj);
    }
      
    processPriceData.clipartPrice = clipArtPrice;
    processPriceData.decorationPrice = price;
    

    
    log("pre round price=" + price.toString() + ", " + clipArtPrice.toString());
    
    price.round();
    clipArtPrice.round();
    
    if(d.setUnitPrices) {
      if(!this.usingOverridingDec) {
        this.overridingDecorationPrice = price.getLegacy();
      }
      if(!this.usingOverridingClipart) {
        this.overridingClipartPrice = clipArtPrice.getLegacy();
      } 
    }
    
    this.clipartPrice = clipArtPrice;
    this.decorationPrice = price
    
    var totalPrice = price.add(clipArtPrice);
    totalPrice.round();
    
    log("price=" + price.toString() + ", clipArtPrice=" + clipArtPrice.toString() + ", totalPrice=" + totalPrice.toString());
    
    
    if(totalPrice.cost > 0 || totalPrice.isOverriden) {
      processPriceData.total = totalPrice;
      priceData.processes.add(processPriceData);
    }
    return totalPrice;
  },
  
  getUsedTemplates: function() {
    var usedTemplates = {};
    var templates = [];
    for (var i=0; i < this.items.list.length; i++) {
      var item = this.items.list[i];
      if(item.asset && item.asset.templateId != null) {
        if(usedTemplates[item.asset.templateId] == null) {
          usedTemplates[item.asset.templateId] = true;
          templates.push(d.assets[item.asset.templateId]);
        }
      }
    }
    return templates;
  },
  
  resetLegacyOption: function() {
    
  },
  
  serialize: function CPVAP_serialize(queryComponents, prefix) {
    if(prefix==null) {
      prefix="p[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[uod]") + "=" + encodeURIComponent(this.usingOverridingDec));
    queryComponents.push(encodeURIComponent(prefix + "[uoc]") + "=" + encodeURIComponent(this.usingOverridingClipart));
    queryComponents.push(encodeURIComponent(prefix + "[odp]") + "=" + encodeURIComponent(this.decorationPrice.getOverride()));
    queryComponents.push(encodeURIComponent(prefix + "[ocp]") + "=" + encodeURIComponent(this.clipartPrice.getOverride()));
    if (this.productProcess.process.isWilcomEMB()) {
      queryComponents.push(encodeURIComponent(prefix + "[uosc]") + "=" + encodeURIComponent(this.usingOverridingStitchCount));
      queryComponents.push(encodeURIComponent(prefix + "[osc]") + "=" + encodeURIComponent(this.overridingStitchCount));
    }
    if (this.productProcess.process.isScreenPrinting()) {
      queryComponents.push(encodeURIComponent(prefix + "[cc]") + "=" + encodeURIComponent(this.colorCount));
      for(var i=0;i<this.colorCount;i++) {
        var color = this.colorNames[i];
        queryComponents.push(encodeURIComponent(prefix + "[cs][]") + "=" + encodeURIComponent(color));
      }
    }
    
    return queryComponents.join('&');
  }
});


var TeamNames = Class.create({
  CLASSDEF: {
      name:  'TeamNames'
  },
  
  initialize: function TeamNames_initialize(configuredProduct) {
    this.configuredProduct = configuredProduct;
    this.names = new MapList(this);
    this.items = {};
    this.nextId = 1;
    this.selectedTeamName = null;
    this.suffix = "";
    this.container = $("teamname_edit_container");
  },
  
  setSuffix: function(suffix) {
    this.suffix = suffix;  
  },
  
  registerTeamNameItem: function TeamNames_registerTeamNameItem(teamNameItem) {
    this.items[teamNameItem.id] = teamNameItem;
    this.configuredProduct.usingTeamnames = true;
  },
  
  unregisterTeamNameItem: function TeamNames_unregisterTeamNameItem(teamNameItem) {
    delete this.items[teamNameItem.id]
    if(hashFirstElement(this.items) == null) {
      this.configuredProduct.usingTeamnames = false;
      var hasSizes = this.configuredProduct.supportMultiSizes();
      if(hasSizes) {
        var sizeField = this.configuredProduct.getSelectedSize();
        sizeField.refreshHtml();
      }
      this.configuredProduct.product.type.updatePrice();
    }
  },
  
  getNextId: function TeamNames_getNextId() {
    return this.nextId++;
  },
  
  displayInline: function() {
    return (d.mode == DESIGNER_MODE_TEMPLATE);
  },
  
  customProduct: function() {
    return (d.mode == DESIGNER_MODE_CONFIGURE);
  },
  
  load: function TeamNames_load(params) {
    this.names = new MapList(this);
    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.names.list.length > 0) {
      this.selectedTeamName = this.names.list[0];
    } else {
      this.selectedTeamName = null;
    }
    this.loadTeamNameIds();
  },
  
  //the teamnames have been initialised from a custom product...check it...
  checkInitFromCustom: function TeamNames_checkInitFromCustom() {
    var defOpt = null;
    var defSubOpt = null;
    var hasSizes = this.configuredProduct.supportMultiSizes();
    
    if(hasSizes) {
      var sizeField = this.configuredProduct.product.getSizeField();
      if(sizeField.defaultOption != null) {
        defOpt = sizeField.defaultOption.id;
        if(sizeField.defaultOption.defaultSub != null) {
          defSubOpt = sizeField.defaultOption.defaultSub.id;
        }
      }
    }
    
    if(defOpt != null) {
      for(var i=0; i < this.names.list.length; i++) {
        var name = this.names.list[i];
        if(name.optionId == null) {
          name.optionId = defOpt;
          name.subOptionId = defSubOpt;
        }
      }
    }
    if(this.configuredProduct.product.usesMinQty()) {
      var minQty = this.configuredProduct.getMinQty();
      while(this.names.list.length < minQty) {
       var name = new TeamName({id : this.getNextId(), o: defOpt, s: defSubOpt}, this);
       this.names.add(name);
      }
    }
    
  },
  
  checkDefaultText: function() {
    var names = this.getNames();
    var ids = this.getNameDataList();
    var selName = this.getSelectedName();
    
    for(var i=0; i < ids.length; i++) {
      var found = false;
      var nameItem = ids[i];
      if(nameItem.defaultText != null) {
        for(var x=0; x < names.list.length; x++) {
          var name = names.list[x];
          var cellData = name.getCellData(nameItem.id);
          if((cellData != null)&&(cellData != "")) {
            found = true;
            break;
          }
        }
        if(!found) {
          selName.setCellData(nameItem.id, nameItem.defaultText);
        }
      }
    }
  },
  
  
  getUniqueLabel: function(label) {
    var nameData = this.getNameDataList();
    var found = false;
    var cnt = 1;
    while(!found) {
      found = true;
      for(var i=0; i < nameData.length; i++) {
        if(nameData[i].label == label) {
          found = false;
          cnt ++;
          label = label + " " + cnt;
          break;  
        }
      }
    }
    return label;
  },
  
  initNew: function TeamNames_initNew(newTeamNameItems, commit, addNames) {
    this.newTeamNameItems = newTeamNameItems;
    this.loadTeamNameIds();
    var existingGroups = this.groupNamesBySizes();
    if(addNames) {
      if(!this.customProduct()) {
        var sizeField = this.configuredProduct.getSelectedSize();
        if(this.configuredProduct.supportMultiSizes() && sizeField != null) {
          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 + this.suffix);
                  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 + this.suffix);
                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 qEl = $("tn_qty" + this.suffix);
          if(qEl != null) {
            var qty = parseInt(qEl.value, 10);
            this.addTeamNames(qty, null, null, existingGroups);
          }
        }
      } else {
        this.addTeamNames(1, 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];
    }
    
    this.checkDefaultText();

    if(this.container != null) {
      this.container.update(this.renderHtml());
    }
    
    
    
    if(commit) {
      this.names = this.tmpNames;
      this.selectedTeamName = this.tmpSelectedTeamName;
    }
    
    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.suffix = "";
    this.loadTeamNameIds();
    $("teamname_edit_container").update(this.renderHtml());
    d.deSelectCurrentItem();
    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.newTeamNameItems != null) {
      for(var i=0; i < this.newTeamNameItems.length; i++) {
        this.newTeamNameItems[i].del();
      //this.unregisterTeamNameItem(this.newTeamNameItem);
      }
      this.newTeamNameItems = 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.supportMultiSizes());
    
    var defOpt = null;
    var defSubOpt = null;
    
    if(hasSizes) {
      options = this.configuredProduct.product.type.sizeField.getOptionList(this.configuredProduct, this.configuredProduct.product.getSizeField(), this.configuredProduct.getSelectedSize(), this.configuredProduct.product.sizeColorCombinations, false, false, true);
      var sizeField = this.configuredProduct.product.getSizeField();
      if(sizeField.defaultOption != null) {
        defOpt = sizeField.defaultOption.id;
        if(sizeField.defaultOption.defaultSub != null) {
          defSubOpt = sizeField.defaultOption.defaultSub.id;
        }
      }
    }
    
    
    var name = new TeamName({id : this.getNextId(), o: defOpt, s: defSubOpt}, this);
    var names = this.displayInline() ? this.names : this.tmpNames;
    names.add(name);
   

    var rowHtml = this.renderNameHtml(name, hasSizes,options,this.getNameDataList(), (names.list.length == 1));
    new Insertion.Bottom("tn_names_tbody"+ this.suffix, rowHtml);
    
    if(this.displayInline()) {
      this.updateConfiguredProductFromTeamNames();
    }
  },
  
  //from interface button 
  removeMember: function TeamNames_removeMember(id, dontReselect) {
    var names = this.displayInline() ? this.names : this.tmpNames;
    
    if(this.configuredProduct.product.usesMinQty()) {
      if(names.list.length == this.configuredProduct.product.minQty) {
        alert(ml("You must purchase at least %s items", this.configuredProduct.product.minQty));
        return;
      }
    }
    
    if((names.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.getSelectedName() == names.byId[id]);
    names.remove(id);
    $('tn_' + id + '_row'+ this.suffix).remove();
    if(wasSelected && dontReselect) {
      this.setSelectedName(null);
      this.selectedTeamName = null;
    } else if(wasSelected && names.list.length > 0) {
      this.setSelectedName(names.list[0]);
      $('tn_v_' + this.getSelectedName().id + this.suffix).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 false;
    }
    this.saveTeamNamesFromUI();
    this.updateConfiguredProductFromTeamNames();
    d.track("save-teamnames");
    this.configuredProduct.setQtyFromMulti();
    this.configuredProduct.product.type.updatePrice();
    this.selectedTeamName = this.tmpSelectedTeamName;
    this.updateTeamNameItems();
    d.itemChanged();
    return true;
  },
  
  //save from form elements back into data model
  saveTeamNamesFromUI: function TeamNames_saveTeamNamesFromUI() {
    if(this.newTeamNameItems != null) {
      this.newTeamNameItems = null;
    }
    var hasSizes = (!(this.customProduct()) && (this.configuredProduct.supportMultiSizes()));

    var sizeField = hasSizes ? this.configuredProduct.getSelectedSize() : null;
    var ids = this.getNameDataList();
    
    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 + this.suffix);
        var sizeIds = sizeField.extractOptIds(sizeSelection);
        name.optionId = sizeIds[0];
        name.subOptionId = sizeIds[1];
      }
                
      for(var i=0; i < ids.length; i++) {
        var nameData = $F('tn_' + name.id + '_' + ids[i].id + this.suffix);
        name.setCellData(ids[i].id,nameData);  
      }
    }
    
  },
  
  //update the size/qty of the configured product to match the config of the teamnames,,,
  updateConfiguredProductFromTeamNames: function TeamNames_updateConfiguredProductFromTeamNames() {
    var hasSizes = (this.configuredProduct.supportMultiSizes());
    this.configuredProduct.usingTeamnames = true;
    if(!this.customProduct()) {
      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.refreshHtml();
      }
      this.configuredProduct.qty = this.names.list.length;
      this.configuredProduct.refreshQty();
      if(d.currentCProduct != null) {
        this.configuredProduct.product.type.updatePrice();
      }
    }
  },
  
  updateTeamNameItems: function TeamNames_updateTeamNameItems() {
    for(var k in this.items) {
      var item = this.items[k];
      item.setDirty();
      item.setText(false, 2);
    }
  },
  
  //get a list of teamnames used in cp, and the data field id they use
  getNameDataList: function TeamNames_getNameDataList() {
    //first get a list of lebels, and the object ids each lable could use
    
    var usedLabels = {};
    var labels = [];
    
    for(var k in this.items) {
      var item = this.items[k];
      if(usedLabels[item.label] == null) {
        usedLabels[item.label] = [];
        labels.push(usedLabels[item.label]);
      }
      usedLabels[item.label].push(item);
    }
    
    this.labelMapping = {};
    var labelData = [];
    //for each label find the best object id to use by looking in the stored data
    for(var i=0; i < labels.length; i++) {
      var labelItems = labels[i];
      bestCount = 0;
      bestItem = null;
      for(var j=0; j < labelItems.length; j++) {
        var item = labelItems[j];
        var hitCount = 0;
        for(var k=0; k < this.names.list.length; k++) {
          var cellData = this.names.list[k].getCellData(item.id);
          if(cellData != null && cellData !='') {
            hitCount ++;
          }
        }
        if((bestItem == null)||(hitCount > bestCount)) {
          bestItem = item;
          bestCount = hitCount;
        }
      }
      labelData.push({ label: bestItem.label, id: bestItem.id, width: (bestItem.inputWidth == null ? 15 : bestItem.inputWidth), defaultText: bestItem.defaultText, defaultItem: bestItem, items: labelItems});
      //let each item know what source id it should use...
      for(var j=0; j < labelItems.length; j++) {
        labelItems[j].sourceTeamnameId = bestItem.id;
      }
      this.labelMapping[bestItem.id] = labelItems;
    }
    
    
    return labelData;
  },
  
  renderHtml: function TeamNames_renderHtml() {
    var options = null;
    var hasSizes = (this.configuredProduct.supportMultiSizes());
    
    if(hasSizes) {
      options = this.configuredProduct.product.type.sizeField.getOptionList(this.configuredProduct, this.configuredProduct.product.getSizeField(), this.configuredProduct.getSelectedSize(), this.configuredProduct.product.sizeColorCombinations, false, false, true);
    }
    
    var selName = this.getSelectedName();
    
    var cells = [];
    var rows = [];
    
    var ids = this.getNameDataList();
    if(this.displayInline()) {
      cells.push('<th>&nbsp;</th>');
    }
    if(this.customProduct() || hasSizes) {
      cells.push('<th>Sizes</th>');
    }
    for(var i=0; i < ids.length; i++) {
      cells.push('<th>' + ids[i].label + '</th>');
    }
    if(!this.customProduct()) {
      if(!this.displayInline()) {
        cells.push('<th>View</th>');
        cells.push('<th>Remove</th>');
      } else {
        cells.push('<th>&nbsp;</th>');
      }
    }
    var header = '<tr>' + cells.join('') + '</tr>';
    
    var names = this.getNames();
   
    for(var x=0; x < names.list.length; x++) {
      var name = names.list[x];
      
      rows.push('<tr>' + this.renderNameHtml(name, hasSizes,options, ids , (names.list.length==1 || selName==name) ) + '</tr>');
    }
    var extraHtml = '';
    var extraTableHtml = '';
    if(this.displayInline()) { 
      //add the color option
      cells = [];
      if(hasSizes) {
        cells.push('<td colspan="2">' + ml('Color') + ':</td>');
      } else {
        cells.push('<td>' + ml('Color') + ':</td>');
      }
      for(var i=0; i < ids.length; i++) {
        var item = ids[i].defaultItem;
        cells.push('<td><ul id="mp_' + item.elId + '_colors" class="color_panel"></ul></td>');
      }
      extraTableHtml = '<tbody><tr>' + cells.join(" ") + '</tr></tbody>';
      
      //add the add button
      extraHtml = '<div class="submit">' + 
					'<input type="button" value="Add Another" onclick="d.currentCProduct.getTeamNames().addMember();" class="button" />' + 
				  '</div>';
    }
    
    return "<table class='base hundred'>" + header + '<tbody id="tn_names_tbody' + this.suffix + '">' + rows.join("\n") + "</tbody>" + extraTableHtml + "</table>" + extraHtml;
  },
  
  renderNameHtml: function TeamNames_renderNameHtml(name, hasSizes,options, ids, isSelected) {
    cells = [];
    var selHtml = isSelected ? ' checked="true"' : '';
    if(!this.customProduct()) {
      if(this.displayInline()) {
        cells.push('<td class="align_center"><input type="radio" id="tn_v_' + name.id + this.suffix + '" name="tn_view"' + selHtml + ' onclick="d.currentCProduct.getTeamNames().view(' + name.id + ');"/></td>');
      }
    }
    if(this.customProduct()) {
      cells.push('<td>Default Content</td>');
    } else {
      if(hasSizes) {
        var event = '';
        if(this.displayInline()) {
          event = ' onchange="d.currentCProduct.getTeamNames().getItem(' + name.id + ').setSize(this.value);"';
        }
        cells.push('<td><select id="tn_size_' + name.id + this.suffix + '" ' + event + '>' + selectOptionHtml(options, name.selectedId() ) + '</select></td>');
      }
    }
    for(var i=0; i < ids.length; i++) {
      var cellData = name.getCellData(ids[i].id);
      if(cellData == null) cellData = "";
      var event = '';
      if(this.displayInline()) {
        event = ' onkeyup="d.currentCProduct.getTeamNames().setCellData(' + name.id + ',\'' + ids[i].id + '\', this.value);"';
      }
      cells.push('<td><input type="text" id="tn_' + name.id + '_' + ids[i].id + this.suffix + '" value="' + cellData + '" size="' + ids[i].width + '" ' + event + '/></td>');
    }
    if(!this.customProduct()) {
      if(!this.displayInline()) {
        cells.push('<td><input type="radio" id="tn_v_' + name.id + this.suffix + '" name="tn_view"' + selHtml + ' onclick="d.currentCProduct.getTeamNames().view(' + name.id + ');"/></td>');
      }
      cells.push('<td class="align_center"><a href="#" onclick="d.currentCProduct.getTeamNames().removeMember(' + name.id + '); return false;" class="icon delete" ignore_deselect="true">delete</a></td>');
    }
    return '<tr id="tn_' + name.id + '_row'  + this.suffix + '">' + cells.join('') + '</tr>';
  },
  
  bindColors: function() {
    var ids = this.getNameDataList();
    for(var i=0; i < ids.length; i++) {
      var item = ids[i].defaultItem;
      item.buildTemplateColorSelectors(ids[i].items);
    }
  },
  
  setCellData: function TeamNames_setCellData(nameId, cellId, data) {
    var name = this.names.byId[nameId];
    if(name == null) {
      alert("Cannot find name");
      return;
    }
    name.setCellData(cellId, data);
    if(this.getSelectedName() != null && this.getSelectedName().id == nameId) {
      var usedItems = this.labelMapping[cellId];
      for(var i=0; i < usedItems.length; i++) {
        usedItems[i].setDirty();
        usedItems[i].setText();
      }
    }
  },
  

  view: function TeamNames_view(id) {
    var names = this.displayInline() ? this.names : this.tmpNames;
    this.setSelectedName(names.byId[id]);
    if(this.displayInline()) {
      this.updateTeamNameItems();
    }
  },
  
  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;
  },
  
  getSelectedName: function() {
    if(this.displayInline()) {
      return this.selectedTeamName;
    } else {
      return this.tmpSelectedTeamName;
    }
  },
  
  setSelectedName: function(name) {
    if(this.displayInline()) {
      this.selectedTeamName = name;
    } else {
      this.tmpSelectedTeamName = name;
    }
  },
  
  getNames: function() {
    if(this.displayInline()) {
      return this.names;
    } else {
      return this.tmpNames;
    }
  },
  
  getItem: function(nameId) {
    return this.names.byId[nameId];
  },
  
  validateForSaveLineItem: function() {
    if(!this.customProduct()) {
      var names = this.getNames();
      var ids = this.getNameDataList();
      for(var i=0; i < names.list.length; i++) {
        var name = names.list[i];
        var foundOne = false;
        for(var j=0; j < ids.length; j++) {
          var data = name.getCellData(ids[j].id);
          if(data != null && data != "") {
            foundOne = true;
            break;
          }
        }
        if(!foundOne) {
          if(!this.displayInline()) {
            this.editTeamNames();
          }
          alert("You must enter a value against all teamnames");
          return false;
        }
      }
    }
    return true;
  }
  
  
});


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) {
    return this.names.names[item_id];
  },
  
  setCellData: function TeamName_setCellData(item_id, value) {
    this.names.names[item_id] = value;
  },
  
  setSize: function(size) {
    var ids = ConfiguredField.methods.extractOptIds(size);
    this.optionId = ids[0];
    this.subOptionId = ids[1];
    this.teamNames.updateConfiguredProductFromTeamNames();
  },
  
  serialize: function TeamName_serialize(queryComponents, prefix) {
    if(this.optionId != null) {
      queryComponents.push(encodeURIComponent(prefix + "[o]") + "=" + encodeURIComponent(this.optionId));
    }
    if(this.subOptionId != null) {
      queryComponents.push(encodeURIComponent(prefix + "[s]") + "=" + encodeURIComponent(this.subOptionId));
    }
    for(var k in this.names.names) {
      queryComponents.push(encodeURIComponent(prefix + "[n][" + k + "]") + "=" + encodeURIComponent(this.names.names[k]));
    }
  },
  
  serializeToOptions: function TeamName_serializeToOptions() {
    var o = {};
    if(this.optionId != null) {
      o.o = this.optionId;
    }
    if(this.subOptionId != null) {
      o.s = this.subOptionId;
    }
    var names = {}; 
    for(var k in this.names.names) {
      names[k] = this.names.names[k];
    }
    o.n = names;
    return o;
  }
});

var Product = Class.create({
  CLASSDEF: {
      name: 'Product'
  },
  
  initialize: function P_initialize(id, options) {
    this.id = id;
    this.options = options;
    this.name = options.name;
    this.code = options.code;
    this.description = options.desc;
    
    this.type = d.productTypes[options.type];
    if(this.type.noCategores) {
      this.categoryId = -1;
      this.subCategoryId = -1;
    } else {
      this.categoryId = options.cid;
      this.subCategoryId = options.sc;
    }
    this.canDoDecorate = options.cdec == null ? true : options.cdec;
    this.availableProcesses = {};
    this.displayImage = options.di;
    this.imageURL = options.u;
    this.largeImageURL = options.u2;
    this.sizeChartURL = options.szc;
    this.sizeTable = options.szt;
    
    this.price = [parseFloat(options.p[0]), parseFloat(options.p[1]), parseFloat(options.p[2])];
    
    this.taxExempt = options.te;
    
    this.minQtyHash = $H(options.mq);
    this.bundleSize = options.bs;
    if (this.bundleSize < 1) this.bundleSize = 1;
    /*
    this.minQty = options.mq;
    if(this.minQty == -1) {
      this.minQty = this.type.minQty;
      this.bundleSize = this.type.bundleSize;
    } else {
      this.bundleSize = options.bs;
    }
    */
    
    this.discountId = options.discid;
    this.discountToBase = options.datb;
    
    this.useDefaultDecorationPricing = options.uddp;
    if(this.useDefaultDecorationPricing) {
      //load the product as if it had single decoration pricing, but all pointing to default prices....
      this.useSingleDecorationPricing = true;
      this.singleDecPricing = new MapList(this);
      for(var i=0; i < this.type.processes.list.length; i++) {
        var tproc = this.type.processes.list[i];
        this.singleDecPricing.add({ id: tproc.id, use_default: true}); 
      }
    } else {
      this.useSingleDecorationPricing = options.sp;
      if(this.useSingleDecorationPricing) {
        this.singleDecPricing = new MapList(this);
        for(var i=0; i < options.spp.length; i++) {
          if(options.spp[i].pt != null) {
            options.spp[i].priceTable = d.priceTables[options.spp[i].pt];
            if(options.spp[i].priceTable == null) {
              options.spp[i].priceTable = this.type.processes.byId[options.spp[i].id].getPriceTable();
              log("ERROR: unable to get price table '" + options.spp[i].pt + "', defaulting to:" + (options.spp[i].priceTable == null ? 'null' : options.spp[i].priceTable.id) );
            }
          }
          this.singleDecPricing.add(options.spp[i]);
        }
      }
    }
    
    
    
    this.views = new MapList(this);
  
    for(var i=0; i < options.v.size(); i++) {
      var pv = new ProductView(this, options.v[i], i);
      if(d.inSimpleMode() || !this.canDoDecorate || pv.areas.list.length > 0 || (!pv.allowDesign && pv.allowView)) {
          this.views.add(pv);
      }
    }
    
    log("Product(): Loaded Views");
    
    this.defaultView = this.views.byId[options.sdv];
    this.displayView = this.views.byId[options.sdisv];
    
    this.colors = new MapList(this);
    this.discontinuedColors = new MapList(this);
    
    var pc = options.c;
    for(var i=0;i<pc.length;i++) {
      var l = new ProductColor(this, pc[i]);
      if (l.discontinued) {
        this.discontinuedColors.add(l);
      } else {
        this.colors.add(l);
      }
    }
    log("Product(): Loaded Colors");
    this.defaultColor = this.colors.byId[options.dc];
    if(this.defaultColor == null) {
      log("Product missing default color " + options.dc);
      this.defaultColor = this.colors.list[0];
    }
    
    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(allowDeleted) {
    if(this.type.sizeField == null) {
      return null;
    }
    var pField = this.fields.byId[this.type.sizeField.id];
    if (pField && (allowDeleted || pField.used)) return pField;
    return null;
  },
  
  getSizesHtml: function P_getSizesHtml() {
    var pSizeField = this.getSizeField();
    if (pSizeField == null) return '';
    
    var html = '';
    for(var i=0; i < pSizeField.options.list.length; i++) {
      var pOpt = pSizeField.options.list[i];
      if(!pOpt.def.isMulti) {
        html += pOpt.def.value + ' ';
        
        if(pOpt.def.subs != null) {
          for(var j=0; j < pOpt.def.subs.length; j++) {
            var pSubOpt = pOpt.subs[pOpt.def.subs[j].id];
            if(pSubOpt != null) {
              html += pSubOpt.def.value + ' ';
            }
          }
        }
      }
    }
    return html;
  },
  
  usesMinQty: function P_usesMinQty() {
    var b = false;
    this.minQtyHash.each(function(pair) {
        if (pair.value > 1) b = true;
    });
    return b;
    //return (this.minQty > 1);
  },
  
  usesBundles: function P_usesBundles() {  
    return (this.bundleSize > 1);
  },   
  
  usesBundlesOrMinQty: function P_usesBundles() {  
    return (this.usesMinQty() || (this.bundleSize > 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;
    }
    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);
    }
  },
  
  getBestMatchedView: function P_getBestMatchedView(oldView) {
    var processUsageData = oldView.getProcessUsage();
    var moveResult = -1;
    var bestView = null;
    var bestScore = 0;
    for(var i=0; i < this.views.list.size(); i++) {
      var view = this.views.list[i];
      var score = view.getProcessMatchScore(processUsageData);
      if((score.score > bestScore)&&(score.score != 0)) {
        bestView = view;
        bestScore = score.score;
      }
    }
    return bestView;
  }
});   

var ProductField = Class.create({
  CLASSDEF: {
      name: 'ProductField'
  },
  
  initialize: function PF_initialize(product, options) {
    this.product = product;
    this.valid = true;
    this.id = options.id; 
    this.sfid = options.sfid;
    this.fieldDef = this.product.type.fields.byId[this.id];
    if(this.fieldDef == null) return; //why bother continueing?
    
    this.used = options.u;
    this.priceDelta = options.d;
    this.usePriceDefaults = options.uDef;
    this.options = new MapList(this);
    
    this.multiOption = null;
    //if(this.used) { <- this stops legacy fields that have been removed from appearing..
      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.usesBundles()) {
            log("NOTE: ProductFieldChoice " + opt.id + " has is multi but product uses bundles " + product.name);
            this.options.remove(opt.id);
          } else {
            if(opt.selected && !opt.def.isMulti) {
              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.usesBundles() && this.multiOption == null && this.fieldDef.productType.ms && this.fieldDef.multiOption!=null) { //there is no multi option def for this product but the type says only show in multi option mode...
            log("Adding Multi Choice because type has multi only defined");
            this.options.add(new ProductFieldChoice(this, {id:this.fieldDef.multiOption.id, s:true }));
          }
          
          if(this.defaultOption == null) {
            log("ERROR: no default option loaded... using first option");
            this.defaultOption = this.options.list[0];
          }
        }
      //}
    } else {
      log("Field " + this.fieldDef.name + " is not used"); 
    }
    
  },
  
  //used when swapping products.. try and find a matching option....
  findOption: function(otherOption) {
    var foundOption = null;
    if(this.options.byId[otherOption.id] != null) {
      foundOption = this.options.byId[otherOption.id];
    } else {
      for(var i=0; i < this.options.list.length; i++) {
        var opt = this.options.list[i];
        if(opt.def.name == otherOption.def.name) {
          foundOption = opt;
          break;
        } else if(opt.def.value == otherOption.def.value) {
          foundOption = opt;
        }
      }
    }
    return foundOption;
  },
  
  //dynamically add multi option to support teamnames...
  addMultiOption: function PF_addMultiOption() {
    this.multiOption = 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];
    if(this.def == null) return;
    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;
    this.discontinued = options.dis;
    this.defaultSub = null;
    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;
          if(this.defaultSub == null) {
            this.defaultSub =  pfsc; 
          } else if(pfsc.selected) {
            this.defaultSub =  pfsc;
          }
        } 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.discontinued = options.dis;
    this.selected = options.s;
  }
});

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

    this.availableProcesses = {};
    
    for(var i=0; i < options.a.size(); i++) {
      var area = new ProductViewArea(this, options.a[i], i)
      if (area.isValid()) this.areas.add(area);
    }
  },
  
  //allowStartUrl: a view can have a start url when loading existing configured items/custom products
  //allowScale: should we lookup and layout modifiers to see if the scale changes...
  getViewURL: function PV_getViewURL(size, colorId, scale) {
    var url = this.url.replace(d.layoutViewUrlCID, colorId);
    url = url.replace(d.layoutViewUrlS, size );
    return url.replace(d.layoutViewUrlSc, scale );
  },
  
  buildSelectorHtml: function PV_buildSelectorHtml(colorId, configuredView, customProduct, state) {
    if(!this.allowView) {
      return "";
    }
    var url = null;
    if(configuredView != null) {
      url = configuredView.getViewURL(d.getThumbnailSize(), true, false, state);
    } else if(customProduct!=null) {
      url = customProduct.getViewURL(this.id, d.getThumbnailSize(), colorId, 1);
    } 
    if(url == null) {
      url = this.getViewURL(d.getThumbnailSize(), colorId, 1);
    }
    
    url += '?v=' + (new Date()).getTime();
    
    if((this.allowDesign)||(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      var areaSelHtml = (d.mode==DESIGNER_MODE_TEMPLATE) ? '' : '<div id="d_l_ah_' + this.id + '" class="sel_area_highlight" style="display:none;">&nbsp;</div>';
      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 + ');"/>' + areaSelHtml + '<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(d.mode==DESIGNER_MODE_TEMPLATE) return; //viewing custom product
    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);
    }
  },
  
  getProcessMatchScore: function(procUsageData) {
    var bestArea = null;
    var bestScore = 0;
    for(var i=0; i < this.areas.list.length; i++) {
      var area = this.areas.list[i];
      var score = area.getProcessMatchScore(procUsageData);
      if(score > bestScore) {
        bestArea = area;
        bestScore = score;
      }
    }
    return {area: bestArea, score: bestScore};
  }
});

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....
        var pvp = new ProductViewAreaProcess(this, opts);
        if(pvp.process != null) {
          this.processes.add(pvp);
          this.view.registerAvailableProcess(ptp.id);
        } else {
          log("Unable to get process for area");  
        }
      }
    }
    this.processes.resort();
  },
  
  isValid: function PVA_isValid() {
    //check if any processes can be used
    for(var i=0; i < this.processes.list.size(); i++) {
      var p = this.processes.list[i];
      var process = processes.byId[p.id];
      if (process != null && process.isValid()) return true;
    }
    return false;
  },
  
  getName: function PVA_getName() {
    return this.view.name + "-" + this.name + " (" + this.view.product.name + ")";
  },
  
  canDecorate: function PVA_canDecorate(process) {
    return (this.processes.byId[process.id] != null);
  },
  
  //get the disabled message.. for area it would be '%1s is not available on the %2s'
  //if we cant decorate because the view cant, use the views getDecProcDisabledMessage
  getDecProcDisabledMessage: function PVA_getDecProcDisabledMessage(process) {
    if(this.view.canDecorate(process)) {
      return process.name + " is not available on the " + this.name;
    } else {
      return this.view.getDecProcDisabledMessage(process);
    }
  },
  
  isInArea: function PVA_isInArea(x,y) {
    if(x > this.l && x < (this.l + this.w) && y > this.t && y < (this.t + this.h)) {
      return true;
    } else {
      return false;
    } 
  },
  
  
  getProcessMatchScore: function(procs) {
    var score = 0;
    for(k in procs) {
      if(this.processes.byId[k] != null) {
        score += procs[k];
      }
    }
    return score;
  }
});

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

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


var CustomProduct = Class.create({
  CLASSDEF: {
      name: 'CustomProduct'
  },
  
  initialize: function CProd_initialize(id, options) {
    this.id = id;
    this.name = options.n;
    this.viewOptions = options.v;
    this.productId = options.p;
    this.markup = options.m;
    this.useDefaultPricing = options.udp;
    this.defaultColor = options.color;
    this.allColors = options.allColors;
    if(!this.allColors) {
      this.colors = options.colors;
    }
    this.defaultProcessId = options.def_proc;
    this.configuredPrice = options.cp;
    this.viewUrls = {}; //this contains a map of strings with urls to the views in /xxx[CID]/[SIZE]/xxx format
    for(var i=0; i < options.u.length;i++) {
      var urlInfo = options.u[i];
      this.viewUrls[urlInfo.id] = urlInfo.u;
    }
    this.usingTeamNames = (options.utn == true);
    if(this.usingTeamNames) {
      this.teamNames = options.tn;  
    }
  },
  
  //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.spid = options.spid;
    this.options = options;
    this.optionsById = new Hash();
    this.cats = [];
    this.catsById = new Hash();
    this.catsLoaded = false;
    this.name = options.name;
    this.ms = options.ms;
    this.hidden = options.hidden == null ? false : options.hidden;
    
    //this.viewsAreDerived = options.vad; moved to product.hasDerivedViews
    this.noCategores = options.ddp;
    
    if(this.noCategores == true) { //no categories.....
      var notCat = new Category({id:-1,n:"No Category"}, this);
      this.cats.push(notCat);
      this.catsById[-1] = notCat;
      this.catsLoaded = true;
    }
    
    this.pricingType = options.pt;
    this.flags = options.flags;
    this.hasSizes = options.hs;
    
    
    this.fields = new MapList(this);
    this.disFields = new MapList(this);
    this.fieldsByCode = {};
    this.fieldsByName = {};
    this.colorField = null;
    this.sizeField = null;
    this.customFields = new MapList(this);
    
    this.minQty = options.mq;
    this.bundleSize = options.bs;
    
    this.discountId = options.discid;
    this.discountToBase = options.datb;
    
    
    
    var fl = options.f;
    for(var i=0;i<fl.length;i++) {
      var f = new ProductTypeField(fl[i].id, fl[i], this);
      //if(f.fieldType != FIELD_TYPE_PRODUCT_SIZE || this.hasSizes) { we need to load discontinued size field now...(oms)
        this.fields.add(f);
        if(f.code != null && f.code != '') {
          this.fieldsByCode[f.code] = f;
        }
        if(f.name != null && f.name != '') {
          this.fieldsByName[f.name] = f;
        }
        
        if(f.fieldType == FIELD_TYPE_PRODUCT_SIZE) {
          this.sizeField = f;
        } else if(f.fieldType == FIELD_TYPE_PRODUCT_COLOR) {
          this.colorField = f;
        } else {
          this.customFields.add(f);
        }
      //}
    }
    this.currentCategory = null;
    this.currentSubCategory = null;
    this.boundElements = false;
    this.taxExempt = options.te;
    
    this.processes = new MapList(this);
  },
  
  addCategories: function PT_addCategories(cats) {
    this.cats = [];
    this.catsById = new Hash();
    if (cats) {
      for(var i=0;i<cats.length;i++) {
        var cat = new Category(cats[i], this);
        this.cats.push(cat);
        this.catsById[cat.id] = cat;
      }
      this.catsLoaded = true;
    }
  },
  
  addProcess: function PT_addProcess(options) {
    return this.processes.add(new ProductTypeProcess(this,options));
  },
  
  addProcessMix: function PT_addProcessMix(from, to) {
    var proc = this.processes.byId[from];
    if(proc != null) {
      proc.addMix(to);
    }
  },
  
  loadCategory:function(catId, products) {
    var cat = this.catsById[catId];
    if(cat!=null) {
      cat.load(products);
    }
    return cat;
  },
  
  getColorPanel: function PT_getColorPanel() {
    if(this.colorPanel!=null) {
      return this.colorPanel;
    }
    if(this.noColorPanel) {
      return null;
    }
    this.colorPanel = $("pt_col_" + this.id);
	
    if(this.colorPanel==null) {
      this.noColorPanel = true;
    } else {
      this.colorPanel.className="color_panel";
    }
    return this.colorPanel;
  },
  
  buildColorPanelForOMS: function PT_buildColorPanelForOMS(cProduct, cp) {
    var html = '';
    
    if(cp != null) {
      var unavailableColors = [];
      var colors = cProduct.getAvailableColorList(unavailableColors);
      
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        var colorName="";
        for(var k=0; k < color.colors.length; k++) {
          colorName+= color.colors[k][1] + ' ';
        }
        var cellHtml = '<li><span id="oms_color_' + color.id + '" class="colorbox" onmouseover="orderManager.popupManager.showRollover(this, \'<p>' + colorName + '</p>\');" onmouseout="orderManager.popupManager.hideRollover(this);">';
        var h = parseInt(18 / color.colors.length);
        for(var j=0; j < color.colors.length;j++) {
          cellHtml += '<span style="background-color: ' + color.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
        }
        cellHtml += '<b class="overlay">&nbsp;</b></span></li>';
        html += cellHtml;
      }
      
      for(var i = 0; i < unavailableColors.length;i++) {
        var color = unavailableColors[i]["color"];
        var comment="";
        for(var k=0; k < color.colors.length; k++) {
          comment+= color.colors[k][1] + ' ';
        }
        comment += "is not available in ";
        var sizes = unavailableColors[i]["conflict_sizes"];
        for(var k=0; k < sizes.length; k++) {
          comment+= sizes[k];
          if (k != sizes.length - 1) comment += ", ";
          else comment += ".";
        }
        var cellHtml = '<li><span id="oms_color_' + color.id + '" class="colorbox disabled" onmouseover="orderManager.popupManager.showRollover(this, \'<p>' + comment + '</p>\');" onmouseout="orderManager.popupManager.hideRollover(this);">';
        var h = parseInt(18 / color.colors.length);
        for(var j=0; j < color.colors.length;j++) {
          cellHtml += '<span style="background-color: ' + color.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
        }
        cellHtml += '<b class="overlay">&nbsp;</b></span></li>';
        html += cellHtml;
      }
      
      Element.update(cp, html);
      
      //choose selected colorcolor_cell
      var f = this.colorField;
      if(f != null) {
        var selectedColorId = cProduct.getSelectedColorId();
        var cpc = $("oms_color_" + selectedColorId);
        if(cpc==null) {
          log("Unable to get selected color cell for color " + selectedColorId);
        } else {
          cpc.className += " selected";
          this.selectedColorCellForOMS = cpc;
        }
      }
      //bind the color cells
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        var cpc = $("oms_color_" + color.id);
        this.bindColorCell(cpc, color);
      }
    }
  },
  
  buildColorPanel: function PT_buildColorPanel(cProduct) {
    var cp = this.getColorPanel();
    
    var html = '';
    
    if(cp != null) {
      var count = 0;
      var colors = cProduct.getAvailableColorList();
      
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        //log(color);
        var int_title="";
        for(var k=0; k < color.colors.length; k++) {
          int_title+= color.colors[k][1] + ' ';
        }
        var cellHtml = '<li><a href="#" class="color_panel_cell" title="'+int_title+'" id="cpc_' + color.id + '" onmousemove="d.colorMouseMove(this, ' + color.id + ');" onmouseout="d.colorMouseOut(this, ' + color.id + ');">';
        var h = parseInt(20 / color.colors.length, 10);
        for(var j=0; j < color.colors.length;j++) {
          cellHtml += '<span style="background-color: ' + color.colors[j][0] + '; height: '+h+'px;">&nbsp;</span>';
        }
        cellHtml += "<b>&nbsp;</b></a></li>";
        html += cellHtml;
        count += 1;
      }
     // html += "<!--</ul>-->";
      
      log("setting color panel");
      //log(html);
      Element.update(cp, html);
      log("setting color panel- DONE");
      
      //choose selected colorcolor_cell
      var f = this.colorField;
      if(f != null) {
        var c = null;
        var selectedColorId = cProduct.getSelectedColorId();
        var cpc = $("cpc_" + selectedColorId);
        if(cpc==null) {
          log("Unable to get selected color cell for color " + selectedColorId);
        } else {
          cpc.className += " selected";
          this.selectedColorCell = cpc;
        }
      }
      //bind the color cells
      for(var i = 0; i < colors.length;i++) {
        var color = colors[i];
        var cpc = $("cpc_" + color.id);
        this.bindColorCell(cpc, color);
      }
    }
  },
  
  bindColorCell: function PT_bindColorCell(el, color) {
    var self = this;
    el.onclick = function() {
      if(self.selectedColorCell!=null) {
        self.selectedColorCell.className = self.selectedColorCell.className.replace(" selected", "");
      }
      d.track("change-product-color");
      self.selectedColorCell = el;
      self.selectedColorCell.className = self.selectedColorCell.className+ " selected";
      d.currentCProduct.selectColor(color);
      //self.updateProductViews(true, true);
      d.itemChanged();
      return false;
    };
  },
  
 
  
  //select this product type
  //cProduct: the current configured product..  if different type than this then set product to first product of this type
  //doUpdate: update the designer view
  //doSelect: select the first item 
  selectConfiguredProduct: function PT_selectConfiguredProduct(cProduct, doUpdate, doSelect, productId) {
    log("Selecting Configured Product", true);
    d.deSelectCurrentItem();
    var rebuildImages = false;
    if(cProduct.product == null || 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(productId==null) productId = cat.products[0].id;
      
      var new_prod = d.getProduct(productId, null, null);
      if(new_prod == null) {
        log("product " + productId + " not cached.. getting now..");
        var self = this;
        d.getProduct(productId, function() {
          self.selectConfiguredProduct(cProduct, doUpdate, doSelect, productId);
        } , "m_apparel_pane");
        return;
      }
      if(cProduct.product) {
        var moveResult = cProduct.productChangeWillMatch(new_prod);
        if(moveResult != 0) {
          var warning = moveResult==2 ? ml("Some of the artwork you have done cannot be used on the product you have selected. Are you sure you want to change the product?") : ml("None of the artwork you have done can be used on the product you have selected. Are you sure you want to change the product?") ;
          
          if(!confirm(warning)) {
            this.showProductAsSelected( cProduct.product);
            return;
          }
        }
        d.deSelectCurrentItem();
      }
      cProduct.setProduct(new_prod);
      rebuildImages = true;
      cProduct.price = null;
      
    }
    
    var productCaption = $("left_title");
    if(productCaption!= null) {
      if (cProduct.usingCustomProduct()) {
        productCaption.innerHTML = cProduct.customProduct.name;
      } else {
        productCaption.innerHTML = cProduct.product.name;
      }
    }
    
    // update breadcrumb
    /*
    if (d.mode == DESIGNER_MODE_SHOP) {
      if (cProduct.usingCustomProduct()) {
        d.updateBreadCrumb(null, cProduct.customProduct.id);
      } else {
        d.updateBreadCrumb(cProduct.product.id);
      }
    }
    */
    
    /*
    DNC-4007 - we now want this back all the time...
    var previewEl = $("show_preview");
    if(previewEl != null) {
      if(cProduct.product.hasDerivedViews) {
        previewEl.show();
      } else {
        previewEl.hide();
      }
    }
    */
    
    cProduct.updateViewListHtml();
    this.buildColorPanel(cProduct);
    log("this.buildColorPanel");
    this.setFields(cProduct);
    log("this.setFields");
    //select the category etc...
    var cat = this.catsById[cProduct.product.categoryId];
    if((cat != this.currentCategory)&&(cat!=null)&&(this.currentCategory!=null)&&(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT)) {
      this.markCategorySelected(0); //move to 'all'
      this.selectCategory(0, cProduct.product.id); //render 'all' listing
      this.selectProductInListing(cProduct.product.id); //visually mark as selected
      if(doUpdate) {
        d.selectCurrentView(cProduct.getFirstSelectedViewId(), null);
        if(doSelect) {
          if(!d.currentCViewArea.findAndSelectItem()) {
            d.selectTab('m','customize');
          }
        }
        this.showEditing();
      }
      this.doShow();
    } else if(doUpdate) {
      this.selectProductInListing(cProduct.product.id); //visually mark as selected
      d.selectCurrentView(cProduct.getFirstSelectedViewId(), null);
      if(doSelect) {
        if(!d.currentCViewArea.findAndSelectItem()) {
          d.selectTab('m','customize');
        }
      }
      this.showEditing();
      this.doShow();
    } else {
      this.doShow();
    }
    if(cProduct.product.usesBundles()) {
      $("c_bun_info").show();
      $("c_min_qty").innerHTML=cProduct.getMinQty();
      $("c_bun").innerHTML=cProduct.product.bundleSize;
      $("qty_container").className = "qty_dropdown";
      $("c_bun_container").show();
    } else if(cProduct.product.usesMinQty()) {
      $("c_min_qty").innerHTML=cProduct.getMinQty();
      $("c_bun_container").hide();
      $("c_bun_info").show();
    } else {
      $("c_bun_info").hide();
      $("qty_container").className = "";
    }
    var qty = cProduct.checkQty(cProduct.qty, true, true);
    cProduct.updateQty(qty, false, d.inOrderManager);
    cProduct.refreshQty();
    
    
    this.updatePrice();
    
    d.clearCopyState();
    
  },
  
  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);
      }
    }
  },
  
  setOptions: function PTF_setOptions(cProduct, optionsContainer, listContainer) {
    html = '';
    if (this.customFields.list.length > 0) {
      html += '<div class="custom_fields">';
      for(var i=0;i< this.customFields.list.length;i++) {
        var field = this.customFields.list[i];
        html +=
        '<div class="custom_field" id="pt_fc_' + field.id + '" style="display:none;">' +
          '<label>' +
            '<span class="help" id="cf_lbl_' + field.id + '">' + field.name + '</span>' +
            '<b class="tip" id="cf_tip_' + field.id + '">' +
              '<b>' + field.description + '</b>' +
            '</b>' +
          '</label>' +
          '<div id="pt_foc_' + field.id + '" class="custom_option"></div>' +
        '</div>';
      }
      html += '</div>';
      optionsContainer.update(html);
      
      for(var i=0;i< this.customFields.list.length;i++) {
        this.customFields.list[i].setField(cProduct, true);
      }
      
      listContainer.show();
    } else {
      listContainer.hide();
    }
  },
  
  setFieldsForOMS: function PTF_setFieldsForOMS(cProduct, optionsContainer, popupId) {
    
    html = '';
        
    if (this.customFields.list.length > 0) {
      html += '<div class="custom_fields">';
      for(var i=0;i< this.customFields.list.length;i++) {
        var field = this.customFields.list[i];
        html +=
        '<div class="custom_field" id="oms_pt_fc_' + field.id + '_' + popupId + '" style="display:none;">' +
          '<label>' +
            '<span class="help" id="oms_cf_lbl_' + field.id + '_' + popupId + '">' + field.name + '</span>' +
            '<b class="tip" id="oms_cf_tip_' + field.id + '_' + popupId + '">' +
              '<b>' + field.description + '</b>' +
            '</b>' +
          '</label>' +
          '<div id="oms_pt_foc_' + field.id + '_' + popupId + '" class="custom_option"></div>' +
        '</div>';
      }
      html += '</div>';
      optionsContainer.update(html);
      
      for(var i=0;i< this.customFields.list.length;i++) {
        this.customFields.list[i].setFieldForOMS(cProduct, popupId);
      }
    }
  },
  
  getFirstLoadedCat: function PT_getFirstLoadedCat() {
    for(var i=0; i < this.cats.length; i++) {
      if(this.cats[i].products != null) {
        return this.cats[i];
      }
    }
    return null;
  },
  
  //the user has selected a different product from the product list
  changeSelectedProduct: function PT_changeSelectedProduct(productId) {
    d.deSelectCurrentItem();
    var p = d.currentCProduct;
    var self = this;
    d.track("change-selected-product");
    d.getProduct(productId, function(product) {
        
      
      log("changeSelectedProduct callback");
      var moveResult = p.productChangeWillMatch(product);
      if(moveResult != 0) {
        var warning = moveResult==2 ? ml("Some of the artwork you have done cannot be used on the product you have selected. Are you sure you want to change the product?") : ml("None of the artwork you have done can be used on the product you have selected. Are you sure you want to change the product?") ;
        
        if(!confirm(warning)) {
          //cat.selectProduct(cProduct.product.id);
          self.showProductAsSelected( p.product);
          return;
        }
      }
      d.deSelectCurrentItem();
      self.showProductAsSelected(product);
      p.setProduct(product);
      log("p.setProduct(product);");
      self.selectConfiguredProduct(p,false, false);
      p.afterProductChanged();
      log("self.selectConfiguredProduct(p,false, false);");
      self.updateProductDetails(true);
      
      d.selectCurrentView(p.getFirstSelectedViewId(), null);
      //self.updateProductViews(true);
      log("changeSelectedProduct callback DONE");
    }, $("pt_info_" + this.id));
  },
  
  //show a product as selected on product selector.. assumes the current product type/cat is the same...if not do nothing
  showProductAsSelected: function PT_showProductAsSelected(product) {
    if(d.mode == DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) return; //viewing custom product...
    if(this.id != product.type.id) {
      return;
    }
    this.selectProductInListing(product.id);
  },
  
  updateProductDetails: function PT_updateProductDetails() {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    var p = d.currentCProduct.product;
    var img = $("pt_i_" + this.id);
    if(img != null) setImageUrl(img, img, p.getDisplayImageURL(), null);
    //$("pt_i_" + this.id).src = p.getViewURL(p.product.defaultView.id, 2);
    var el = $("pt_c_" + this.id);
    if(el != null) el.innerHTML = p.name;
    var el = $("pt_d_" + this.id);
    if(el != null) el.innerHTML = p.description;
  },
  
  
  deselect: function PT_deselect() {
    if (this.container != null) this.container.hide();
    //this.views.style.display = "none"; 
    if (this.dataContainer != null) this.dataContainer.hide();
    if (this.fieldsContainer != null) this.fieldsContainer.hide();
    if (this.catSelectorContainer != null) this.catSelectorContainer.hide();
    var cp = this.getColorPanel();
    if(cp != null) cp.hide();
  },
  
  //show the product type information, calling the server if we dont have it locally yet...
  select: function PT_select(callback, selectedCategoryId, productId) {
    if (!this.typeSelector) this.typeSelector = $('sel_prod_type');
    if (this.typeSelector) this.typeSelector.value = this.id;
    
    if(d.subMode == DESIGNER_SUBMODE_QUOTE) {
      callback();
      return;
    }
    
    if(this.boundElements) {
      if(!this.container) {
        callback();
        return;
      }
      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(this.fieldsContainer != null) {
      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($("product_category_container") != null) {
        if(this.noCategores) {
          $("product_category_container").hide();
        } else {
          $("product_category_container").show();
        }
      }
    }
  },

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

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

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

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

var ProductTypeOption = Class.create({
    CLASSDEF: {
        name: 'ProductTypeOption'
    },
  
  initialize: function PTO_initialize(id, options, field) {
    this.id = id;
    this.name = options.n;
    this.value = options.c;
    this.isMulti = options.m;
    this.defaultPrices = options.pta;
    this.field = field;
    this.discontinued = options.dis;
    this.defaultOn = options.defo;
    this.defaultSelected = options.defs;
    this.pos = options.p;
    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.discontinued = options.dis;
    this.defaultOn = options.defo;
    this.defaultSelected = options.defs;
    this.pos = options.p;
  }/*,
  
  getDelta: function PTSO_getDelta(product, color_type) {
    var fc = product.optionsByPoId[this.id];
    if(fc != null) {
      return fc.delta[color_type];
    }
    return 0;
  },
  
  getFieldChoice: function PTSO_getFieldChoice(product) {
    return product.optionsByPoId[this.id];
  }*/
});

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

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

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

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

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

