The Gravey 2.0 Framework and AIM RIA

aimauctions.js

Summary

Collection of application level classes.
This collection implements the rich-internet-application tier of the Auction Module for AIM (Auction Inventory Management), which is a demonstration of the Gravey framework.

Version: 2.0

Requires:

Author: Bruce Wallace (PolyGlotInc.com)


Class Summary
ALEDO This is the abstract base class for Auction/Lot EDOs.
ALIndex This class encapsulates an EDO index for Auctions/Lots.
Auction This class encapsulates an Auction record.
AuctionModel This class encapsulates the data model of a particular Auction.
AuctionModelView This class produces the view of the entire data panel.
AuctionSelectionModel This class encapsulates the data model for "which Auction is currently selected".
AuctionSummary This class encapsulates the identifying attributes of an Auction.
AuctionView This class produces a view of a Auction.
BidderLoadReplyCmd This Command processes the reply of the "load Bidder data" request.
BidderMenuCmd This class implements the bidder menu edit command.
Lot This class encapsulates a Auction "lot" record.
LotListView This class produces a view of a List of Lot EDOs.
LotView This class produces a view of a Lot.
NextPhaseCmd This class implements the Move to Next Auction Phase cmd.
PhaseButtonController This class is a Button Controller that manages the button that "moves" an auction from one phase to the next.

Method Summary
static Object _ContactsSelected( bidderKey )
          
static void _GetBidderLists( <long> bidderKey )
           Load in contacts and shippings list from specified bidder
NOTE: this is a Command helper function.
NEVER call this from outside the context of a Command object!
static Object _ShippingSelected( bidderKey )
          
static void onBidderLoadReply( <XMLreq> xmlReq )
           handle event for 'reply received from "load Bidder data" server-request'
static Object onBidderSelect( <String> viewID )
           handle event for 'user selects from the bidder menu'
static void onPhaseBtnPressed()
           handle event for 'next phase' button press

// This file uses JSDoc-friendly comments [ http://jsdoc.sourceforge.net/ ]

/**
 * @file         aimauction.js
 * @fileoverview Collection of application level classes.<br/>
 * This collection implements the rich-internet-application tier of
 * the Auction Module for AIM (Auction Inventory Management),
 * which is a demonstration of the Gravey framework.
 * @author       Bruce Wallace  (PolyGlotInc.com)
 * @requires     grvEDO.js
 * @requires     grvAJAX.js
 * @version      2.0
 */

////////////////////////////////////////////
//////////// Domain Objects ////////////////
////////////////////////////////////////////

Class(AuctionSummary,["unique Key","auction ID","item count"])
.Extends(EDOHolderSelectionItem);
/**
 * @class This class encapsulates the identifying attributes of an Auction.
 * @extends EDOHolderSelectionItem
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function AuctionSummary()
{
	/** @param {int} theKey unique key of this record
	 *  @param {String} theID auction ID
	 *  @param {int} itemCount count of items attached to this auction
	 */
	this.konstructor = function( theKey, theID, itemCount )
	{
		// define instance variables
		this.key	= theKey;
		this.bsID	= theID;
		this.count  = itemCount;
	}

	/** return "this" formatted for main menu item @type String */
	this.getDescription = function() {
		return grvDiamond(this.count)+this.bsID;
	}

	/** return "this" as human-readable string @type String */
	this.toString = function()
	{
		var		s = new Array();
				s.push( this.key );
				s.push( this.bsID );
		return	s.join( "-" );
	}

	/** set name to given value */
	this.updateProperties = function( ID ){
		this.bsID = ID;
	}
}


Class(ALIndex).Extends(EDOIndex);
/**
 * @class This class encapsulates an EDO index for Auctions/Lots.
 * @extends EDOIndex
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function ALIndex()
{
  if (!ALIndex.Auction){//keep this line above jsdoc comments!

	/** Static factory method to generate Auction-flavored index
	 * @param {int} index Auction index
	 * @type ALIndex
	 */
	ALIndex.Auction = function() {
		return new ALIndex(0,null);
	}
	/** Static factory method to generate Lot-flavored index
	 * @param {int} index Lot index
	 * @type ALIndex
	 */
	ALIndex.Lot = function( index ) {
		return new ALIndex(0,index);
	}
  }

	/** @param {int} optA index into auctions list for current Auction
	 *  @param {int} optL index into lots list for specified Auction
	 * If args not specified or if all are null then this represents "no selection".
	 */
	this.konstructor = function( optA,optL )
	{
		// define instance variables
		this.a = optA===undefined ? null : optA;
		this.l = optL===undefined ? null : optL;
	}

	/** return "this" as event handler arg list string @type String */	
	this.asEventArgs = function() {
		return " new ALIndex("+this.a+","+this.l+") ";
	}

	this.asString = function() {
		return this.invoke( this, this._noString, this._lotString )
		     + this._auctionString( this.a );
	}

	this._noString = function(){ return ""; }

	/** return auction description @type String */
	this._auctionString = function( aindex ){ 
		var a = gAuctionData._getAuction( aindex );
		return (a && a[ kStrIdAuctionID ]) ? a[ kStrIdAuctionID ] : "New Auction";
	}
	/** return lot description @type String */
	this._lotString = function( lindex ){
		lindex -= 0; //type cast to int
		return "Lot["+(lindex+1)+"] of ";
	}

	/** return whether "this" contains a selection @type boolean */
	this.hasSelection = function(){ return this.a!=null
	                                    || this.l!=null; }
	
	/** return whether "this" equals given EDOIndex @type boolean */
	this.equals = function(x){
		return x ? (this.a==x.a && this.l==x.l) : false;
	}

	/** Invoke the appropriate method given the index type and
	 * return its result. Methods are expected to have the signature<pre>
	 * self.method(this.subindex,optionalArgument)
	 *</pre>
	 * @param {Object} self the parent object of the method
	 * @param {Function} sMethod method to call if xi is Auction type
	 * @param {Function} lMethod method to call if xi is Lot     type
	 * @param {Object} optArg optional extra param to pass to method
	 * @return the result of the called method
	 */
	this.invoke = function(self,sMethod,lMethod,optArg){
		var aMethod;
		var i;
		if (this.l!=null) { aMethod = lMethod; i = this.l; } else
                          { aMethod = sMethod; i = this.a; }
		return aMethod.call( self, i, optArg );
	}

}

