var StampRenditions = {
  renditions: [],
  observers: [],

  remove: function(element) {
    var rendition = StampRenditions.getByElem(element);
    if (!rendition) return null;
    
    if (rendition instanceof Mblst.TextStampRendition) {
      TextStampRenditions.remove(element);
    } else {
      ImageStampRenditions.remove(element);
    }
  },

  getByElem: function(element) {  
    return this.renditions.detect( function(r) { return r.containerElem.id == $(element).id });
  },
  
  register: function(rendition, sendToBack) {
    if (sendToBack) {
      this.renditions.unshift(rendition);
    } else {
      this.renditions.push(rendition);
    }
  },
  
  unregister: function(rendition) {
    this.renditions = this.renditions.reject(function(r) { return r==rendition });
  },

  toJSON: function() {
    var jsonArr = [];
    this.renditions.inject(jsonArr, function(arr, r){
      if (r.active && !((r instanceof Mblst.TextStampRendition) && !r.text)) arr.push(r.toJSON().evalJSON(true));
      return arr;
    });
    return jsonArr.toJSON();
  }
  
}



var StampGalleries = new Object();
StampGalleries.Gallery = Class.create();
StampGalleries.Gallery.prototype = {
  initialize: function(element, options) {
    this.element = $(element);
    this.paginator = null;
    this.filter = null;

    this.options = Object.extend({
      indicator: null,
      
      paginationClassName: 'pagination',
      paginationLinkClassName: 'simple',
      filterClassName: 'filter',
      filterAllClassName: 'all',
      filterTagClassName: 'tag',
      galleryClassName: 'stampGallery',
      
      count: 20,
      conciseCount: 50,
      orientation: 'landscape',
      categories: function(orientation) {return ''},
      type: 'stamp',
      loadOnInit: false,
      alwaysReload: false,
      initConcise: true,
      view: 'stamp_gallery_image',
      conciseView: 'stamp_gallery_image_concise',
      defaultFilterElem: null,
      
      altBaseUrl: null
      
    }, options || {});
    
    this.create();
    
  },
  
  create: function() {
    var paginationLinks = [];
    var paginationControls = ($$CN(this.element, this.options.paginationClassName) || []);
    paginationLinks[0] = ($$CN(paginationControls[0], this.options.paginationLinkClassName) || []);
    paginationLinks[1] = ($$CN(paginationControls[1], this.options.paginationLinkClassName) || []);
    var filterControl = $$CN(this.element, this.options.filterClassName).first();
    var thumbContainer = $$CN(this.element, this.options.galleryClassName).first();

    var initView = (this.options.initConcise) ? this.options.conciseView : this.options.view;
    var initCount = (this.options.initConcise) ? this.options.conciseCount : this.options.count;
    var initCategories = this.options.categories(this.options.orientation);
    var initBaseUrl = (this.options.altBaseUrl ||
                       Mblst.Url.publicstampC(initCategories, this.options.type));
    
    this.paginator = new Mblst.Paginate(
      thumbContainer,
      Mblst.Url.pageBase(initBaseUrl),
      null,
      initCount, {
        loadOnInit: false,
        alwaysReload: this.options.alwaysReload,
        useLoadingTriggerRefY: true,
        prevElem: paginationLinks[0][0] || null,
        nextElem: paginationLinks[0][1] || null,
        auxPrevElem: paginationLinks[1][0] || null,
        auxNextElem: paginationLinks[1][1] || null,
        indicatorObj: this.options.indicator,
        mblstReqOptions: { view: initView},
        ajaxOptions: {method: 'get'}
    });
    
    if(filterControl) {
      this.filter = new Mblst.PaginateFilter(
        this.paginator,
        Mblst.UrlBuilder.stampFilterPage(Mblst.Url.publicstampC, initCategories, this.options.type),
        $$CN(filterControl, this.options.filterAllClassName).first(),
        $$CN(filterControl, this.options.filterTagClassName),
        {defaultFilterElem: this.options.defaultFilterElem});      
    }
    
    // We need to handle load on init ourselves, since we don't want
    // the paginator to load if we will be adding a filter.
    if (this.options.loadOnInit) this.paginator.load();
    
  },
  load: function() {
    this.paginator.load();
  },
  show: function() {
    Element.show(this.element);
  },
  hide: function() {
    Element.hide(this.element);
  },  
  setFullMode: function(options) {
    this.paginator.clear();
    this.options = Object.extend(this.options, options || {});
    this.paginator.options.mblstReqOptions.view = this.options.view;
    var categories = this.options.categories(this.options.orientation);
    
    if (this.filter) {
      this.filter.setUrlBuilder(
        Mblst.UrlBuilder.stampFilterPage(Mblst.Url.publicstampC, categories, this.options.type),
        1,
        this.options.count
      );
    } else {
      var baseUrl = (this.options.altBaseUrl || Mblst.Url.publicstampC(categories, this.options.type));
      this.paginator.setUrl(
        Mblst.Url.pageBase(baseUrl), 1, this.options.count);
    }
  },
  setConciseMode: function(options) {
    this.paginator.clear();
    this.options = Object.extend(this.options, options || {});
    this.paginator.options.mblstReqOptions.view = this.options.conciseView;
    
    var categories = this.options.categories(this.options.orientation);  

    if (this.filter) {
      this.filter.setUrlBuilder(
        Mblst.UrlBuilder.stampFilterPage(Mblst.Url.publicstampC, categories, this.options.type),
        1,
        this.options.conciseCount
      );
    } else {
      var baseUrl = (this.options.altBaseUrl || Mblst.Url.publicstampC(categories, this.options.type));
      this.paginator.setUrl(
        Mblst.Url.pageBase(baseUrl), 1, this.options.conciseCount);
    }
  }
}

Object.extend(StampGalleries, {
   
  galleries: false,

  getByElem: function(element) {  
    return StampGalleries.galleries.detect( function(g) { return $E(g) == $(element) });
  },
  
  getAll: function() {
    return StampGalleries.galleries;
  },
  
  add: function(gallery) {
    StampGalleries._add(gallery);
  },
  
  remove: function(gallery) {
    if (!StampGalleries.galleries) return;
    StampGalleries._remove(gallery);
  },
  
  _add: function(gallery) {
    if (!this.galleries) this.galleries = [];
    this.galleries.push(gallery);
  },   
  
  _remove: function(gallery) {
    this.galleries = this.galleries.reject( function(g) { return g==gallery });
  }
  
});

