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

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

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

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