////////////////////////////////////////////////////////////////////////////////
// Constants defining property names for the auction/lot properties
////////////////////////////////////////////////////////////////////////////////

var kStrIdAuctionID   = 'Auction ID';
var kDateIdCreate     = 'Create Date';
var kDateIdLaunch     = 'Launch Date';
var kDateIdLockdown   = 'Lockdown Date';
var kDateIdCutoff     = 'Cutoff Date';
var kDateIdAward      = 'Bid Award Date';
var kDateIdClosed     = 'Closed Date';

// Lot fields
var kIntIdLotNum      = 'Lot#';	//should not be null
var kStrIdLotName     = 'Lot Name';
var kDateIdPayDate    = 'Effective Pay Date';
var kDateIdClosed     = 'Closed Date';
var kAmtIdExpPerItem  = 'Expenses/Item';
var kPctIdActualBid   = 'Actual Bid %';
var kPctIdCommission  = 'Commission %';
var kPctIdEstTarget   = '% of Est Target %';
var kIntIdContactsKey = 'Contact';
var kIntIdShippingKey = 'Shipping';
// transient Lot field(s)
var kIntIdBidderKey   = 'Bidder';


Class(ALEDO,["parent AuctionModel","item count",
	"edo Class","property values array",
	"edit Date","edit User","unique Key"])	
.Extends(EDO);
/**
 * @class This is the abstract base class for Auction/Lot EDOs.
 * @extends EDO
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function ALEDO()
{
	/** 
	 *  @param {AuctionModel} holder parent EDOHolderModel
	 *  @param {int} itemcount how many items are linked to this
	 *  @param {Function} CLAZZ this EDO's "class"
	 *  @param {array}  propValues array of property initial values
	 *  @param {String} editDate date of last edit
	 *  @param {String} editUser ID of last user to edit
	 *  @param {int} theKey unique key of this record
	 *  @param {int} optEditStatus optional edit status where the
	 * status codes are: 0=no-change; 1=delete; 2=create; 3=hidden; 4=update
	 *  @param {String} optName optional name of this instance
	 */
	this.konstructor = function
	(
		holder,
		itemcount,
		CLAZZ,
		propValues,
		editDate,
		editUser,
		theKey,
		optEditStatus,
		optName
	){
		this.souper( CLAZZ, holder, propValues, editDate, editUser,
					theKey, optEditStatus, optName );

		// define instance variables
		this.itemCount = itemcount;
	}

	this.toolTip = function( xi ){
		return this.edoToolTip(xi) + " Items="+this.itemCount;
	}

	this.toString = function() {
		return this[ kStrIdAuctionID ];
	}

	this.canDelete = function(){
		if (!this.isActive())
		  return "This is already deleted.";

		var c = this.itemCount;
		if (c>0)
		  return "This can't be deleted. It is referenced by "
		            +c+" Item"+(c==1?".":"s.");

		if (this.isLotZero && this.isLotZero())
		  return "Lot 0 must always exist.";

		return null;
	}

	/** push into given array all ALEDO-specific fields of the UpdateItem message
	 * formatted as<pre>
	 * "{parentAuctionID}~{property1}~...~{propertyN}"
	 * where the fields are defined as follows...
	 *     {parentAuctionID} = "my" parent auction ID
	 *     {propertyI}       = the i-th editable property of this EDO
	 *</pre>
	 * @param {Array} uFields  string array containing Update Msg fields
	 */
	this.pushUpdateFields = function( uFields )
	{
		uFields.push( encodeURIComponent(this.holder.getID()) );
		this.pushEncodedValues( uFields );
	}
}

Class(Auction,["property values array","edit Date","edit User",
			 "unique Key","parent holder","lot count"])