Mblst.BlingEditor = Class.create();
Mblst.BlingEditor.prototype = {
  /**
   * Constructor
   */
  initialize: function(controlsElem, previewElem, wasteElem, headElem, form, options) {
    this.controlsElem = $(controlsElem);
    this.leftElem = this.controlsElem.parentNode;
    this.previewElem = $(previewElem);
    this.wasteElem = $(wasteElem);
    this.headElem = $(headElem);
    this.form = $(form);
    this.exampleThumbContainer = null;
    this.history = [];
    this.currentMode = null;
    this.lastMode = null;
    this.currentStamp = null;
    this.examplePhotoShown = false;
    this.firstStampSelected = false;

    this.options = Object.extend({
      moveModeClassName: 'moveMode',
      stampModeClassName: 'stampMode',
      textAddModeClassName: 'textAddMode',
      textEditModeClassName: 'textEditMode',
      previewModeClassName: 'previewMode',
      stampSlotClassName: 'stampSlot',
      stampThumbClassName: 'stampThumb',
      currentStampClassName: 'currentStamp',
      textStampClassName: 'textStamp',
      selectedModeClassName: 'selected',
      previewImageClassName: 'previewImage',
      
      saveButtonText: 'Save',
      
      exampleThumbContainer: 'bling_editor_left_examples',
      exampleShownClassName: 'example',
      examplePhotoSrc: '/images/stampex.gif',
      emptyStampSlotSrc: '/images/ltgraypix.gif',
      examplePhotoW: 300,
      examplePhotoH: 300,

      onClickSubmit: function(editor) {
        editor.submit();
      },
      
      onStampDrag: function(draggable, event) {
        if (this.stampMode) {
          var d = draggable.currentDelta();
          //draggable.options.reverteffect(draggable.element, d[1]-draggable.delta[1], d[0]-draggable.delta[0]);
          draggable.finishDrag(event, false);
          //Event.stop(event);
        }
      }.bind(this),

      stampMax: 40
    }, options || {});
    
    this.create();
    
    Droppables.add(this.wasteElem, {accept: this.options.stampSlotClassName, onDrop: this.onTrash.bind(this), hoverclass:'wastebin-active'});
    Droppables.add(this.wasteElem, {accept: this.options.textStampClassName, onDrop: this.onTrashText.bind(this), hoverclass:'wastebin-active'});
    Droppables.add(this.previewElem, {accept: this.options.stampThumbClassName, onDrop: this.onDropThumb.bind(this)});

    this.onclickPreviewListener = this.onclickPreview.bindAsEventListener(this);
    Event.observe(this.previewElem, 'click', this.onclickPreviewListener);
  },

  /**
   * Control methods
   */
  create: function() {

    // determine if have a real photo or are showing an example
    if (this.previewElem.style.backgroundImage.match(/stampex\.gif/)) {
      this.examplePhotoShown = true;
    }
    if (this.options.exampleThumbContainer)
      this.exampleThumbContainer = $(this.options.exampleThumbContainer);
    
    // add controls    
    this.stampModeButton = document.createElement("a");
    this.stampModeButton.href = "javascript: void(0)";
    this.stampModeButton.id = "bling_editor_stamp_link";
    this.controlsElem.appendChild(this.stampModeButton);
    Element.hide(this.stampModeButton);
    //Event.observe(this.stampModeButton, 'mouseup', this.stampModeButton.blur.bind(this.stampModeButton));

    this.moveModeButton = document.createElement("a");
    this.moveModeButton.href = "javascript: void(0)";
    this.moveModeButton.id = "bling_editor_move_link";
    this.controlsElem.appendChild(this.moveModeButton);
    //Event.observe(this.moveModeButton, 'mouseup', this.moveModeButton.blur.bind(this.moveModeButton));

    this.textAddModeButton = document.createElement("a");
    this.textAddModeButton.href = "javascript: void(0)";
    this.textAddModeButton.id = "bling_editor_text_add_link";
    this.controlsElem.appendChild(this.textAddModeButton);
    //Element.hide(this.textAddModeButton);

    var modeIndicatorText = document.createTextNode("choose a photo below");
    this.modeIndicator = document.createElement("div");
    this.modeIndicator.id = "bling_editor_mode_indicator";
    this.modeIndicator.appendChild(modeIndicatorText);
    this.controlsElem.appendChild(this.modeIndicator);
    Element.hide(this.modeIndicator);
    
    var stampPreviewHeader = document.createTextNode("Current stamp");
    this.stampPreviewImg = document.createElement("img");
    this.stampPreviewImg.src = this.options.emptyStampSlotSrc;
    this.stampPreviewImg.alt = "";
    this.stampPreviewImg.id = "bling_editor_stamp_preview_img";    
    this.stampPreview = document.createElement("div");
    this.stampPreview.id = "bling_editor_stamp_preview";
    this.stampPreview.appendChild(stampPreviewHeader);
    this.stampPreview.appendChild(this.stampPreviewImg);
    this.controlsElem.appendChild(this.stampPreview);
    Element.hide(this.stampPreview);

    this.resetButton = document.createElement("input");
    this.resetButton.type = "button";
    this.resetButton.value = "Reset";
    this.resetButton.id = "bling_editor_reset_button";
    this.controlsElem.appendChild(this.resetButton);
    Element.hide(this.resetButton);
    Form.Element.disable(this.resetButton);

    this.undoButton = document.createElement("input");
    this.undoButton.type = "button";
    this.undoButton.value = "Undo";
    this.undoButton.id = "bling_editor_undo_button";
    this.controlsElem.appendChild(this.undoButton);
    Element.hide(this.undoButton);
    Form.Element.disable(this.undoButton);

    var hr = document.createElement("hr");
    this.controlsElem.appendChild(hr);

    this.saveButton = document.createElement("input");
    this.saveButton.type = "button";
    this.saveButton.value = this.options.saveButtonText;
    this.saveButton.id = "bling_editor_submit";
    this.controlsElem.appendChild(this.saveButton);

    // Register DOM event callbacks
    Event.observe(this.stampModeButton, 'click', this.onclickStampMode.bind(this));
    Event.observe(this.moveModeButton, 'click', this.onclickMoveMode.bind(this));
    Event.observe(this.textAddModeButton, 'click', this.onclickTextAddMode.bind(this));
    Event.observe(this.resetButton, 'click', this.onclickReset.bind(this));
    Event.observe(this.undoButton, 'click', this.onclickUndo.bind(this));
    Event.observe(this.saveButton, 'click', this.onclickSubmit.bind(this));
    
    // Register ControlEvent callbacks
    this.textShoweditListener = this.onclickTextEditMode.bindAsEventListener(this);
    this.textDismissListener = this.onTextEditDismiss.bindAsEventListener(this);
    ControlEvent.observe(Mblst.TextStampRendition, 'showedit', this.textShoweditListener);
    ControlEvent.observe(Mblst.TextStampRendition, 'update', this.textDismissListener);
    ControlEvent.observe(Mblst.TextStampRendition, 'cancel', this.textDismissListener);
        
    Element.removeClassName(this.controlsElem, "loading");
    Element.show(this.modeIndicator);
    Element.show(this.undoButton);
    Element.show(this.resetButton);
  },
  show: function() {
    Element.show(this.leftElem);
    Element.show(this.headElem);
    Element.show(this.previewElem);
	Element.show(this.wasteElem);  	
  },
  hide: function() {
    Element.hide(this.leftElem);
    Element.hide(this.headElem);
    Element.hide(this.previewElem);
	Element.hide(this.wasteElem);  	
  },
  showExamplePreview: function() {    
    this.previewElem.style.width = this.options.examplePhotoW+'px';
    this.previewElem.style.height = this.options.examplePhotoH+'px';
    this.previewElem.style.backgroundImage = 'url("'+this.options.examplePhotoSrc+'")';

    $('i_owner_partner_user_id').value = '';
    $('i_temp_photo_url').value = '';
    
    Element.addClassName(this.previewElem.parentNode.parentNode, this.options.exampleShownClassName);
    this.examplePhotoShown = true;
  },  
  showPhotoPreview: function(url, ownerPartnerUserID, origW, origH) {
    //var dims = Dimension.getSquareInscribedDims(origW, origH, 300);
    
    //this.previewElem.style.width = dims.scaledWidth+'px';
    //this.previewElem.style.height = dims.scaledHeight+'px';
    this.previewElem.style.width = '350px';
    this.previewElem.style.height = '350px';		
    this.previewElem.style.backgroundImage = 'url("'+url+'")';
        
    $('i_owner_partner_user_id').value = ownerPartnerUserID;
    $('i_temp_photo_url').value = url;
    
    Element.removeClassName(this.previewElem.parentNode.parentNode, this.options.exampleShownClassName);
    this.examplePhotoShown = false;    
  },
  setMoveMode: function() {
    this.currentMode = 'move';
    this.wasteElem.innerHTML = "Drag Here &nbsp;&nbsp;To&nbsp;&nbsp; Remove";
    this.modeIndicator.innerHTML = "drag stamps to arrange";
    Element.addClassName(this.previewElem.parentNode, this.options.moveModeClassName);
    Element.addClassName(this.moveModeButton, this.options.selectedModeClassName);
    Element.show('bling_editor_head_move');
    ControlEvent.notify('movemode', this);
  },
  setStampMode: function() {
    this.currentMode = 'stamp';
    if (!this.firstStampSelected) this.onFirstStampSelected();
    this.modeIndicator.innerHTML = "click photo to add stamps";
    Element.addClassName(this.previewElem.parentNode, this.options.stampModeClassName);
    Element.addClassName(this.stampModeButton, this.options.selectedModeClassName);
    Element.show('bling_editor_head_stamp');
    this.stampPreview.style.visibility = "visible";
    ControlEvent.notify('stampmode', this);
  },
  setTextEditMode: function() {
    this.currentMode = 'text_edit';
    this.wasteElem.innerHTML = "Drag Here &nbsp;&nbsp;To&nbsp;&nbsp; Remove";
    this.modeIndicator.innerHTML = "";
    Element.addClassName(this.previewElem.parentNode, this.options.textEditModeClassName);  
    Element.show('bling_editor_head_text_edit');
    new Effect.Highlight('bling_editor_head_text_edit');
  },
  setTextAddMode: function() {
    this.currentMode = 'text_add';
    this.modeIndicator.innerHTML = "click photo to add text";
    Element.addClassName(this.previewElem.parentNode, this.options.textAddModeClassName);
    Element.addClassName(this.textAddModeButton, this.options.selectedModeClassName);
    Element.show('bling_editor_head_text_add');
    ControlEvent.notify('textaddmode', this);
  },  
  setPreviewMode: function() {
    this.currentMode = 'preview';
    Element.addClassName(this.previewElem.parentNode, this.options.previewModeClassName);
    this.leftElem.style.visibility = "hidden";
    Element.hide(this.wasteElem);
    Element.show('bling_editor_head_preview');
  },  
  gotoLastMode: function() {
    var mode = this.lastMode;
    this.clearCurrentMode();
    this.setMode(mode);
  },
  setMode: function(mode) {
    if (mode == 'move') {
      this.setMoveMode();
    } else if (mode == 'stamp') {
      this.setStampMode();
    } else if (mode == 'text_edit') {
      this.setTextEditMode();
    } else if (mode == 'text_add') {
      this.setTextAddMode();      
    } else if (mode == 'preview') {
      this.setPreviewMode();
    }    
  },  
  clearCurrentMode: function() {
    if (this.currentMode == 'move') {
      this.clearMoveMode();
    } else if (this.currentMode == 'stamp') {
      this.clearStampMode();
    } else if (this.currentMode == 'text_edit') {
      this.clearTextEditMode();
    } else if (this.currentMode == 'text_add') {
      this.clearTextAddMode();      
    } else if (this.currentMode == 'preview') {
      this.clearPreviewMode();      
    } else {
      this.currentMode = null;
    }
  },
  clearMoveMode: function() {
    this.lastMode = this.currentMode;    
    Element.removeClassName(this.moveModeButton, this.options.selectedModeClassName);
    Element.removeClassName(this.previewElem.parentNode, this.options.moveModeClassName);
    this.currentMode = null;
    this.wasteElem.innerHTML = "";
    Element.hide('bling_editor_head_move');
  },
  clearStampMode: function() {
    this.lastMode = this.currentMode;    
    Element.removeClassName(this.stampModeButton, this.options.selectedModeClassName);
    Element.removeClassName(this.previewElem.parentNode, this.options.stampModeClassName);
    this.currentMode = null;
    this.stampPreview.style.visibility = "hidden";
    Element.hide('bling_editor_head_stamp');
  },
  clearTextEditMode: function() {
    this.lastMode = this.currentMode;    
    Element.removeClassName(this.previewElem.parentNode, this.options.textEditModeClassName);
    this.currentMode = null;
    this.wasteElem.innerHTML = "";
    Element.hide('bling_editor_head_text_edit');
  },
  clearTextAddMode: function() {
    this.lastMode = this.currentMode;
    Element.removeClassName(this.textAddModeButton, this.options.selectedModeClassName);
    Element.removeClassName(this.previewElem.parentNode, this.options.textAddModeClassName);
    this.currentMode = null;
    Element.hide('bling_editor_head_text_add');
  },  
  clearPreviewMode: function() {
    this.lastMode = this.currentMode;    
    Element.removeClassName(this.previewElem.parentNode, this.options.previewModeClassName);
    this.leftElem.style.visibility = "visible";
    Element.show(this.wasteElem);
    this.currentMode = null;
    Element.hide('bling_editor_head_preview');
  },  
  setOwnerUserId: function(uid) {
    $('i_owner_partner_user_id').value = uid;    
  },
  setPhotoDescription: function(desc) {
    $('i_partner_photo_description').value = desc;    
  },  
  setImageStamp: function(stamp) {
    if (this.currentStamp && this.currentStamp.triggerElem.id == stamp.triggerElem.id) return;
    
    this.stampPreviewImg.src = stamp.stampURL;
    this.stampPreviewImg.width = stamp.thumbW;
    this.stampPreviewImg.height = stamp.thumbH;
    if (!this.currentStamp) {
      Element.show(this.stampPreview);
    } else {
      Element.removeClassName(this.currentStamp.triggerElem, this.options.currentStampClassName);
    }
    this.currentStamp = stamp;
    Element.addClassName(this.currentStamp.triggerElem, this.options.currentStampClassName);    
    this.clearCurrentMode();
    this.setStampMode();
  },
  removeStamp: function(stampElem) {
    stampElem = $(stampElem);
    for (var i = 0; i < this.history.length; i++) {
      if (this.history[i] == stampElem.id) {
        this.history.splice(i, 1);
        break;
      }
    }
    StampRenditions.remove(stampElem);
    
    if (this.history.length == 0) {
      Form.Element.disable(this.undoButton);
      Form.Element.disable(this.resetButton);
    }    
  },
  stamp: function(xPageOffset, yPageOffset) {
    if (!this.currentStamp) return;
    if (this.history.length >= this.options.stampMax) mblstalert("You've added the max number of stamps! To add new stamps, you must remove others first.");
    
    var stampRendition = ImageStampRenditions.add(this.currentStamp, xPageOffset, yPageOffset);
    if (stampRendition.options.moveModeOnStamp) {
      this.clearCurrentMode();
      this.setMoveMode();      
    }
      
    if (this.history.length == 0) {
      Form.Element.enable(this.undoButton);
      Form.Element.enable(this.resetButton);
      if (stampRendition.options.moveModeOnStamp) new Effect.Highlight(this.headElem, {duration: 1.0});
    }
    this.history.push(stampRendition.containerElem.id);
  },
  textStamp: function(xPageOffset, yPageOffset) {
    if (this.history.length >= this.options.stampMax) mblstalert("You've added the max number of stamps! To add new stamps, you must remove others first.");

    var textOptions = Object.extend({
      independent: true,
      pageOffsetX: xPageOffset,
      pageOffsetY: yPageOffset
    }, this.options);

    var stampRendition = TextStampRenditions.add(textOptions);
    stampRendition.trigger();
      
    if (this.history.length == 0) {
      Element.show(this.moveModeButton);
      Form.Element.enable(this.undoButton);
      Form.Element.enable(this.resetButton);
    }
    this.history.push(stampRendition.containerElem.id);
  },  
  serialize: function() {
    return StampRenditions.toJSON();
  },
  submit: function() {
    if (!this.valAddBlingForm()) return false;
    this.form.submit();
    return false;
  },
  /**
   * Other callbacks
   */
  onFirstStampSelected: function() {
    Element.show(this.stampModeButton);
    if (this.exampleThumbContainer) Element.hide(this.exampleThumbContainer);
    Element.show(this.leftElem);
    Element.show(this.wasteElem);
    Element.show(this.headElem);
    new Effect.Highlight(this.headElem, {duration: 1.0});
    this.firstStampSelected = true;    
  },
  /**
   * Validators
   */
  valAddBlingForm: function() {
    var serialized = this.serialize();
    if (!serialized || serialized == "[]") {
			// JEC: Allowing submission w/o stamps
      //alert("You must add a stamp first!");
      //return false;
			$('i_stamp_params').value = '[]';
    } else {
      $('i_stamp_params').value = serialized;
    }
    return true;
  },  
  /**
   * ControlEvent callbacks
   */
  onTextEditDismiss: function(event) {
    var textRendition = ControlEvent.source(event);
    if((this.lastMode == 'text_edit') || (this.lastMode == 'text_add')) {
      this.clearCurrentMode();
      this.setMode('move');
    } else {
      this.gotoLastMode();
    }
    if (textRendition.text == textRendition.options.defaultStampText) {
      this.removeStamp(textRendition.containerElem.id);    
    }
  },
  /**
   * DOM Event handlers
   */
  onTrash: function(element, droppable, evt) {
    this.removeStamp($(element).id);
  },
  onTrashText: function(element, droppable, evt) {
    this.removeStamp($(element).id);
  },
  onDropThumb: function(element, droppable, evt) {
    var x = Event.pointerX(evt);
    var y = Event.pointerY(evt);
    this.stamp(x, y);
    this.clearCurrentMode();
    this.setMode('move');    
  },    
  onclickSubmit: function() {
    this.options.onClickSubmit(this);
    return false;
  },  
  onclickPreview: function(evt) {
    if (this.currentMode == 'stamp') {
      var x = Event.pointerX(evt);
      var y = Event.pointerY(evt);
      this.stamp(x, y);      
    } else if (this.currentMode == 'text_add') {
      var x = Event.pointerX(evt);
      var y = Event.pointerY(evt);
      this.textStamp(x, y);           
    } else {
      return false;
    }
  },
  onclickMoveMode: function() {
    this.clearCurrentMode();
    this.setMoveMode();
    return false;
  },
  onclickStampMode: function() {
    this.clearCurrentMode();
    this.setStampMode();
    return false;
  },
  onclickTextEditMode: function() {
    this.clearCurrentMode();
    this.setTextEditMode();
    return false;
  },
  onclickTextAddMode: function() {
    //this.clearCurrentMode();
    //this.setTextAddMode();
    //TODO: Fix this hardcode hack
    var pos = Position.page(this.previewElem);
    this.textStamp(pos[0]+170,pos[1]+230);
    return false;    
  },
  onclickUndo: function() {
    if (this.history.length == 0) return;
    
    var last = this.history.pop();
    if (last) this.removeStamp(last);
  },  
  onclickReset: function() {
    if (this.history.length == 0) return;
    
    var stampElem = this.history.pop();
    do {
      this.removeStamp(stampElem);
    } while (stampElem = this.history.pop());     
  }  
};