.Extends(ALEDO);
/**
 * @class This class encapsulates an Auction record.
 * @extends ALEDO
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function Auction()
{
	// init "static" Class methods/elements
	if (!Auction.Properties) {//keep this line above jsdoc comments!

		/** Static factory method to generate blank Auction @type Auction */
		Auction.Factory = function(index,holder)
		{
			var x = new Auction( null, grvGetTodayAsMMDDYYYY(),kUserID,null,holder,0 );
			x[ kDateIdCreate ] = x.date;
			return x;
		}

		// init static member variables
		Auction.UpdateID   = "Auction";
		Auction.Properties = [ kStrIdAuctionID,
				kDateIdCreate, kDateIdLaunch, kDateIdLockdown,
				kDateIdCutoff, kDateIdAward, kDateIdClosed ];

		Auction.EditRules  = [ MVCEditRule.kALNO,
				MVCEditRule.kDATE, MVCEditRule.kDate, MVCEditRule.kDate,
				MVCEditRule.kDate, MVCEditRule.kDate, MVCEditRule.kDate ];
		
		Auction.States     = [ kDateIdCreate, kDateIdLaunch, kDateIdLockdown,
				kDateIdCutoff, kDateIdAward, kDateIdClosed ];
	}

	/** @param {array} array of editable property values in same order as .Properties
	 *  @param {String} editDate date of last edit
	 *  @param {String} editUser ID of last user to edit
	 *  @param {int} theKey unique key of this record
	 *  @param {AuctionModel} holder parent EDOHolderModel
	 *  @param {int} itemcount how many Items are linked to this
	 *  @param {int} optEditStatus optional edit status
	 *  @param {String} optName optional name of this instance
	 */
	this.konstructor = function
	( propArray, editDate, editUser, theKey, holder, itemcount, optEditStatus, optName )
	{
		//Cant edit ID if items are already assigned to auction
		Auction.EditRules[0] = (itemcount>0) ? MVCEditRule.kRO : MVCEditRule.kALNO;

		this.souper( holder, itemcount, Auction,
		 propArray, editDate, editUser,	theKey, optEditStatus, optName );

		//if new object then init a few fields
		if (!propArray) this[kDateIdCreate] = editDate;

		//set up special validations
		this[ kStrIdAuctionID+MVCAttributeModel.kValidateSuffix ] = this.validateBSID;

		for (var i=0; i<Auction.States.length; ++i)
		  this[ Auction.States[i]+MVCAttributeModel.kValidateSuffix ] = this.validateDate;
	}

	/** custom validation method to verify legality of a phase date
	 * @type String
	 * @return error message or null if no error
	 */
	this.validateDate = function( dateID )
	{
		var dateStr = this[ dateID ];      if ( grvIsEmpty(dateStr) ) return null;
		var date    = Date.parse(dateStr); if ( date == null        ) return null;

		switch ( dateID )
		{
	      case kDateIdCreate:
			if (grvAfterNow(dateStr))
			  return "Create date can't be in the future.";
			break;

	      case kDateIdLaunch:
	        dateStr = this[kDateIdCreate];
			if (grvIsEmpty(dateStr) || grvAfter(dateStr,date+1))
			  return "Launch date must be after Create date";
			break;

	      case kDateIdLockdown:
	        dateStr = this[kDateIdLaunch];
			if (grvIsEmpty(dateStr) || grvAfter(dateStr,date+1))
			  return "Lockdown date must be after Launch date";
			break;

	      case kDateIdCutoff:
	        dateStr = this[kDateIdLockdown];
			if (grvIsEmpty(dateStr) || grvAfter(dateStr,date+1))
			  return "Cutoff date must be after Lockdown date";
			break;

	      case kDateIdAward:
	        dateStr = this[kDateIdCutoff];
			if (grvIsEmpty(dateStr) || grvAfter(dateStr,date+1))
			  return "Bid Award date must be after Cutoff date";
			break;

	      case kDateIdClosed:
	        dateStr = this[kDateIdAward];
			if (grvIsEmpty(dateStr) || grvAfter(dateStr,date+1))
			  return "Close date must be after Bid Award date";
			break;

	      default:
	      	return "Illegal Auction State";
		}

		return null;	//no error
	}
	
	/** return the current auction phase or null if in an illegal state @type String */
	this.nextState = function()
	{
		if (this.isValid()){
		  for (var i=0; i<Auction.States.length; ++i){
		    var stateID = Auction.States[i];
			if ( grvIsEmpty(this[stateID]) ) return stateID;
		  }
		}
		return null;//illegal state
	}

	/** custom validation method to verify uniqueness of auction ID
	 * @type String
	 * @return error message or null if no error
	 */
	this.validateBSID = function()
	{
		if ( this.hasDuplicateBSID()) return "Duplicate Auction IDs are not allowed.";
		return null;
	}

	/** verify uniqueness of Auction ID (as best we can)
	 * @type boolean
	 * @return true iff our auction ID duplicates any in parent auction summary list
	 */
	this.hasDuplicateBSID = function() {
		var key = this.holder.model.auctionKey( this[kStrIdAuctionID] );
		return (key>0) && (key!=this.key);
	}

	/** return a clone of this object @type Auction */
	this.clone = function() {
		return new Auction( this.propValues(), this.date, this.whom,
			this.key, this.holder, this.itemCount, this.editStatus );
	}

	this.canCreate = function(appendFlag) {
	  return this.holder.isTrue()
		 ? "Can't add a new Auction until pending changes have been saved"
		 : null;
	}

	/** If created, can we undo creation? @type boolean
	 * @param {boolean} appendFlag if true then append else insert
	 */
	this.canUncreate = function(appendFlag){ return false; }//override me
}