var AnimatedBlingStamps = {
  stamps: [],
  animator: null,
  previewElem: null,
  numActiveFrames: 1,
  lastFrameIdx: 0,
  
  maxFrames: 12,
  minFrameDelay: 0.16,
  activeClassPrefix: 'activeFrame',

  initialize: function(element) {
    this.previewElem   = $(element);
  },
  remove: function(stamp) {
    this.stamps = this.stamps.reject(function(d) { return d.containerElem==$(stamp.containerElem) });
    this.updateActiveFrameCount();
    if (this.stamps.length == 0 && this.animator) {
      this.animator.cancel();
      this.animator = null;
    }
  },
  add: function(stamp) {
    this.stamps.push(stamp);
    var numCanonicalFrames = stamp.frames.length * (stamp.options.frameDelay / 0.16);
    if(numCanonicalFrames != this.numActiveFrames) this.updateActiveFrameCount();
    if (!this.animator) this.animator = new Effect.FrameAnimator(this.update.bind(this));
  },
  updateActiveFrameCount: function() {
    var stampsLCM = this.stamps.inject(1, function(accum, s) {
      var numCanonicalFrames = s.frames.length * (s.options.frameDelay / 0.16);
      return lcm(accum, numCanonicalFrames);
    });
    this.numActiveFrames = Math.min(stampsLCM, this.maxFrames);
  },
  update: function() {
    var idx = (this.lastFrameIdx + 1) % this.numActiveFrames;
    this.previewElem.className = this.activeClassPrefix + idx;
    this.lastFrameIdx = idx;
  }
}

var ImageStampRenditions = {
  slots: null,
  numSlotsCreated: 0,
  slotBufferLength: 10,
  maxSlots: 40,
  previewElem: null,

  initialize: function(element) {
    this.previewElem   = $(element);
    
    // add stamp slots
    this.slots = [];
    for (var i=0; i < this.slotBufferLength ; i++) {
      slot = new Mblst.ImageStampRendition(this.previewElem, this.options);
      this.slots.push(slot);
      this.numSlotsCreated++;
    }
    this.slots.reverse();
  },
  add: function(stamp, xPageOffset, yPageOffset) {
    if((this.slots.length == 0) && !this.__increase()) return null;
    
    // if we have a frame/border, force bottom of the stack
    if (stamp.origW >= 300) {
      var rendition = new Mblst.ImageStampRendition(this.previewElem, {sendToBack: true});
      this.numSlotsCreated++;
    } else {
      var rendition = this.slots.pop();
    }
    rendition.activate(stamp, xPageOffset, yPageOffset);
    return rendition;
  },
  remove: function(element) {
    var rendition = StampRenditions.getByElem(element);
    if (!rendition) return null;
    
    rendition.deactivate();
    this.slots.push(rendition);
  },
  __increase: function() {
    if ((this.slots.length == 0) && (this.numSlotsCreated < this.maxSlots)) {
      for (var i=0; i < this.slotBufferLength ; i++) {
        slot = new Mblst.ImageStampRendition(this.previewElem, this.options);
        this.slots.push(slot);
        this.numSlotsCreated++;
      }
      this.slots.reverse();
      return true;
    } else {
      return false;
    }
  }
}