////////////////////////////////////////////////////////////////////////////////

var kSpecialLotName = "special unassigned items lot";

Class(Lot,["property values array","edit Date","edit User",
				"unique Key","parent holder","item count"])
.Extends(ALEDO);
/**
 * @class This class encapsulates a Auction "lot" record.
 * @extends ALEDO
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function Lot()
{
	// init "static" Class methods/elements
	if (!Lot.Properties) {//keep this line above jsdoc comments!

		/** Static factory method to generate blank Lot @type Lot */
		Lot.Factory = function(index,holder)
		{
			var x = new Lot( null,grvGetTodayAsMMDDYYYY(),kUserID,null,holder,0 );
			x[ kAmtIdExpPerItem ] = 0;
			x[ kPctIdActualBid  ] = 0;
			x[ kPctIdCommission ] = 0;
			x[ kPctIdEstTarget  ] = 0;
			x[ kIntIdBidderKey  ] = 0;
			x.expanded = true;
			return x;
		}

		// init static member variables
		Lot.UpdateID   = "Lot";
		Lot.Properties = [ kIntIdLotNum, kStrIdLotName,
				kDateIdPayDate, kDateIdClosed, kAmtIdExpPerItem,
				kPctIdActualBid, kPctIdCommission, kPctIdEstTarget,
				kIntIdContactsKey, kIntIdShippingKey,
				//transient field(s)
				kIntIdBidderKey
				 ];
		Lot.EditRules  = [ MVCEditRule.kINT, MVCEditRule.kEdit,
				MVCEditRule.kDate, MVCEditRule.kDate, MVCEditRule.kDec,
				MVCEditRule.kDec, MVCEditRule.kDec, MVCEditRule.kDec,
				MVCEditRule.kInt, MVCEditRule.kInt,
				//transient field(s)
				MVCEditRule.kEdit
				 ];
	}

	/** @param {array} array of editable property values in same order as .Properties
	 *  @param {String} editDate date of last edit
	 *  @param {String} editUser ID of last user to edit
	 *  @param {int} theKey unique key of this record
	 *  @param {AuctionModel} holder parent EDOHolderModel
	 *  @param {int} itemcount how many Items are linked to this
	 *  @param {int} optEditStatus optional edit status
	 *  @param {String} optName optional name of this instance
	 */
	this.konstructor = function
	( propArray, editDate, editUser, theKey, holder, itemcount, optEditStatus, optName )
	{
		this.souper( holder, itemcount, Lot,
		 propArray, editDate, editUser,	theKey, optEditStatus, optName );
		
		this[ kIntIdLotNum+MVCAttributeModel.kValidateSuffix ] = this.validateLotNum;
		this.expanded = false;
	}

	this.hasLotNum = function( n ){ return n == this[kIntIdLotNum]; }

	this.isLotZero = function(){
		return grvIsEmpty(this[kIntIdLotNum]) ? false
		 : (this[kIntIdLotNum]==0 && this[kStrIdLotName]==kSpecialLotName);
	}

	/** custom validation method to verify uniqueness of Lot number
	 * @type String
	 * @return error message or null if no error
	 */
	this.validateLotNum = function()
	{
		if ( this.isLotZero()         ) return null;
		if ( this[kIntIdLotNum]==0    ) return "Lot number zero is only for special lot.";
		if ( this.hasDuplicateLotNum()) return "Duplicate Lot numbers are not allowed.";
		return null;
	}

	/** custom validation method to verify uniqueness of Lot number
	 * @type boolean
	 * @return true iff our lot number duplicates any in parent auction
	 */
	this.hasDuplicateLotNum = function() {
		return this.holder.lotCount( this[kIntIdLotNum] ) > 1;
	}

	/** return a clone of this object @type Lot */
	this.clone = function() {
		return new Lot( this.propValues(), this.date, this.whom,
			this.key, this.holder, this.itemCount, this.editStatus );
	}

	this.toString = function() {
		var s = grvIsEmpty(this[kIntIdLotNum ]) ? "" : ("#"+this[kIntIdLotNum]);
		var t = grvIsEmpty(this[kStrIdLotName]) ? "" : (" "+this[kStrIdLotName]);
		return s+t;
	}

	/** Is it legal to edit "this"?
	 * @return errmsg or null
	 * @type String
	 */
	this.canEdit = function(){
		if ( kReadOnly ) return "Not allowed in read-only mode.";
		if (!this.isActive()) return "This is deleted.";
		return this.isLotZero() ? "Lot 0 is not editable." : null;
	}

	/** mark this entity as having been edited */
	this.editMe = function()
	{
		this.souper();
		_GetBidderLists( this[kIntIdBidderKey] );
	}
}

////////////////////////////////////////////
////////////// Data Models /////////////////
////////////////////////////////////////////