Mblst.ImageStampRendition = Class.create();
Mblst.ImageStampRendition.instanceNonce = 0;
Mblst.ImageStampRendition.prototype = {
  /**
   * Constructor
   */
  initialize: function(previewElem, options) {
    Mblst.ImageStampRendition.instanceNonce++;
    this.previewElem = $(previewElem);
    this.active = false;
    this.url = null;
    this.width = 0;
    this.height = 0;
    this.frames = [];
    this.sizeable = null;
    this.previewPos = null;
    this.previewDims = null;
    this.textRendition = null;
    this.textEditTrigger = null;

    this.options = Object.extend({
      sendToBack: false,
      
      hoverClassName: 'stampHover',
      staticStampClassName: 'stamp',
      animStampClassName: 'animStamp',
      stampSlotClassName: 'stampSlot',
      stampSlotImgClassName: 'stampSlotImg',
      stampFrameClassPrefix: 'stampFrame',
      
      stampSlotIdPrefix: 'image_stamp_slot_',
      emptyStampSlotSrc: '/images/ltgraypix.gif',
      iefixPNGSrc: '/images/spacer.png',
      
      globalMaxFrames: 12,
      pngFrameCount: 0,
      frameDelay: 0.16,
      
      stampText: null,
      stampTextTop: null,
      stampTextLeft: null,
      stampTextW: null,
      stampFontFamily: null,
      stampFontSize: null,
      stampFontColor: null,
      stampFontBold: null,
      stampFontItalic: null,
      
      autoPositionMinDim: 250

    }, options || {});
    this.lastOptions = this.options;
    
    this.create();
  },

  /**
   * Control methods
   */
  create: function() {       
    this.containerElem = document.createElement("div");
    this.containerElem.className = this.options.stampSlotClassName;
    this.containerElem.id = this.options.stampSlotIdPrefix + Mblst.ImageStampRendition.instanceNonce;
    
    this.addSlotImg(); // add one slot img
    
    if (this.options.sendToBack) {
      Element.prependChild(this.previewElem, this.containerElem);
    } else {
      this.previewElem.appendChild(this.containerElem);
    }
    this.sizeable = new Mblst.Sizeable(this.containerElem, {moveChange: this.onMove.bind(this), abortDragCondition: this._abortDragCondition});

    StampRenditions.register(this, this.options.sendToBack);

    // Create closure text rendition move callback
    this.textMoveendListener = this.onTextRenditionEndMove.bindAsEventListener(this);

    // Create closure text rendition remove callback
    this.textRemoveListener = this.onTextRenditionRemove.bindAsEventListener(this);
    
    Element.hide(this.containerElem);
    
    this.mouseoverListener = this.onEnterHover.bindAsEventListener(this);
    this.mouseoutListener = this.onLeaveHover.bindAsEventListener(this); 
    Event.observe(this.containerElem, 'mouseover', this.mouseoverListener);
    Event.observe(this.containerElem, 'mouseout', this.mouseoutListener);
  },
  addSlotImg: function() {   
    var slotImg = document.createElement("img");
    slotImg.src = this.options.emptyStampSlotSrc;
    slotImg.alt = "";
    slotImg.className = this.options.stampSlotImgClassName;
    slotImg.galleryimg = "no";
    this.containerElem.appendChild(slotImg);
  },
  getSingleSlotImg: function() {
    var slotImgs = Element.Class.childrenWith(this.containerElem, this.options.stampSlotImgClassName);
    return slotImgs[0];
  },
  getSlotImgs: function(numImgs) {
    var slotImgs = Element.Class.childrenWith(this.containerElem, this.options.stampSlotImgClassName);
    if (slotImgs.length < numImgs) {
      for (var i = 0; i < numImgs - slotImgs.length; i++) {
        this.addSlotImg();
      }
      slotImgs = Element.Class.childrenWith(this.containerElem, this.options.stampSlotImgClassName);
    }
    return slotImgs.slice(0, numImgs);
  },
  animSetup: function() {
    if (!this.options.pngFrameCount || !this.url || !this.url.match(/png\.gif$/)) return;

    var slotImgs = this.getSlotImgs(this.options.pngFrameCount);
    
    for (var i = 0; i < this.options.pngFrameCount; i++) {
      slotImgs[i].width = this.width;
      slotImgs[i].height = this.height;
      Element.removeClassName(slotImgs[i], this.options.staticStampClassName);
      Element.addClassName(slotImgs[i], this.options.animStampClassName);
      
      frameSrcURL = this.url.replace(/png\.gif$/, i + '.png');
      if(navigator.appVersion.indexOf('MSIE')>0) {
        slotImgs[i].src = this.options.iefixPNGSrc;
        slotImgs[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + frameSrcURL + "', sizingMethod='scale')";
      } else {
        slotImgs[i].src = frameSrcURL;
      }
      this.frames.push(slotImgs[i]);
    }
    var k = 0.16 / this.options.frameDelay;
    for (var i = 0; i < this.options.globalMaxFrames; i++) {
      idx = Math.floor(i*k) % this.options.pngFrameCount;
      Element.addClassName(this.frames[idx], this.options.stampFrameClassPrefix + i);
    }
    AnimatedBlingStamps.add(this);
  },  
  staticSetup: function() {
    // reinsert stamp
    //stamp.parentNode.appendChild(stamp);
    var slotImg = this.getSingleSlotImg();

    slotImg.width = this.width;
    slotImg.height = this.height;
    Element.removeClassName(slotImg, this.options.animStampClassName);
    Element.addClassName(slotImg, this.options.staticStampClassName);
   
    if(this.url.match(/\.png$/) && navigator.appVersion.indexOf('MSIE')>0) {
      slotImg.src = this.options.iefixPNGSrc;
      slotImg.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.url + "', sizingMethod=scale)";
    } else {
      slotImg.src = this.url;
    }
  },
  textSetup: function(xStampOffset, yStampOffset) {
    var xTextOffset = xStampOffset + this.options.stampTextLeft;
    var yTextOffset = yStampOffset + this.options.stampTextTop;

    var textOptions = Object.extend({
      stampOffsetX: xTextOffset,
      stampOffsetY: yTextOffset,
      injectionSuccessor: this.containerElem
    }, this.options);

    this.textRendition = TextStampRenditions.add(textOptions);
    
    // Register ControlEvent observer
    ControlEvent.observe(this.textRendition, 'moveend', this.textMoveendListener);
    ControlEvent.observe(Mblst.TextStampRendition, 'remove', this.textRemoveListener);
  },  
  activate: function(stamp, xPageOffset, yPageOffset) {
    this.active = true;
    this.url = stamp.stampURL;
    this.width = stamp.origW;
    this.height = stamp.origH;
    this._setOptions(stamp.options);

    if (!this.previewPos) this.previewPos = Position.cumulativeOffset(this.previewElem);
    if (!this.previewDims) this.previewDims = Element.getDimensions(this.previewElem);
    
    var xOffset = xPageOffset - (this.width/2.0);
    var yOffset = yPageOffset - (this.height/2.0);
    var isWide = ( this.width >= this.options.autoPositionMinDim ) ? true : false;
    var isTall = ( this.height >= this.options.autoPositionMinDim ) ? true : false;
    
    if (isWide && isTall) {
      xOffset = this.previewPos[0];
      yOffset = this.previewPos[1];
      this.width = this.previewDims.width;
      this.height = this.previewDims.height;
    } else if (isWide) {  
      xOffset = Math.max(this.previewPos[0], xOffset);
      xOffset = Math.min(this.previewPos[0] + this.previewDims.width - this.width, xOffset);
    } else if (isTall) {
      yOffset = Math.max(this.previewPos[1], yOffset);
      yOffset = Math.min(this.previewPos[1] + this.previewDims.height - this.height, yOffset);
      yOffset = Math.max(this.previewPos[1], yOffset);
    }

    if (this.options.pngFrameCount) {
      this.animSetup();
    } else {
      this.staticSetup();
    }

    this.containerElem.style.width = this.width+'px';
    this.containerElem.style.height = this.height+'px';

    if (this.options.stampTextW) {
      this.textSetup(xOffset, yOffset);
    }
      
    Position.clone( document.body, this.containerElem, { setHeight: false,
                                                         setWidth: false,
                                                         offsetLeft: xOffset,
                                                         offsetTop: yOffset });
    
    this.sizeable.initResizeHandle(stamp.options.canResize, stamp.options.resizeProportional);
      
    Element.show(this.containerElem);
    //StampRenditions.notify('onStamp', this);
  },
  deactivate: function() {
    if (!this.active) return;
    AnimatedBlingStamps.remove(this);
    
    if (this.textRendition) this.deactivateText();      
    
    var stampFrames = Element.Class.childrenWith(this.containerElem, this.options.stampSlotImgClassName);
    for (var i = 0; i < stampFrames.length; i++) {
      stampFrames[i].src = this.options.emptyStampSlotSrc;
      stampFrames[i].style.filter = "";
      stampFrames[i].width = 1;
      stampFrames[i].height = 1;
      stampFrames[i].className = this.options.stampSlotImgClassName;
    }
    
    this.active = false;
    this.url = null;
    this.width = 0;
    this.height = 0;
    this.frames = [];
    this.executer = null;
    this._resetOptions();
    Element.hide(this.containerElem);
  },
  deactivateText: function() {
    if (this.textRendition) {
      // Remove observers first to avoid an infinite loop.
      ControlEvent.stopObserving(this.textRendition, 'moveend', this.textMoveendListener);
      ControlEvent.stopObserving(Mblst.TextStampRendition, 'remove', this.textRemoveListener);      
      TextStampRenditions.remove(this.textRendition.containerElem);
      this.textRendition = null;      
    }    
  },
  getOffsets: function() {
    return (this.containerElem) ? Position.positionedOffset(this.containerElem) : null;
  },
  toJSON: function() {
    var offsets = Position.positionedOffset(this.containerElem);
    var ret = {
      url: this.url,
      xcoord: offsets[0],
      ycoord: offsets[1],
      width: parseInt(Element.getStyle(this.containerElem,'width') || this.width),
      height: parseInt(Element.getStyle(this.containerElem,'height') || this.height)
    };
    return Object.toJSON(ret);
  },  
  /**
   * DOM Event handlers
   */
  onEnterHover: function() {
    Element.addClassName(this.containerElem, this.options.hoverClassName);
  },  
  onLeaveHover: function() {
    Element.removeClassName(this.containerElem, this.options.hoverClassName);
  },
  /**
   * ControlEvent callbacks
   */
  onTextRenditionEndMove: function(event) {
    var rendition = ControlEvent.source(event);
    var imageStampOffsets = Position.positionedOffset(this.containerElem);
    var textStampOffsets = Position.positionedOffset(rendition.containerElem);
    this.options.stampTextLeft = textStampOffsets[0] - imageStampOffsets[0];
    this.options.stampTextTop = textStampOffsets[1] - imageStampOffsets[1];
  },
  onTextRenditionRemove: function(event) {
    var rendition = ControlEvent.source(event);
    if(this.textRendition && (rendition == this.textRendition)) this.deactivateText();
  },
  /**
   * Other callbacks
   */  
  onMove: function(draggable) {
    if(this.textRendition) {
      var imageStampLeft = parseInt(Element.getStyle(draggable.element,'left') || '0');
      var imageStampTop = parseInt(Element.getStyle(draggable.element,'top') || '0');
      this.textRendition.containerElem.style.left = (imageStampLeft + this.options.stampTextLeft)+'px';
      this.textRendition.containerElem.style.top = (imageStampTop + this.options.stampTextTop)+'px';
    }    
  },  
  /**
   * Utility methods
   */
  _abortDragCondition: function(draggable) {
    // @todo fix this hardcode element id ref
    return (Element.hasClassName('bling_editor_main', 'stampMode')) ? true : false;
  },
  _setOptions: function(options) {
    this.lastOptions = this.options;
    this.options = Object.extend(this.options, options || {});
  },
  _resetOptions: function() {
    this.options = this.lastOptions;
  },  
  dispose: function() {
    if (this.sizeable) this.sizeable.destroy();
  }  
};


var TextStampRenditions = {
  textEditForm: null,
  previewElem: null,
  previewPos: null,
  previewDims: null,
  colorInputObj: null,

  initialize: function(element, form, colorInputObj) {
    this.previewElem   = $(element);
    this.textEditForm   = $(form);
    this.colorInputObj = colorInputObj;
  },
  add: function(options) {
    if (options.independent) {
      if (!this.previewPos) this.previewPos = Position.cumulativeOffset(this.previewElem);
      if (!this.previewDims) this.previewDims = Element.getDimensions(this.previewElem);      
      options = Object.extend({
        previewPos: this.previewPos,
        previewDims: this.previewDims
      }, options);
    }
    var rendition = new Mblst.TextStampRendition(this.previewElem, this.textEditForm, this.colorInputObj, options);
    return rendition;
  },
  remove: function(element) {
    var rendition = StampRenditions.getByElem(element);
    ControlEvent.notify('remove', rendition);
    if (!rendition) return null;
    StampRenditions.unregister(rendition);
    rendition.dispose();
  }
}

Mblst.TextStampRendition = Class.create();
Object.extend(Object.extend(Mblst.TextStampRendition.prototype, Mblst.Control.prototype), {
  /**
   * Constructor
   */
  initialize: function(previewElem, form, colorInputObj, options) {
    this.active = true;
    this.colorInputObj = colorInputObj;
    this.previewElem = $(previewElem);
    this.controlsForm = $(form);
    this.sizeable = null;
    
    this.fontFamilyInput = null;
    this.fontSizeInput = null;
    this.fontColorInput = null;
    this.fontBoldCheckbox = null;
    this.fontItalicCheckbox = null;
    
    this.text = null;
    this.fontFamily = null;
    this.fontSize = null;
    this.fontColor = null;
    this.fontBold = null;
    this.fontItalic = null;
    
    this.lastStampLeft = null;
    this.lastStampTop = null;
    this.lastStampW = null;

    this.options = Object.extend({
      independent: false,
      previewPaddingX: -10,
      previewPaddingY: 4,
      defaultStampText: 'Your text here',
      
      injectionSuccessor: null,
      
      editingClassName: 'editing',
      containerClassName: 'textStamp',
      editFormClassName: 'textStampEditForm',
      textareaClassName: 'textStampTextarea',
      buttonContainerClassName: 'textStampButtonContainer',
      okButtonClassName: 'bluebutton',
      cancelButtonClassName: 'graybutton',
      editLinkClassName: 'textStampEditLink',
      editLinkContainerClassName: 'textStampEditLinkContainer',
      previewClassName: 'textStampPreview',
      hoverClassName: 'stampHover', // this gets overriden when created via imagestamp
      stampIdPrefix: 'text_stamp_',
      
      stampOffsetX: 0,
      stampOffsetY: 0,
      stampTextW: 260,
      stampText: 'Your text here',
      stampFontFamily: 'impact',
      stampFontSize: 42,
      stampFontColor: '00BFBF',
      stampFontBold: 0,
      stampFontItalic: 0   
    }, options || {});
    
    // init text stamp params
    this.text = this.options.stampText;
    this.fontFamily = this.options.stampFontFamily;
    this.fontSize = this.options.stampFontSize;
    this.fontColor = this.options.stampFontColor;
    this.fontBold = this.options.stampFontBold;
    this.fontItalic = this.options.stampFontItalic;    
        
    // Call parent constructor w/ these options
    Mblst.Control.prototype.initialize.call(this, this.options);    
    
  },

  /**
   * Control methods
   */
  insert: function() {},  
  create: function() {
    if(this.options.injectionSuccessor) {
      var successor = $(this.options.injectionSuccessor);
      //successor.parentNode.insertBefore(this.containerElem, successor);
      Element.insertAfter(this.containerElem, successor);
    } else {
      this.previewElem.appendChild(this.containerElem);
    }
    StampRenditions.register(this);    
    
    // Register ControlEvent observers
    this.showeditListener = this.onTextRenditionShown.bindAsEventListener(this);
    this.leaveTextEditModeListener = this.onLeaveTextEditMode.bindAsEventListener(this);
    ControlEvent.observe(Mblst.BlingEditor, 'movemode', this.leaveTextEditModeListener);
    ControlEvent.observe(Mblst.BlingEditor, 'stampmode', this.leaveTextEditModeListener);
    
    // find font style inputs
    this.controlsForm.getElements().each(function(element) {
      if (element.type == "checkbox") {
        if (element.name.indexOf("_bold") > 0) {
          this.fontBoldCheckbox = $(element);
        } else if (element.name.indexOf("_italic") > 0) {
          this.fontItalicCheckbox = $(element);
        }
      } else {
        if (element.name.indexOf("_family") > 0) {
          this.fontFamilyInput = $(element);
        } else if (element.name.indexOf("_size") > 0) {
          this.fontSizeInput = $(element);
        } else if (element.name.indexOf("_color") > 0) {
          this.fontColorInput = $(element);
        }    
      }
    }.bind(this));
    
    this.createEditForm();
    
    // add text preview div
    this.textPreview = document.createElement("div");
    this.textPreview.className = this.options.previewClassName;
    this.containerElem.appendChild(this.textPreview);

    // add edit trigger link
    this.textEditLinkContainer = document.createElement("div");
    this.textEditLinkContainer.className = this.options.editLinkContainerClassName;
    this.textEditLink = document.createElement("a");
    this.textEditLink.href = "javascript: void(0)";
    this.textEditLink.className = this.options.editLinkClassName;
    this.textEditLinkContainer.appendChild(this.textEditLink);
    //Event.observe(this.textEditLink, 'mouseup', this.textEditLink.blur.bind(this.textEditLink));    
    
    this.textPreview.className = this.options.previewClassName;
    this.containerElem.appendChild(this.textPreview);
    this.containerElem.appendChild(this.textEditLinkContainer);

    if(this.options.stampTextW)
      this.containerElem.style.width = (this.options.stampTextW)+'px';

    this.sizeable = new Mblst.Sizeable(
        this.containerElem, {
            moveHandle: true,
            resizeConstraint: 'horizontal',
            moveStarteffect: function(element) {
                Draggable._dragging[element] = true;
                Element.addClassName(element, 'moving');
            },
            moveEndeffect: function(element) {
                Draggable._dragging[element] = false;
                var rendition = StampRenditions.getByElem(element);
                ControlEvent.notify('moveend', rendition);
                Element.removeClassName(element, 'moving');
            },
            resizeStarteffect: function(element) {
                Draggable._dragging[element] = true;
                Element.addClassName(element.parentNode, 'resizing');
            },
            resizeEndeffect: function(element) {
                Draggable._dragging[element] = false;
                var rendition = StampRenditions.getByElem(element);
                ControlEvent.notify('moveend', rendition);
                Element.removeClassName(element.parentNode, 'resizing');
            },            
            abortDragCondition: this._abortDragCondition
    });

    // calc pos values if independent
    if (this.options.independent) {
      var width = this.options.stampTextW;
      var height = Math.round(this.fontSize * 2.4); // approx
      var xOffset = this.options.pageOffsetX - (width/2.0);
      xOffset = Math.max(this.options.previewPos[0], xOffset);
      xOffset = Math.min(this.options.previewPos[0] + this.options.previewDims.width - width, xOffset);      
      var yOffset = this.options.pageOffsetY - (height/2.0);
      yOffset = Math.max(this.options.previewPos[1], yOffset);
      yOffset = Math.min(this.options.previewPos[1] + this.options.previewDims.height - height, yOffset);
      yOffset = Math.max(this.options.previewPos[1], yOffset);
      this.options.stampOffsetX = xOffset;
      this.options.stampOffsetY = yOffset;
    }
    
    Position.clone( document.body, this.containerElem, { setHeight: false,
                                                         setWidth: false,
                                                         offsetLeft: this.options.stampOffsetX,
                                                         offsetTop: this.options.stampOffsetY });    

    // Create form event observers
    new Form.Element.EventObserver(this.fontFamilyInput, this.updateFontFamily.bind(this));
    new Form.Element.EventObserver(this.fontSizeInput, this.updateFontSize.bind(this));
    this.changeboldListener = this.updateFontBold.bindAsEventListener(this);
    this.changeitalicListener = this.updateFontItalic.bindAsEventListener(this);
    this.onclickTriggerListener = this.onclickTrigger.bindAsEventListener(this);
    this.inputFocusListener = this.onInputFocus.bindAsEventListener(this);
    this.inputKeypressListener = this.onkeypressTextInput.bindAsEventListener(this);
    this.onclickPhotoPreviewListener = this.onclickPhotoPreview.bindAsEventListener(this);
    Event.observe(this.fontBoldCheckbox, 'click', this.changeboldListener);
    Event.observe(this.fontItalicCheckbox, 'click', this.changeitalicListener);    
    Event.observe(this.textEditLink, 'click', this.onclickTriggerListener);
    Event.observe(this.stampTextarea, 'focus', this.inputFocusListener);
    Event.observe(this.stampTextarea, 'keypress', this.inputKeypressListener);
    Event.observe(this.textPreview, 'click', this.onclickTriggerListener);
    Event.observe('photo_preview', 'click', this.onclickPhotoPreviewListener);// hack
    
    // Create hover event observers
    this.mouseoverListener = this.onEnterHover.bindAsEventListener(this);
    this.mouseoutListener = this.onLeaveHover.bindAsEventListener(this); 
    Event.observe(this.containerElem, 'mouseover', this.mouseoverListener);
    Event.observe(this.containerElem, 'mouseout', this.mouseoutListener);    

    this.updatePreview();
  },
  createEditForm: function() {
    this.form = document.createElement("form");
    Element.addClassName(this.form, this.options.editFormClassName);

    // add stamp textarea  
    this.stampTextarea = document.createElement("textarea");
    this.stampTextarea.name = "text"+Mblst.Control.instanceNonce;
    this.stampTextarea.rows = this.options.rows || 2;
    this.stampTextarea.cols = this.options.cols || 27;
    this.stampTextarea.className = this.options.textareaClassName;
    this.form.appendChild(this.stampTextarea);
    Element.setOpacity(this.stampTextarea, .65);

    var br = document.createElement("br");
    this.form.appendChild(br);

    this.buttonContainer = document.createElement("div");
    this.buttonContainer.className = this.options.buttonContainerClassName;

    this.okButton = document.createElement("input");
    this.okButton.type = "button";
    this.okButton.value = "Ok";
    this.okButton.className = this.options.okButtonClassName;
    this.buttonContainer.appendChild(this.okButton);

    this.cancelButton = document.createElement("input");
    this.cancelButton.type = "button";
    this.cancelButton.value = "Cancel";
    this.cancelButton.className = this.options.cancelButtonClassName;
    this.buttonContainer.appendChild(this.cancelButton);

    this.form.appendChild(this.buttonContainer);
    this.containerElem.appendChild(this.form);

    Element.hide(this.form);

    // Catch form submit
    this.form.onsubmit = function(){return false};
    this.submitformListener = this.onclickSubmit.bindAsEventListener(this);
    this.clickcancelListener = this.onclickCancel.bindAsEventListener(this);
    Event.observe(this.okButton, 'click', this.submitformListener);    
    Event.observe(this.cancelButton, 'click', this.clickcancelListener);         
  },  
  load: function() {
    this.state = 'loading';
    this.colorInputObj.options.onUpdate = this.updateFontColor.bind(this);
    this.stampTextarea.value = (this.text || this.options.defaultStampText);
    this.loadStyleInputs();
    //this.sizeable.initResizeHandle(true, false);
  },
  loadStyleInputs: function() {
    Field.selectOption(this.fontFamilyInput, this.fontFamily.toLowerCase());
    this.updateFontFamily();
    Field.selectOption(this.fontSizeInput, this.fontSize);
    this.updateFontSize();
    this.fontBoldCheckbox.checked = (this.fontBold) ? true : false;
    this.updateFontBold();
    this.fontItalicCheckbox.checked = (this.fontItalic) ? true : false;
    this.updateFontItalic();    
    this.colorInputObj.colorSelected(this.fontColor);
  },
  show: function() {
    Element.hide(this.textPreview);
    Element.hide(this.textEditLinkContainer);
    Element.show(this.form);
    //this.stampTextarea.select();
    Element.addClassName(this.containerElem, this.options.editingClassName);
    ControlEvent.notify('showedit', this);
  },
  hide: function() {
    Element.hide(this.form);
    Element.show(this.textPreview);
    Element.show(this.textEditLinkContainer);
    Element.removeClassName(this.containerElem, this.options.editingClassName);
  },
  submit: function() {
    this.update();
    this.event('beforeDismiss');
    this.dismiss();
    this.event('afterDismiss');
    return false;
  },
  cancel: function() {
    this.event('beforeDismiss');
    this.dismiss();
    this.event('afterDismiss');
    ControlEvent.notify('cancel', this);
    return false;
  },
  update: function() {
    // update members
    this.text = $F(this.stampTextarea);
    this.fontFamily = $F(this.fontFamilyInput);
    this.fontSize = $F(this.fontSizeInput);
    this.fontColor = $F(this.fontColorInput);
    this.fontBold = (this.fontBoldCheckbox.checked) ? 1 : 0;
    this.fontItalic = (this.fontItalicCheckbox.checked) ? 1 : 0;
    // update preview
    this.updatePreview();
    
    // fire callback
    ControlEvent.notify('update', this);
  },
  toJSON: function() {
    var offsets = Position.positionedOffset(this.containerElem);
    var text = (this.text != this.options.defaultStampText) ? this.text : '';
    var ret = {
      xcoord: offsets[0] + this.options.previewPaddingX,
      ycoord: offsets[1] + this.options.previewPaddingY,
      width: parseInt(Element.getStyle(this.containerElem,'width') || this.width) - (2*this.options.previewPaddingX),
      text: text,
      fontFamily: this.fontFamily,
      fontSize: this.fontSize,
      fontColor: this.fontColor,
      fontBold: this.fontBold,
      fontItalic: this.fontItalic
    };     
    return Object.toJSON(ret);
  },   
  /**
   * Helpers
   */
  updatePreview: function() {
    this.textEditLink.innerHTML = (this.text) ? "Click&nbsp;to&nbsp;Edit&nbsp;Text" : "Click&nbsp;to&nbsp;Add&nbsp;Text";
    this.textPreview.innerHTML = this.text;
    this.textPreview.style.color = '#'+this.fontColor;
    this.textPreview.style.fontFamily = "'"+this.fontFamily+"'";
    this.textPreview.style.fontSize = this.fontSize+'px';
    this.textPreview.style.fontWeight = (this.fontBold) ? 'bold' : 'normal';
    this.textPreview.style.fontStyle = (this.fontItalic) ? 'italic' : 'normal';
  },    
  updateFontColor: function(color) {
    if ((this.state != 'showing') && (this.state != 'loading')) return true;
    this.stampTextarea.style.color = color;
    return true;
  },  
  updateFontFamily: function() {
    if ((this.state != 'showing') && (this.state != 'loading')) return true;
    this.stampTextarea.style.fontFamily = "'"+$F(this.fontFamilyInput)+"'";
    return true;
  },
  updateFontSize: function() {
    if ((this.state != 'showing') && (this.state != 'loading')) return true;
    this.stampTextarea.style.fontSize = $F(this.fontSizeInput)+'px';
    return true;
  },
  updateFontBold: function() {
    if ((this.state != 'showing') && (this.state != 'loading')) return true;
    this.stampTextarea.style.fontWeight = (this.fontBoldCheckbox.checked) ? 'bold' : 'normal';
    return true;
  },
  updateFontItalic: function() {
    if ((this.state != 'showing') && (this.state != 'loading')) return true;
    this.stampTextarea.style.fontStyle = (this.fontItalicCheckbox.checked) ? 'italic' : 'normal';
    return true;
  },
  /**
   * ControlEvent callbacks
   */
  onTextRenditionShown: function(event) {
    var source = ControlEvent.source(event);
    if(this.state == 'showing' && source != this) {
      this.event('beforeDismiss');
      this.dismiss();
      this.event('afterDismiss');
    }
  },
  onLeaveTextEditMode: function(event) {
    var source = ControlEvent.source(event);
    if(this.state == 'showing') {
      this.event('beforeDismiss');
      this.dismiss();
      this.event('afterDismiss');
    }    
  },
  /**
   * DOM Event handlers
   */
  onEnterHover: function() {
    if (this.text) Element.addClassName(this.containerElem, this.options.hoverClassName);
  },  
  onLeaveHover: function() {
    if (this.text) Element.removeClassName(this.containerElem, this.options.hoverClassName);
  },
  onclickSubmit: function(evt) {
    if(evt) Event.stop(evt);
    this.event('beforeSubmit');
    this.submit();
    this.event('afterSubmit');
    return false;
  },
  onclickCancel: function(evt) {
    if(evt) Event.stop(evt);
    this.cancel();
    return false;
  },
  onclickTrigger: function(evt) {
    if (!Element.hasClassName('bling_editor_main', 'stampMode')) {
      this.trigger();
    }
  },
  onInputFocus: function() {
    if ($F(this.stampTextarea) == this.options.defaultStampText) {
      Field.clear(this.stampTextarea);
    }
  },  
  onkeypressTextInput: function(evt) {
    if(evt.keyCode!=Event.KEY_RETURN) return;
    Event.stop(evt);
    this.event('beforeSubmit');
    this.submit();
    this.event('afterSubmit');
    return false;
  },
  onclickPhotoPreview: function(evt) {
    if (this.state == 'showing' && !Element.descendantOf(Event.element(evt), this.containerElem)) {
      this.event('beforeSubmit');
      this.submit();
      this.event('afterSubmit');    
    }
  },    
  /**
   * Utility methods
   */
  _abortDragCondition: function(draggable) {
    // @todo fix this hardcode element id ref
    return (Element.hasClassName('bling_editor_main', 'textEditMode') || Element.hasClassName('bling_editor_main', 'moveMode')) ? false : true;
  },  
  dispose: function() {
    ControlEvent.stopObserving(this, 'showedit', this.showeditListener);
    ControlEvent.stopObserving(Mblst.BlingEditor, 'movemode', this.leaveTextEditModeListener);
    ControlEvent.stopObserving(Mblst.BlingEditor, 'stampmode', this.leaveTextEditModeListener);    
    Event.stopObserving(this.fontBoldCheckbox, 'click', this.changeboldListener);
    Event.stopObserving(this.fontItalicCheckbox, 'click', this.changeitalicListener);   
    Event.stopObserving(this.form, 'submit', this.submitformListener);    
    Event.stopObserving(this.cancelButton, 'click', this.clickcancelListener);
    Event.stopObserving(this.containerElem, 'mouseover', this.mouseoverListener);
    Event.stopObserving(this.containerElem, 'mouseout', this.mouseoutListener);
    Event.stopObserving(this.textEditLink, 'click', this.onclickTriggerListener);
    Event.stopObserving(this.textPreview, 'click', this.onclickTriggerListener);    
    this.sizeable.dispose();
    if (this.containerElem.parentNode) Element.remove(this.containerElem);
  }  
});

Mblst.BlingStampThumb = Class.create();
Mblst.BlingStampThumb.isDefaultThumbSelected = false;
Mblst.BlingStampThumb.prototype = {
  /**
   * Constructor
   */
  initialize: function(blingEditorObj, triggerElem, stampURL, origW, origH, thumbW, thumbH, options) {
    this.blingEditorObj = blingEditorObj;
    this.triggerElem = $(triggerElem);
    this.draggable = null;
    this.stampURL = stampURL;
    this.origW = origW;
    this.origH = origH;
    this.thumbW = thumbW;
    this.thumbH = thumbH;    

    this.options = Object.extend({
      canResize: false,
      resizeProportional: false,
      moveModeOnStamp: true,
      
      frameDelay: 0.16,
      pngFrameCount: 0,
      
      stampText: null,
      stampTextTop: null,
      stampTextLeft: null,
      stampTextW: null,
      stampFontFamily: null,
      stampFontSize: null,
      stampFontColor: null,
      stampFontBold: null,
      stampFontItalic: null

    }, options || {});
       
    this.onclickThumbListener = this.onclickThumb.bindAsEventListener(this);
    Event.observe(this.triggerElem, 'click', this.onclickThumbListener);
    
    this.draggable = new Mblst.Draggable(this.triggerElem, {
      revert: true,
      escapeScrollDiv: this.triggerElem.parentNode,
      starteffect: function(element) {
        Element.addClassName(element, 'moving');
        element._opacity = Element.getOpacity(element);
        Draggable._dragging[element] = true;
        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
        this.setStamp();
      }.bind(this),      
      endeffect: function(element) {
        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){ 
            Draggable._dragging[element] = false 
          }
        });
        Element.removeClassName(element, 'moving'); 
      }      
    });
  },

  /**
   * Control methods
   */
  setStamp: function() {
    this.prefetchAnimFrames();
    this.blingEditorObj.setImageStamp(this);
  },  
  prefetchAnimFrames: function() {
    if (!this.options.pngFrameCount || !this.stampURL || !this.stampURL.match(/png\.gif$/)) return;

    this.preloads = [];
    for (var i = 0; i < this.options.pngFrameCount; i++) {
      frameSrcURL = this.stampURL.replace(/png\.gif$/, i + '.png');
      this.preloads.push(frameSrcURL);
    }
    preloadImages(this.preloads);
  },  
  /**
   * DOM Event handlers
   */
  onclickThumb: function(evt) {
    this.setStamp();
    window.scrollTo(0, 0);
    return false;
  }  
};