Class(AuctionModel,["Auction Selection Model"]).Extends(EDOHolderModel);
/**
 * @class This class encapsulates the data model of a particular Auction.
 * This model subscribes to a {@link AuctionSelectionModel}
 * so that new Lot lists can be downloaded whenever a new Auction is selected.
 * <p>NOTE: Other parts of AIM assume that the lot lists will never be null;
 * zero items is ok though.</p>
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function AuctionModel()
{
	/** @param {AuctionSelectionModel} auctionSelectionModel model of which Auction is selected
	 *  @param {String} optName optional name of this instance
	 */
	this.konstructor = function( auctionSelectionModel, optName )
	{
		// init static DOM (must do this here instead of at static load time!)
		if (!AuctionModel.LoadXslDOM) AuctionModel.LoadXslDOM = grvGetXslDOM( kXSLPath+"AIMaucToJS.xsl" );
		var holderName = optName ? optName : "Auction";
		this.souper( auctionSelectionModel, AuctionModel.LoadXslDOM, holderName );

		// init instance variables
		this._resetEDOs();
	}

	/** return the parent Auction ID for this model @type String */
	this.getID = function(){
		return this.auction ? this.auction[kStrIdAuctionID] : null;
	}

	/** return whether any EDOs are changed for this model @type boolean */
	this.isDirty = function()
	{
		if (this.auction && this.auction.inEdit()) return true;
		if (this.lList.isDirty()) return true;
		return false;
	}

	/** return whether all EDOs are valid for this model @type boolean */
	this.isValid = function()
	{
		if (!this.auction || !this.auction.isValid()) return false;

		//special case: if we are deleting auction then other errs dont matter
		if (this.auction.isDeleted()) return true;

		return this.lList.isValid();
	}

	/** clear EDOs */
	this._resetEDOs = function( optAuction )
	{
		this.auction = optAuction ? optAuction : null;

		if ( this.lList ) this.lList.reset();
		else this.lList = new EDOListModel( Lot );
	}

	/** set our Auction object   but dont publish */
	this._setAuction = function(o){ this.auction = o; }

	/** get a new Lot list @type ListModel */
	this._newLots = function(){ return this.lList.reset(); }

	/** return the debug details of "this" @type String */
	this.dump = function()
	{
		var		dStr = new Array();
				dStr.push( "AuctionModel>>>[" );
				dStr.push( "auction=" +(this.auction ?this.auction .dump():"null") );
				dStr.push( "lots="    +(this.lList   ?this.lList   .dump():"null") );
				dStr.push( "]" );
		return	dStr.join("\n");
	}

	/** create a new item for the selection list from current EDO
	 * @type EDOHolderSelectionItem
	 */
	this.createSelectionItem = function() {
		return new AuctionSummary( this.auction.key, this.auction[kStrIdAuctionID], 0 );
	}
	/** return the updated properties from the current EDO needed for select list update
	 * @return arbitrary value as needed @type Object
	 */
	this.getUpdatedProperty = function() {
		return this.getID();
	}

	/** return how many lots have the given lot number @type int */
	this.lotCount = function( lotnum )
	{
		var count = 0;
		this.lList.iterate( function(i,lot){ if (lot.hasLotNum(lotnum)) ++count; } );
		return count;
	}

	/** return whether there is currently an EDO @type boolean */
	this.hasEDO = function(){ return this.auction!=null; }

	/** return whether current EDO matches selection item "key"
	 * @type boolean
	 * @param {EDOHolderSelectionItem} item selection menu item
	 */
	this.keysMatch = function(item){ return this.auction.key==item.key; }

	/** return whether current EDO matches selection item "properties"
	 * @type boolean
	 * @param {EDOHolderSelectionItem} item selection menu item
	 */
	this.propMatch = function(item){ return this.getID()==item.bsID; }

	/** push into given array all AJAX SAVE Post params for this model
	 * @param {Array} posts  string array containing POST params
	 */
	this.pushPosts = function( posts )
	{
		this.auction.pushPosts( posts );	//THIS **MUST** BE FIRST!!
		this.lList  .pushPosts( posts );
	}

	/** return a new blank EDO of the type implied by EDOIndex @type EDO
	 * @param {EDOIndex} xi edo index
	 */
	this._newEDO = function(xi) { 
		return xi.invoke( this, Auction.Factory, Lot.Factory, this );
	}

	/** return the specified lot @type Lot
	 * @param {int} i index into lot list
	 */
	this._getLot = function( i ) {
	  return (i!=null && i>=0
	          && i<this.lList.getCount())
				 ? this.lList.getItem( i ) : null;
	}

	/** return the specified auction @type Auction
	 * @param {int} i index into auction list
	 */
	this._getAuction = function( i ) {
	  return (i==0 && this.auction)
	                ? this.auction : null;
	}
	/** return reference to the specified editable domain object
	 * @param {ALIndex} xi edo index
	 */
	this.getEDO  = function(xi){
		return xi.invoke( this, this._getAuction, this._getLot );
	}

	/** insert a new lot
	 * @param {int} i lot list index
	 * @param {EDO} edo the shipping
	 */
	this._insertLot = function( xi ) {
	    this.lList.addBefore( i, edo );
	}
	/** create new EDO which is inserted after selected EDO
	 * @return EDOIndex of new EDO
	 * @type EDOIndex
	 * @param {EDOIndex} xi edo index
	 */
	this.createEDO = function(xi){
		xi.invoke( this, this._newAuction, this._insertLot, this.newEDO(xi) );
		return xi;
	}

	/** append a new lot
	 * @return EDOIndex of new EDO
	 * @type EDOIndex
	 * @param {int} i lot list index
	 * @param {EDO} edo the lot
	 */
	this._appendLot = function( i, edo ) {
	    return ALIndex.Lot( this.lList.addItem( edo ) );
	}
	/** create a new auction
	 * @return EDOIndex of new EDO
	 * @type EDOIndex
	 * @param {int} i auction list index
	 * @param {EDO} edo the auction
	 */
	this._newAuction = function( i, auctionEDO )
	{
		this._resetEDOs( auctionEDO );

		//auto-create special lot zero
		var lotEDO = this.newEDO(ALIndex.Lot(0));
		lotEDO[ kIntIdLotNum  ] = 0;
		lotEDO[ kStrIdLotName ] = kSpecialLotName;
		lotEDO.makeReadOnly();
		this._appendLot( 0, lotEDO );
		
		this.updateStamp();
		return ALIndex.Auction();
	}

	/** create new EDO which is appended at end of EDO list
	 * @return EDOIndex of new EDO
	 * @type EDOIndex
	 * @param {EDOIndex} xi edo index
	 */
	this.appendEDO = function(xi){
		return xi.invoke( this, this._newAuction, this._appendLot, this.newEDO(xi) );
	}

	/** toggle the "expanded" flag of the specified EDOs.
	 * @param {boolean} doAllFlag if true, set all of the EDOs to new value
	 * @param {EDOIndex} xi edo index
	 */
	this.toggleExpand = function( doAllFlag, xi )
	{
		var edo = this.getEDO(xi);
		if (doAllFlag)
		{
			var newVal = !edo.isExpanded();
			this.lList  .toggleExpandAll( newVal );
			this.auction.toggleExpand   ( newVal );
		}
		else edo.toggleExpand();
		this.updateStamp();
	}
}