Mblst.AnonPhotoUploader = Class.create();
Mblst.AnonPhotoUploader.prototype = {

  initialize: function(container, options) {
    this.containerElem = $(container);
		this.submitImageUrlElem = null;
    this.formPickerStart = null;
    
    this.photoWidth = 0;
    this.photoHeight = 0;
    this.photoURL = null;
    this.photoOwner = null;
    
    this.options = Object.extend({
			submitImageUrlElem: 'submit_image_url',
      inputUploadPhoto: 'input_upload_photo',
			inputImageUrl: 'input_image_url',
      indicator: null,
			
      currentTabClassName: 'active',
      currentTabLinkClassName: 'current',
      tabLinkIdPrefix: 'pickertablink',      
      tabIdPrefix: 'pickertab',
      //tabs: ['picker_upload', 'picker_url'],
      tabs: null,
      
      toUploadLink: 'to_upload_link',
      toUrlLink: 'to_url_link',
      uploadPane: 'picker_upload',
      urlPane: 'picker_url'

    }, options || {});    
        
    // Init DOM elem members
    if (this.options.formPickerStart) this.formPickerStart = $(this.options.formPickerStart);
    if (this.options.submitImageUrlElem) this.submitImageUrlElem = $(this.options.submitImageUrlElem);
    if (this.options.inputImageUrl) this.inputImageUrl = $(this.options.inputImageUrl);		
 
     // Init picker tabs
    if (this.pickerTabs = this.options.tabs) {
	    for (i = 0; i < this.pickerTabs.length; i++) {
	      Event.observe(this.options.tabIdPrefix+i, 'click', function(i){this.changePickerTab(i);return false;}.bind(this, i));
	    }
    }

    // Init picker links
    if (this.options.toUploadLink) {
      this.toUploadLink = $(this.options.toUploadLink);
      this.toUploadPaneListener = this.toUploadPane.bindAsEventListener(this);
      Event.observe(this.toUploadLink, 'click', this.toUploadPaneListener);
    }
    if (this.options.toUrlLink) {
      this.toUrlLink = $(this.options.toUrlLink);
      this.toUrlPaneListener = this.toUrlPane.bindAsEventListener(this);
      Event.observe(this.toUrlLink, 'click', this.toUrlPaneListener);
    }    
 
     // Register DOM callbacks
    this.clicksubmitimageurlListener = this.onclickSubmitImageUrl.bindAsEventListener(this);
    if (this.submitImageUrlElem) Event.observe(this.submitImageUrlElem, 'click', this.clicksubmitimageurlListener);
 
    // Init Control event observers
    this.submitTempPhotoUploadInputListener = this.onsubmitTempPhotoUploadInput.bindAsEventListener(this);
    ControlEvent.observe(Mblst.TempPhotoUploadInput, 'submit', this.submitTempPhotoUploadInputListener);
  
    // Submit form if URL found on init
	this.submitInitImageUrl();
  },
  /**
   * Public Mutators
   */
  show: function() {
    Element.show(this.containerElem);
  },
  hide: function() {
    Element.hide(this.containerElem);
  },
  reset: function() {
    this.resetPhotoPicker();
  },
  changePickerTab: function(tabNum) {
    this.reset();
    for (i = 0; i < this.pickerTabs.length; i++) {
      if (i == tabNum) {
        this.selectPickerTab(i);
      } else {
        this.deselectPickerTab(i);
      }
    }
  },	  
  /**
   * Mutator Helpers
   */
  selectPickerTab: function(tabNum) {
    Element.addClassName(this.options.tabIdPrefix+tabNum, this.options.currentTabClassName);
    Element.addClassName(this.options.tabLinkIdPrefix+tabNum, this.options.currentTabLinkClassName);
    Element.show(this.pickerTabs[tabNum]);
  },  
  deselectPickerTab: function(tabNum) {
    Element.removeClassName(this.options.tabIdPrefix+tabNum, this.options.currentTabClassName);
    Element.removeClassName(this.options.tabLinkIdPrefix+tabNum, this.options.currentTabLinkClassName);
    Element.hide(this.pickerTabs[tabNum]);
  },
  toUploadPane: function() {
    Element.hide(this.options.urlPane);
    Element.show(this.options.uploadPane);
  },
  toUrlPane: function() {
    Element.hide(this.options.uploadPane);
    Element.show(this.options.urlPane);  
  },	
  onclickSubmitImageUrl: function() {
    if (!this.inputImageUrl || !this.valInputImageUrl()) return false;
    this.uploadTempPhoto($F(this.inputImageUrl));
  },	
  onPhotoPickTaskComplete: function() {
    if (!this.photoURL) return;
    if (this.options.indicator) this.options.indicator.hide();
    ControlEvent.notify('photopick', this);
  },
  submitInitImageUrl: function() {
    if(!this.inputImageUrl) return false;
        var url = $F(this.inputImageUrl);
    if (!url) {
        return false;
    }
    else if (!url.match(/^http:\/\/\S+\.(jpg|jpeg|png|gif)$/)) {
        alert("Invalid image URL.");
        return false;
    }
    this.uploadTempPhoto($F(this.inputImageUrl));
  },
  resetPhotoPicker: function() {
    this.photoWidth = 0;
    this.photoHeight = 0;
    this.photoURL = null;
    this.photoOwner = null;    
  },
  showUploadFailed: function() {
    
  },
  /**
   * AJAX Requests
   */
  uploadTempPhoto: function(url) {
    new Ajax.Request(
      Mblst.Url.tempPhotoC(), {
        asynchronous: true,
        method: 'post',
        parameters: 'src_url='+encodeURIComponent(url),
        onSuccess: this.onStartUploadTempPhotoSuccess.bind(this),
        onFailure: this.showUploadFailed
      }
    );
    if (this.options.indicator) this.options.indicator.show('loading photo...');
  },
  pollUploadStatus: function(ticketid) {
    new Mblst.TicketPoller(
      [ticketid], {
        onComplete: this.onUploadTempPhotoComplete.bind(this),
        onFailure: this.showUploadFailed,
        initialDelay: 200,
        intervalScaleFactor: 500
      }
    );  
  },
  /**
   * Control Event Callbacks
   */
  onsubmitTempPhotoUploadInput: function(evt) {
    var tempPhotoInput = ControlEvent.source(evt);
    var tid = tempPhotoInput.getTicketId();
    if (tid) {
      if (this.options.indicator) this.options.indicator.show('loading photo...');
      this.pollUploadStatus(tid);
    }
    return false;
  },
  /**
   * AJAX Callbacks
   */  
  onStartUploadTempPhotoSuccess: function(transport, json) {
    if ( transport.responseText && transport.responseText != 'N;' && transport.responseText != '0') {
      this.pollUploadStatus(transport.responseText);  
    } else {
      this.showUploadFailed();
    }
  },
  /**
   * Mblst.TicketPoller Callbacks
   */    
  onUploadTempPhotoComplete: function(ticketStashData) {
    this.photoWidth = ticketStashData[0].orig_width;
    this.photoHeight = ticketStashData[0].orig_height;
    this.photoURL = ticketStashData[0].image_url;
    this.photoOwner = this.selectedPhotoOwner;
    this.onPhotoPickTaskComplete();
  },
  /**
   * Validators
   */    
  valInputImageUrl: function() {
    if(!this.inputImageUrl) return false;
		var url = $F(this.inputImageUrl);
    if (!url) {
        alert("You must enter a URL.");
        return false;
    }
    else if (!url.match(/^http:\/\/\S+\.(jpg|jpeg|png|gif)$/)) {
        alert("Invalid image URL.");
        return false;
    }
    return true;		
  }	
}

Mblst.CreateBling = Class.create();
Mblst.CreateBling.prototype = {

  initialize: function(blingEditor, createTrain, createPicker, stampTabs, options) {
    this.blingEditor = blingEditor;
    this.createTrain = createTrain;
    this.createPicker = createPicker;
    this.stampTabs = stampTabs;
    
    this.backStampPhotoElem = null;
    this.nextStampPhotoElem = null;
    this.backSendPhotoElem = null;
    this.backSendPhotoTopElem = null;
    this.nextSendPhotoElem = null;
    
    this.backStampTopLink = null;
    this.backSendTopLink = null;
    
    this.formStampPhoto = null;
    
    this.modes = ['pick', 'stamp', 'send', 'processing'];
    this.midPaneElems = [];
    this.headPaneElems = [];
   
    this.options = Object.extend({     
      formStampPhoto: 'stamp_photo_form',
      
      backStampPhotoElem: 'back_stamp_photo',
      nextStampPhotoElem: 'next_stamp_photo',
      backSendPhotoElem: 'back_send_photo',
      backSendPhotoTopElem: 'back_send_photo_top',
      nextSendPhotoElem: 'next_send_photo',
      
      backStampTopLink: 'car0',
      backSendTopLink: 'back_send_top_link',
      
      midPaneIdPrefix: 'create_mid_pane_',
      headPaneIdPrefix: 'create_head_pane_',
      
      indicator: null

    }, options || {});    

    // Init BlingEditor object
    this.blingEditor.options.onClickSubmit = this.onClickBlingEditorSubmit.bind(this);
    this.blingEditor.saveButton.value = 'Save';
    
    // Init DOM elem members
    if (this.options.backStampPhotoElem) this.backStampPhotoElem = $(this.options.backStampPhotoElem);
    if (this.options.nextStampPhotoElem) this.nextStampPhotoElem = $(this.options.nextStampPhotoElem);
    if (this.options.backSendPhotoElem) this.backSendPhotoElem = $(this.options.backSendPhotoElem);
    if (this.options.backSendPhotoTopElem) this.backSendPhotoTopElem = $(this.options.backSendPhotoTopElem);
    if (this.options.nextSendPhotoElem) this.nextSendPhotoElem = $(this.options.nextSendPhotoElem);
    if (this.options.backStampTopLink) this.backStampTopLink = $(this.options.backStampTopLink);
    if (this.options.backSendTopLink) this.backSendTopLink = $(this.options.backSendTopLink);    
 
    // Init mid pane DOM elements
    for (i = 0; i < this.modes.length; i++) {
      this.midPaneElems[this.modes[i]] = $(this.options.midPaneIdPrefix+this.modes[i]);
    }
    
    // Init head pane DOM elements
    for (i = 0; i < this.modes.length; i++) {
      this.headPaneElems[this.modes[i]] = $(this.options.headPaneIdPrefix+this.modes[i]);
    }    
    
    // Register ControlEvent callbacks
    this.photopickListener = this.onPhotoPickComplete.bindAsEventListener(this);
	// TODO Fix this hack, we're still in an FB specific class.
    ControlEvent.observe(Mblst.AnonPhotoUploader, 'photopick', this.photopickListener);

    this.textEditShowListener = this.onTextEditShow.bindAsEventListener(this);
    this.textEditDismissListener = this.onTextEditDismiss.bindAsEventListener(this);
    ControlEvent.observe(Mblst.TextStampRendition, 'showedit', this.textEditShowListener);
    ControlEvent.observe(Mblst.TextStampRendition, 'update', this.textEditDismissListener);
    ControlEvent.observe(Mblst.TextStampRendition, 'cancel', this.textEditDismissListener);
    
    // Register DOM callbacks
    this.clicknextstampphotoListener = this.onClickNextStampPhoto.bindAsEventListener(this);
    this.clickbackstampphotoListener = this.onClickBackStampPhoto.bindAsEventListener(this);
    this.clicknextsendphotoListener = this.onClickNextSendPhoto.bindAsEventListener(this);
    this.clickbacksendphotoListener = this.onClickBackSendPhoto.bindAsEventListener(this);
    if (this.nextStampPhotoElem) Event.observe(this.nextStampPhotoElem, 'click', this.clicknextstampphotoListener);
    if (this.backStampPhotoElem) Event.observe(this.backStampPhotoElem, 'click', this.clickbackstampphotoListener);
    if (this.nextSendPhotoElem) Event.observe(this.nextSendPhotoElem, 'click', this.clicknextsendphotoListener);
    if (this.backSendPhotoElem) Event.observe(this.backSendPhotoElem, 'click', this.clickbacksendphotoListener);
    if (this.backSendPhotoTopElem) Event.observe(this.backSendPhotoTopElem, 'click', this.clickbacksendphotoListener);
    if (this.backStampTopLink) Event.observe(this.backStampTopLink, 'click', this.clickbackstampphotoListener);
    if (this.backSendTopLink) Event.observe(this.backSendTopLink, 'click', this.clickbacksendphotoListener);    
  },
  /**
   * Public Mutators
   */
  nextPickPhoto: function(picker) {
    this.options.contacts = picker.options.contacts;
    var orientation = (picker.photoWidth >= picker.photoHeight) ? 'landscape' : 'portrait';
    this.hidePickerMode();
    this.createPicker.hide();
    this.blingEditor.showPhotoPreview(picker.photoURL, picker.photoOwner, picker.photoWidth, picker.photoHeight);
	this.blingEditor.show();
    this.stampTabs.show();
    this.showStampMode();
    this.createTrain.changeCar(1);
		Element.show('bling_editor'); //hack! hardcoded
    new Effect.Highlight(this.midPaneElems['stamp']);
  },
  backStampPhoto: function() {
    this.hideStampMode();
    this.stampTabs.hide();
    this.createPicker.show();
    this.blingEditor.hide();
    Element.hide('bling_editor'); //hack! hardcoded
    this.showPickerMode();
    this.createTrain.changeCar(0);
  },
  nextStampPhoto: function() {
    if (!this.blingEditor.valAddBlingForm()) return false;
    this.hideStampMode();
    this.stampTabs.hide();
    this.blingEditor.submit();
    this.blingEditor.hide();
    this.showProcessingMode();  
  },
  backSendPhoto: function() {
    this.hideSendMode();
    this.blingEditor.gotoLastMode();
    this.stampTabs.show();
    this.showStampMode();
    this.createTrain.changeCar(1);
  },  
  send: function(uid, message) {
    this.hideSendMode();
    this.blingEditor.setOwnerUserId(uid);
    if (message) this.blingEditor.setPhotoDescription(message);
    this.blingEditor.submit();
    this.showProcessingMode();
  },
  
  /**
   * Mutator Helpers
   */
  showPickerMode: function() {
    Element.show(this.midPaneElems['pick']);
    Element.show(this.headPaneElems['pick']);
  },
  hidePickerMode: function() {
    Element.hide(this.midPaneElems['pick']);
    Element.hide(this.headPaneElems['pick']);
  },
  showStampMode: function() {
    Element.show(this.midPaneElems['stamp']);
    Element.show(this.headPaneElems['stamp']);
  },
  hideStampMode: function() {
    Element.hide(this.midPaneElems['stamp']);
    Element.hide(this.headPaneElems['stamp']);
  },  
  showSendMode: function() {
    Element.show(this.midPaneElems['send']);
    Element.show(this.headPaneElems['send']);
  },
  hideSendMode: function() {
    Element.hide(this.midPaneElems['send']);
    Element.hide(this.headPaneElems['send']);
  },
  showProcessingMode: function() {
    Element.show(this.midPaneElems['processing']);
    Element.show(this.headPaneElems['processing']);
  },
  /**
   * DOM Callbacks
   */
  onClickNextStampPhoto: function() {
    this.nextStampPhoto();
  },
  onClickBackStampPhoto: function() {
    this.backStampPhoto();
  },
  onClickNextSendPhoto: function() {
 
  },
  onClickBackSendPhoto: function() {
    this.backSendPhoto();
  },
  /**
   * BlingEditor Callbacks
   */
  onClickBlingEditorSubmit: function(editor) {
    this.nextStampPhoto();
  },  
  /**
   * ControlEvent Callbacks
   */    
  onPhotoPickComplete: function(event) {
    var picker = ControlEvent.source(event);
    this.nextPickPhoto(picker);
  },
  onTextEditShow: function(event){
    this.stampTabs.hide();
  },
  onTextEditDismiss: function(event){
    this.stampTabs.show();
  }  
  /**
   * Validators
   */    
}