Class(AuctionSelectionModel,["AuctionSummary List Model"]).Extends(EDOHolderSelectionModel);
/**
 * @class This class encapsulates the data model for
 * "which Auction is currently selected".
 * @extends EDOHolderSelectionModel
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function AuctionSelectionModel()
{
	/** @param {ListModel} listModel {@link AuctionSummary} list */
	this.konstructor = function( listModel ){
		this.souper( listModel, "Auction " );
	}

	/** Return the list index of the specified Auction or <0 if not found. @type int */
 	this.findAuction = function( auctionID, auctionKey )
	{
		var list = this.model;
		var N    = list.getCount();
		if (N<1) return -2;//NO DATA IN auction SUMMARY LIST!!

		for (var i=0; i<N; ++i)
		  if (list.getItem(i).bsID == auctionID
		  ||  list.getItem(i).key  == auctionKey) return i;

		return -1;
	}

	/** return the key of the auction with the given auction ID @type int */
	this.auctionKey = function( auctionID )
	{
		var key = -1;
		this.model.iterate( function(i,ss){ if (ss.bsID==auctionID) key = ss.key; } );
		return key;
	}

	/** Select the "initially selected" EDOHolder<br>
	 * @param {boolean} selectFirst if selection critera has no match then
	 * if selectFirst==true then select the first in list
	 *                      else leave selection alone 
	 */
 	this.selectInitial = function( selectFirst )
	{
		var i = this.findAuction( kAuctionID, kAuctionKey );
		if (i==-2){ this.selectNothing(); return; }//EMPTY LIST!!
		if (i>= 0){ this.select(i);       return; }//found it
		if (selectFirst) this.select(0);
	}
}

////////////////////////////////////////////
////////////// Controllers /////////////////
////////////////////////////////////////////


Class(PhaseButtonController,["button text model"])
.Extends(MVCTxtButtonController);
/**
 * @class This class is a Button Controller that manages the button that
 * "moves" an auction from one phase to the next.
 * @extends MVCTxtButtonController
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function PhaseButtonController()
{
	/** @param {Auction} auction Auction we are to observe
	 */
	this.konstructor = function( auction ) {
		this.souper( this, "Declare Phase as met", "onPhaseBtnPressed" );

		// init instance variables
		this.auction = auction;
		this.enabld  = false;
	}

	/** Return whether this button should be enabled. @type boolean */
	this.isEnabled = function() {
		return kReadOnly ? false : (this.enabld && this.auction.inEdit());
	}

	/** simulate MVCScalarModel interface enough to supply button text @type String */
	this.getValue = function()
	{
		this.enabld = true;
		switch ( this.auction.nextState() )
		{
	      case kDateIdLaunch:	return "Launch Auction";
	      case kDateIdLockdown:	return "Lockdown Auction";
	      case kDateIdCutoff:	return "Cutoff Auction";
	      case kDateIdAward:	return "Award Bids";
	      case kDateIdClosed:	return "Close Auction";
		}
		this.enabld = false;
		return "N/A";
	}
}


////////////////////////////////////////////
////////////// Data Views  /////////////////
////////////////////////////////////////////

Class(AuctionView,["Auction object"])
.Extends(EDOView);
/**
 * @class This class produces a view of a {@link Auction}.
 * @extends EDOView
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function AuctionView()
{
	/** @param {Auction} auction Auction we are to observe
	 */
	this.konstructor = function( auction ) {
		this.souper( auction, ALIndex.Auction() );
	}

	this.buildFields = function( HTML )
	{
		HTML.push( this.buildUprField( kStrIdAuctionID, 10, 10, false ) );

		HTML.push( this.buildDayField( kDateIdCreate  ,         false ) );
		HTML.push( this.buildDayField( kDateIdLaunch  ,         false ) );
		HTML.push( this.buildDayField( kDateIdLockdown,         false ) );

		HTML.push( this.buildDayField( kDateIdCutoff  ,         false ) );
		HTML.push( this.buildDayField( kDateIdAward   ,         false ) );
		HTML.push( this.buildDayField( kDateIdClosed  ,         false ) );

		HTML.push( "<td>"+ this.embedHTML( this.getWidgetID()+".btn",
							new PhaseButtonController( this.model ) )
				  +"</td>" );

	}
}

Class(LotView,["Lot object","EDOIndex of Lot"])
.Extends(EDOView);
/**
 * @class This class produces a view of a {@link Lot}.
 * @extends EDOView
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function LotView()
{
	/** @param {Lot} edo EDO we are to observe
	 *  @param {EDOIndex} edoIndex index to EDO
	 */
	this.konstructor = function( edo, edoIndex ) {
		this.souper( edo, edoIndex );
	}

	this.preSelect = function()
	{
		var bk = this.model[ kIntIdBidderKey ];
		this  .sharedMenuSelect(   gBidderSelected    , kIntIdBidderKey );
		this.cascadedMenuSelect( _ContactsSelected(bk), kIntIdContactsKey );
		this.cascadedMenuSelect( _ShippingSelected(bk), kIntIdShippingKey );
	}

	this.buildFields = function( HTML )
	{
		var bidderKey = this.model[kIntIdBidderKey];

		HTML.push( this.buildStrField( kIntIdLotNum      ,  3,  3, false ) );
		HTML.push( this.buildStrField( kStrIdLotName     , 50, 50, false, 3 ) );
//		HTML.push( this.buildStrField( kIntIdBidderKey   , 10, 10, true  ) );
		HTML.push( this.buildPopField( kIntIdBidderKey   ,         true,
			gBidderSelected, 1, "onBidderSelect" ) );

		HTML.push( this.buildNulField(                             false ) );
		HTML.push( this.buildDayField( kDateIdPayDate    ,         false ) );
		HTML.push( this.buildPctField( kPctIdActualBid   ,         false ) );
		HTML.push( this.buildAmtField( kAmtIdExpPerItem  ,         false ) );
//		HTML.push( this.buildStrField( kIntIdContactsKey , 10, 10, true  ) );
		HTML.push( this.buildPopField( kIntIdContactsKey ,         true,
			_ContactsSelected(bidderKey),null,null,this.model.contName   ) );

		HTML.push( this.buildNulField(                             false ) );
		HTML.push( this.buildDayField( kDateIdClosed     ,         false ) );
		HTML.push( this.buildPctField( kPctIdCommission  ,         false ) );
		HTML.push( this.buildPctField( kPctIdEstTarget   ,         false ) );
//		HTML.push( this.buildStrField( kIntIdShippingKey , 10, 10, true  ) );
		HTML.push( this.buildPopField( kIntIdShippingKey ,         true,
			_ShippingSelected(bidderKey),null,null,this.model.shipName ) );
	}
}


Class(LotListView,["EDO ListModel"])
.Extends(EDOListView);
/**
 * @class This class produces a view of a List of Lot EDOs.
 * @extends EDOListView
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function LotListView()
{
	/** @param {EDOListModel} edoListModel EDO list we are to observe
	 */
	this.konstructor = function( edoListModel ) {
		this.souper( edoListModel, LotView );
	}

	this.itemEDOIndex = function( index ) {
		return ALIndex.Lot( index );
	}
}


Class(AuctionModelView,["Auction data model"])
.Extends(EDOHolderView);
/**
 * @class This class produces the view of the entire data panel.
 * @extends EDOHolderView
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function AuctionModelView()
{
	/** @param {AuctionModel} auctionModel Auction/lots we are to observe */
	this.konstructor = function( auctionModel ) {
		this.souper( auctionModel );
	}

	this.buildFields = function( HTML )
	{
	  var vwID = this.getWidgetID();

	  if ( !this.model.auction || this.model.auction.inLimbo() )
	  {
		if (!grvWAITING())
		HTML.push( EDOView.ShellHTML( ALIndex.Auction(), Auction ) );
	  }
	  else if ( this.model.auction.isActive() )
      {
		HTML.push( this.embedHTML( vwID+".auctionWdgt",
						new AuctionView( this.model.auction ) ) );

		HTML.push( this.embedHTML( vwID+".lotWdgt",
		                new LotListView( this.model.lList   ) ) );
      }
      else HTML.push( '<thead>This Auction is deleted. You must Cancel, Save, or Undo.</thead>' );
	}
}
				////////////////////////////////////////////
				//////////////// Commands //////////////////
				////////////////////////////////////////////

/** Load in contacts and shippings list from specified bidder<br>
 *  NOTE: this is a Command helper function.<br>
 *  NEVER call this from outside the context of a Command object!
 * @param {long} bidderKey unique key of bidder
 */
function _GetBidderLists( bidderKey )
{
	if (_ContactsSelected(bidderKey).loaded) return;//already loaded
	_gGrvHackStartupInhibitWait = true;
	EDOHolderModel.AjaxRequest( "Bidder", bidderKey, onBidderLoadReply, "", true );
}

function _ContactsSelected( bidderKey )
{
	if (gContactsSelected[bidderKey]){/*already created*/} else
	{
		var bs = "#"+bidderKey;
		var L  = new MVCMapModel(bs+":Contacts List");
		L.map.addItem( 0, new MVCDecode( "Contact", "0", "", 1 ) );
		gContactsSelected[bidderKey] = new MVCSelectionModel( L, bs+":shared contact selection model" );
		gContactsSelected[bidderKey].loaded = false;
	}
	return gContactsSelected[bidderKey];
}

function _ShippingSelected( bidderKey )
{
	if (gShippingSelected[bidderKey]){/*already created*/} else
	{
		var bs = "#"+bidderKey;
		var L  = new MVCMapModel(bs+":Shipping List");
		L.map.addItem( 0, new MVCDecode( "Shipping", "0", "", 1 ) );
		gShippingSelected[bidderKey] = new MVCSelectionModel( L, bs+":shared shipping selection model" );
		gShippingSelected[bidderKey].loaded = false;
	}
	return gShippingSelected[bidderKey];
}


Class(BidderMenuCmd,["view ID"]).Extends(EDOSuperMenuCmd);
/**
 * @class This class implements the bidder menu edit command.
 * @extends EDOSuperMenuCmd
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function BidderMenuCmd()
{
	/** @param {String} viewID viewID of menu generating this event */
	this.konstructor = function( viewID ) {
		this.souper( viewID );
	}

	this.updateEDO = function( exemplar, isRedo )
	{
		this.edo[ kIntIdContactsKey ] = exemplar[ kIntIdContactsKey ];
		this.edo[ kIntIdShippingKey ] = exemplar[ kIntIdShippingKey ];
	}

	this.prefillEDO = function()
	{
		_GetBidderLists( this.newValue );
		this.updateOtherController( kIntIdContactsKey, 0 );
		this.updateOtherController( kIntIdShippingKey, 0 );
	}
}

Class(BidderLoadReplyCmd,["xml request object"]).Extends(MVCAJAXReplyCmd);
/**
 * @class This Command processes the reply of the "load Bidder data" request.
 * NOTE: It isnt undoable (because we lose all edits!).
 * @extends MVCAJAXReplyCmd
 * @see #konstructor
 * @deprecated ajax call was made synchronous to make undo possible
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function BidderLoadReplyCmd()
{
	/** @param {XMLreq} xmlReq the XML request being replied to
	 *  @param {String} optName optional name of this instance
	 */
	this.konstructor = function( xmlReq, optName ) {
		if (!BidderLoadReplyCmd.DOM)  BidderLoadReplyCmd.DOM = grvGetXslDOM( kXSLPath+"AIMbidListsToJS.xsl" );
		this.souper( xmlReq, BidderLoadReplyCmd.DOM, optName );
	}
}

Class(NextPhaseCmd).Extends(EDOCmd);
/**
 * @class This class implements the Move to Next Auction Phase cmd.
 * @extends EDOCmd
 * @see #konstructor
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function NextPhaseCmd()
{
	this.konstructor = function(){
		this.souper("move to next phase", gEDOSelected.canEditSelected());
		this.auctionEDO = gEDOSelected.getSelected();
		this.dateID     = this.auctionEDO.nextState();
		if (this.dateID==null) this.state = -1;
		this.dateStr    = grvFormatDate( new Date(), "mm/dd/yyyy" );
	}

	this.doit = function(){ this.auctionEDO[ this.dateID ] = this.dateStr; }
	this.undo = function(){ this.auctionEDO[ this.dateID ] =           ""; }
}

////////////////////////////////////////////
////////// Static Event Handlers ///////////
////////////////////////////////////////////

/** handle event for 'user selects from the bidder menu'
 * @param {String} viewID ID of the controller generating this event
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function onBidderSelect( viewID ){
	mvcDoCmd( new BidderMenuCmd( viewID ) );
	return true;
}

/** handle event for 'reply received from "load Bidder data" server-request'
 * @param {XMLreq} xmlReq the XML request being replied to
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function onBidderLoadReply( xmlReq ){
	//replace async with sync to allow undo
	//mvcDoCmd( new BidderLoadReplyCmd( xmlReq ) );

	if (!onBidderLoadReply.DOM) onBidderLoadReply.DOM = grvGetXslDOM( kXSLPath+"AIMbidListsToJS.xsl" );
	
	//translate received XML into javascript (via XSL) and save
	onBidderLoadReply.script = xmlReq.xform2( onBidderLoadReply.DOM );
	if (gGrvTraceEvt) grvDebugWindow( onBidderLoadReply.script );
	eval( onBidderLoadReply.script );
}

/** handle event for 'next phase' button press
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function onPhaseBtnPressed(){
	mvcDoCmd( new NextPhaseCmd() );
}


The Gravey 2.0 Framework and AIM RIA

Documentation generated by JSDoc on Sat Dec 8 21:51:03 2007