//----------------------------------------------------------------------------
// JavaScript object for managing the Pixami PrintClient
//----------------------------------------------------------------------------

// Type of client
var kClientNone    = 0;
var kClientActiveX = 1;
var kClientPlugIn  = 2;
var kAjax = 1;

var count = 0;
var strJSCommError = "There was a problem communicating with the server.  You may be able to clear this by changing to a different page.  If you continue having problems, you may try refreshing the page by pressing F5.";
	
function PxPrintClient2(clientType)
{
	// Initialize all mode parameters
	this.clientType = clientType;

	// Objects for controlling UI and processing
	this.thePrintForm = null;
	this.thePrintClientFrame = null;
	this.thePrintImageObj = null;
	this.theFrameQueryObj = null;
	this.theClientObj = null;
	this.strWebServerPage = "";
	this.strImageServerPage = null;

	// Variables for controlling server-based updates
	this.minResolution = 0;
	this.strProxyText = null;
	this.strProxyColor = null;
	this.minProxySize = 16;
	this.workingBitmap = "images/working.gif"
	this.workingDiv = null;
	this.bInUpdate = false;

	// Variables for managing current frame
	this.frameList = null;
	this.activeFrame = 0;
	this.resetFrame = -1;

	// Variables for managing current page
	this.currPrintPageID = 0;
	this.currPageDim = "";
	this.prevPrintPageID = "";
	this.nextPrintPageID = "";
	this.pageList = null;
	this.pageBackground = "";

	// Variables for maintaining margins
	this.leftMargin   = 0;
	this.topMargin    = 0;
	this.rightMargin  = 0;
	this.bottomMargin = 0;
	this.pageLeftMargin   = 0;
	this.pageTopMargin    = 0;
	this.pageRightMargin  = 0;
	this.pageBottomMargin = 0;
	this.bPageMargins = false;

	// Clip art information
	this.clipArtInfo = null;

	// Don't use cookies by default for backward compatibility
	this.useCookies = 0;
	this.cookieDomain = null;
	this.cookiePath = null;
	this.pageUpdateCallback = null;
	this.queryImageObj = null;

	// To signal album processing
	this.strRefID = null;

	// To specify JPEG quality of preview.  Use default quality initially.
	this.strQuality = "";

	// Padding color around image preview
	this.strPad = "";

	// Product ID
	this.iProductID = 0;
	this.cropEdges = "";
	this.cropLines = "";

	// CoverType
	this.strCoverType = "";

	// Old global variables for managing display
	this.printUpdateURL = "";
	this.processPageInfo = 0;

	// Update completion callback
	this.updateCompleteCallback = null;
	
	// Callback when layout changes
	this.layoutChangedCallback = null;
	this.bChangedLayout = false;
	
	// Special flags
	this.bShowGray = false;
	this.bSpreadPages = false;
}

function PxClipArtInfo(strFileName, width, height)
{
	this.strFileName = strFileName;
	this.width       = parseInt(width);
	this.height      = parseInt(height);
}

PxPrintClient2.prototype = {

	UseCookies : function(useCookies, pageUpdateCallback, queryImageObj)
	{
		this.useCookies = useCookies;
		this.pageUpdateCallback = pageUpdateCallback;
		this.cookieDomain = null;
		this.cookiePath = null;
	},

	UseCookiesEx : function(useCookies, pageUpdateCallback, queryImageObj, strCookieDomain, strCookiePath)
	{
		this.useCookies = useCookies;
		this.pageUpdateCallback = pageUpdateCallback;
		this.cookieDomain = strCookieDomain;
		this.cookiePath = strCookiePath;
	},

	SetUpdateCompleteCallback : function(fn)
	{
		this.updateCompleteCallback = fn;
	},

	SetQuality : function(iQuality)
	{
		this.strQuality = "&Q=" + iQuality;
	},

	SetPadColor : function(strColor)
	{
		this.strPad = "&Pad=" + strColor;
	},

	SetRefID : function(strRefID)
	{
		this.strRefID = strRefID;
	},

	SetWorkingImage : function(workingImage)
	{
		this.workingBitmap = workingImage;
	},

	SetWorkingDiv : function(workingDiv)
	{
		this.workingDiv = workingDiv;
	},

	SetForm : function(printForm)
	{
		// Save the form to use for submission
		this.thePrintForm = printForm;
	},

	SetClientFrame : function(printClientFrame)
	{
		// Save the frame to use for the print client.
		this.thePrintClientFrame = printClientFrame;
	},

	SetWebApplicationPage : function(webServerPage)
	{
		// Save the page to use for form submittal
		this.strWebServerPage = webServerPage;
	},

	SetImageServerPage : function(imageServerPage)
	{
		// Save the page to use in the client for getting proxies, etc.
		this.strWebServerPage = imageServerPage;
	},

	SetPreviewImage : function(printImageObj)
	{
		// Save IMG object for server-based editing.
		this.thePrintImageObj = printImageObj;

		// Set up callbacks
		var oThis = this;
		this.thePrintImageObj.onload  = function() { oThis.ImageLoaded() };
		this.thePrintImageObj.onabort = function() { oThis.ImageAbort() };
		this.thePrintImageObj.onerror = function() { oThis.ImageError() };
		this.printImageWidth = this.thePrintImageObj.width;
		this.printImageHeight = this.thePrintImageObj.height;
	},

	SetParentDiv : function(parentDiv)
	{
		// Save IMG object for server-based editing.
		this.parentDiv = parentDiv;
	},

	SetClientControl : function(printClientObj)
	{
		// Save the control for client-based editing
		this.theClientObj = printClientObj;
	},

	InitializeClientControl : function(
			printClientObj, bScrollBar, backgroundColor, 
			bShowFrame, frameHighlightColor, frameShadowColor, selectColor,
			numRows, numCols, rowSpacing, colSpacing)
	{
		// Save the control for client-based editing
		this.theClientObj = printClientObj;

		// Initialize display mode for control first
		this.theClientObj.SetDisplayMode(bScrollBar, PxConvertColorSpec(backgroundColor), 
				bShowFrame, PxConvertColorSpec(frameHighlightColor), PxConvertColorSpec(frameShadowColor), 
				PxConvertColorSpec(selectColor));

		// Initialize the layout of the control
		this.theClientObj.SetDisplayLayout(numRows, numCols, rowSpacing, colSpacing);

		// Finally, initialize the control itself
		var strURL = this.strWebServerPage;
		if (strURL.indexOf("://") == -1)
			strURL = documentBase + strURL;
		this.theClientObj.SetImageRef(strURL, "", 1);
	},

	SetPageInfo : function()
	{
		// Get current page ID.  If non-zero, then update image
		this.currPrintPageID = this.thePrintForm.elements["Curr"].value;

		// Set up previous and next pages
		var strPageList = this.thePrintForm.elements["Pgs"].value;
		if (strPageList != "")
		{
			this.pageList = strPageList.split(",");
			if (this.pageList.length > 1)
			{
				for (var i = 0; i < this.pageList.length; i++)
				{
					if (this.pageList[i] == this.currPrintPageID)
					{
						if (i != 0)
							this.prevPrintPageID = this.pageList[i-1];
						if (i != (this.pageList.length - 1))
							this.nextPrintPageID = this.pageList[i+1];
						break;
					}
				}
			}
		}
		else
			this.pageList = null;
	},

	SetPageInfoEx : function(currPageID, strPageList)
	{
		// Get current page ID.  If non-zero, then update image
		this.currPrintPageID = currPageID;

		// Set up previous and next pages
		if (strPageList != "")
		{
			this.pageList = strPageList.split(",");
			if (this.pageList.length > 1)
			{
				for (var i = 0; i < this.pageList.length; i++)
				{
					if (this.pageList[i] == this.currPrintPageID)
					{
						if (i != 0)
							this.prevPrintPageID = this.pageList[i-1];
						if (i != (this.pageList.length - 1))
							this.nextPrintPageID = this.pageList[i+1];
						break;
					}
				}
			}
		}
		else
			this.pageList = null;
	},

	UpdateInitialPage : function(printClientObj)
	{
		if ((this.currPrintPageID != 0) && (this.clientType == kClientNone)) 
		{
			this.processPageInfo = 1;
			this.UpdatePrintPageImage("current", "");
		}
	},

	UpdatePage : function()
	{
		this.resetFrame = this.activeFrame;
		this.UpdatePrintPageImage("current", "");
	},

	SetFrameInfo : function(strFrameInfo)
	{
			// Only need for server-based
			if (this.clientType != kClientNone)
				return;
			if (strFrameInfo == "")
				return;

			// Extract frameList from **FrameInfo**
			this.frameList = new FrameList();
			var frameArray = strFrameInfo.split("\f");

			// Get dimensions of page from frame info
			var width = parseInt(frameArray[0]);
			var height = parseInt(frameArray[1]);
			this.currPageDim = Math.floor(width / 2) + "," + Math.floor(height / 2);

			// Get mapping to screen coordinates
			var xScale = this.printImageWidth  / width;
			var yScale = this.printImageHeight / height;
			if (yScale < xScale)
				xScale = yScale;
			var targetWidth  = Math.floor(width  * xScale);
			var targetHeight = Math.floor(height * xScale);
			var xOffset = Math.floor((this.printImageWidth - targetWidth) / 2);
			var yOffset = Math.floor((this.printImageHeight - targetHeight) / 2);

			for (var i = 2; i < frameArray.length; )
			{
				var type = parseInt(frameArray[i]);
				var left = Math.floor((parseInt(frameArray[i+1])   * xScale) + xOffset);
				var top = Math.floor((parseInt(frameArray[i+2])    * xScale) + yOffset);
				var right = Math.floor((parseInt(frameArray[i+3])  * xScale) + xOffset);
				var bottom = Math.floor((parseInt(frameArray[i+4]) * xScale) + yOffset);
				if ((type & 3) == 1)
				{
					var style = parseInt(frameArray[i+5]);
					var feather = parseInt(frameArray[i+6]);
					var edgeRef = new String(frameArray[i+7]);

					this.frameList.AddItem(type, left, top, right, bottom,
									  style, feather, edgeRef,
									  "", "", 0, "");
				}
				else
				{
					var text = new String(frameArray[i+5]);
					var font = new String(frameArray[i+6]);
					var size = parseInt(frameArray[i+7]);
					var color = new String(frameArray[i+8]);
					this.frameList.AddItem(type, left, top, right, bottom,
									  0, 0, "",
									  text, font, size, color);
				}
				this.frameList.items[this.frameList.items.length-1].realLeft = parseInt(frameArray[i+1]);
				this.frameList.items[this.frameList.items.length-1].realTop = parseInt(frameArray[i+2]);
				this.frameList.items[this.frameList.items.length-1].realRight = parseInt(frameArray[i+3]);
				this.frameList.items[this.frameList.items.length-1].realBottom = parseInt(frameArray[i+4]);
				i = i + 9;
			}

			// Save coordinate info in frame list object for later retrieval
			this.frameList.width  = width;
			this.frameList.height = height;
			this.frameList.scale  = xScale;
			this.frameList.left   = xOffset;
			this.frameList.top    = yOffset;
			this.frameList.right  = xOffset + targetWidth;
			this.frameList.bottom = yOffset + targetHeight;

			// Set margins in screen coordinates
			this.leftMargin   = Math.floor(this.pageLeftMargin   * this.frameList.scale);
			this.topMargin    = Math.floor(this.pageTopMargin    * this.frameList.scale);
			this.rightMargin  = Math.floor(this.pageRightMargin  * this.frameList.scale);
			this.bottomMargin = Math.floor(this.pageBottomMargin * this.frameList.scale);
	},

	SetFrameInfoEx : function(strFrameInfo, strFrameInfoEx, strEmptyList)
	{
			// Only need for server-based
			if (this.clientType != kClientNone)
				return;
			if (strFrameInfo == "")
				return;

			// Static Item 
			var i;
			var strStatic = PxGetValueFromXML(strFrameInfoEx, "Static");
			var astrStatic = strStatic.split("\f");

			var strShowFrame = PxGetValueFromXML(strFrameInfoEx, "ShowFrame");
			var astrShowFrame = strShowFrame.split("\f");

			var strStyle = PxGetValueFromXML(strFrameInfoEx, "Style");
			var astrStyle = strStyle.split("\f");

			var strAlignment = PxGetValueFromXML(strFrameInfoEx, "Alignment");
			var astrAlignment = strAlignment.split("\f");

			var strBorders = PxGetValueFromXML(strFrameInfoEx, "Borders");
			var astrBorders = strBorders.split("\f");

			var strFixedAspect = PxGetValueFromXML(strFrameInfoEx, "Aspect");
			var astrFixedAspect = strFixedAspect.split("\f");

			var strResizable = PxGetValueFromXML(strFrameInfoEx, "Resize");
			var astrResizable = strResizable.split("\f");

			var strIgnoreMargins = PxGetValueFromXML(strFrameInfoEx, "IgnoreMargins");
			var astrIgnoreMargins = strIgnoreMargins.split("\f");

			var strImageJnl = PxGetValueFromXML(strFrameInfoEx, "ImageJnl");
			var astrImageJnl = strImageJnl.split("\f");

			var strRotatable = PxGetValueFromXML(strFrameInfoEx, "Rotatable");
			var astrRotatable = strRotatable.split("\f");

			var strRotate = PxGetValueFromXML(strFrameInfoEx, "Rotate");
			if (strRotate == "")
				strRotate = PxGetValueFromXML(strFrameInfoEx, "Rotatate");		// Bug in old version of server
			var astrRotate = strRotate.split("\f");

			var strShadow = PxGetValueFromXML(strFrameInfoEx, "Shadow");
			var astrShadow = strShadow.split("\f");

			// Extract frameList from **FrameInfo**
			this.frameList = new FrameList();
			var frameArray = strFrameInfo.split("\f");

			// Extract empty frame list from EmptyList
			var emptyArray = null;
			if (strEmptyList != null)
				emptyArray = strEmptyList.split(",");
			
			// Get dimensions of page from frame info
			var width = parseInt(frameArray[0]);
			var height = parseInt(frameArray[1]);
			this.currPageDim = Math.floor(width / 2) + "," + Math.floor(height / 2);

			// Get mapping to screen coordinates
			var xScale = this.printImageWidth  / width;
			var yScale = this.printImageHeight / height;
			if (yScale < xScale)
				xScale = yScale;
			this.xScale = xScale;
			var targetWidth  = Math.floor(width  * xScale);
			var targetHeight = Math.floor(height * xScale);
			var xOffset = Math.floor((this.printImageWidth - targetWidth) / 2);
			var yOffset = Math.floor((this.printImageHeight - targetHeight) / 2);

			var frameIndex = 0;
			for (var i = 2; i < frameArray.length; frameIndex++)
			{
				var type = parseInt(frameArray[i]);
				var left = Math.floor((parseInt(frameArray[i+1])   * xScale) + xOffset);
				var top = Math.floor((parseInt(frameArray[i+2])    * xScale) + yOffset);
				var right = Math.floor((parseInt(frameArray[i+3])  * xScale) + xOffset);
				var bottom = Math.floor((parseInt(frameArray[i+4]) * xScale) + yOffset);

				var staticItem = 0;
				if (frameIndex < astrStatic.length)
					staticItem = parseInt(astrStatic[frameIndex]);
				var showFrame = 2;
				if (frameIndex < astrShowFrame.length)
					showFrame = parseInt(astrShowFrame[frameIndex]);
				var style = 0;
				if (frameIndex < astrStyle.length)
					style = parseInt(astrStyle[frameIndex]);
				var alignment = 0;
				if (frameIndex < astrAlignment.length)
					alignment = parseInt(astrAlignment[frameIndex]);
				var borders = "";
				if (frameIndex < astrBorders.length)
					borders = astrBorders[frameIndex];
				var fixedAspect = 0;
				if (frameIndex < astrFixedAspect.length)
					fixedAspect = parseInt(astrFixedAspect[frameIndex]);
				var resizable = 0;
				if (frameIndex < astrResizable.length)
					resizable = parseInt(astrResizable[frameIndex]);
				var ignoreMargins = 0;
				if (frameIndex < astrIgnoreMargins.length)
					ignoreMargins = parseInt(astrIgnoreMargins[frameIndex]);
				var imageJnl = "";
				if (frameIndex < astrImageJnl.length)
					imageJnl = astrImageJnl[frameIndex];
				var rotatable = 0;
				if (frameIndex < astrRotatable.length)
					rotatable = parseInt(astrRotatable[frameIndex]);
				var rotate = 0;
				if (frameIndex < astrRotate.length)
					rotate = parseInt(astrRotate[frameIndex]);
				var shadow = "0,100";
				if (frameIndex < astrShadow.length)
					shadow = astrShadow[frameIndex];

				if ((type & 3) == 1)
				{
					var style = parseInt(frameArray[i+5]);
					var feather = parseInt(frameArray[i+6]);
					var edgeRef = new String(frameArray[i+7]);

					this.frameList.AddItemEx(type, left, top, right, bottom,
									  style, feather, edgeRef,
									  "", "", 0, "", 
									  staticItem, showFrame, style, alignment, borders,
									  fixedAspect, resizable, ignoreMargins, imageJnl,
									  rotatable, rotate, shadow);
				}
				else
				{
					var text = new String(frameArray[i+5]);
					var font = new String(frameArray[i+6]);
					var size = parseInt(frameArray[i+7]);
					var color = new String(frameArray[i+8]);
					this.frameList.AddItemEx(type, left, top, right, bottom,
									  style, 0, "",
									  text, font, size, color, 
									  staticItem, showFrame, style, alignment, borders,
									  fixedAspect, resizable, ignoreMargins, "",
									  rotatable, rotate, shadow);
				}
				var item = this.frameList.items[this.frameList.items.length-1];
				item.realLeft = parseInt(frameArray[i+1]);
				item.realTop = parseInt(frameArray[i+2]);
				item.realRight = parseInt(frameArray[i+3]);
				item.realBottom = parseInt(frameArray[i+4]);
				if ((emptyArray != null) && (emptyArray.length >= this.frameList.items.length))
					item.bEmpty = (emptyArray[this.frameList.items.length-1] == "1");
				i = i + 9;
			}

			// Save coordinate info in frame list object for later retrieval
			this.frameList.width  = width;
			this.frameList.height = height;
			this.frameList.scale  = xScale;
			this.frameList.left   = xOffset;
			this.frameList.top    = yOffset;
			this.frameList.right  = xOffset + targetWidth;
			this.frameList.bottom = yOffset + targetHeight;

			// Set margins in screen coordinates
			this.leftMargin   = Math.floor(this.pageLeftMargin   * this.frameList.scale);
			this.topMargin    = Math.floor(this.pageTopMargin    * this.frameList.scale);
			this.rightMargin  = Math.floor(this.pageRightMargin  * this.frameList.scale);
			this.bottomMargin = Math.floor(this.pageBottomMargin * this.frameList.scale);

			// Force selection of the first non-static frame
			for (var i = 0; i < this.frameList.items.length; i++)
			{
				if (!this.frameList.items[i].staticItem)
				{
					this.activeFrame = i;
					break;
				}
			}
		},

	SetInitialPages : function(strInitPages)
	{
		// Set up initial pages for client-based
		if (this.clientType != kClientNone && null != this.theClientObj )
		{
			// Initialize client with the list
			this.theClientObj.SetInitialPages(strInitPages);

			// Scroll to the current page
			if (this.thePrintForm != null)
			{
				var delta = 0;

				// Look for current page in page list and get index
				var strPages = this.thePrintForm.elements["Pgs"].value;
				var vPages = strPages.split(",");
				var strCurrPage = this.thePrintForm.elements["Curr"].value;
				for (var i = 0; i < vPages.length; i++)
				{
					if (strCurrPage == vPages[i])
					{
						delta = i;
						break;
					}
				}

				// If not at first page, then scroll to it.
				if (delta > 0)
					clientObj.ScrollPage(delta);
			}
		}
	},

	UpdatePrintPageURLData  : function(command, args, width, height, fmt)
	{
		var strURL = "Cmd=UpdatePrintPage&ID=" + this.currPrintPageID;
		bFirst = false;

		// Append frame number
		strURL = strURL + "&Frame=" + this.activeFrame;

		// Append operation
		strURL = strURL + "&Operation=" + command;

		// Append arguments
		args = escape(args);
		strURL = strURL + "&Args=" + args;

		// Set width and height
		strURL = strURL + "&width=" + width + "&height=" + height;

		// Add ProductID
		if (this.iProductID != 0)
			strURL = strURL + "&ProductID=" + this.iProductID;

		// Add CropEdges
		if (this.cropEdges != "")
			strURL = strURL + "&CropEdges=" + this.cropEdges;

		// Add CropLines
		if (this.cropLines != "")
			strURL = strURL + "&CropLines=" + this.cropLines;

		// Append time in milliseconds so nothing get's cached
		var currTime = new Date();
		strURL = strURL + "&tm=" + currTime.getTime();

		// Append minimum resolution requirement if desired
		if (this.minResolution != 0)
			strURL = strURL + "&MinRes=" + this.minResolution;

		// Add proxy color and text if specified
		if (this.strProxyText != null)
			strURL += "&Fill=" + this.strProxyColor + "&Prompt=" + encodeURIComponent(this.strProxyText) + "&MinProxySize=" + this.minProxySize;

		// Add RefID (album ID) if available
		if (this.strRefID != null)
			strURL += "&RefID=" + this.strRefID;

		// Add quality and padding
		strURL += this.strQuality;
		strURL += this.strPad;

		// Add cookie information if available
		if (this.cookieDomain != null)
			strURL += "&CD=" + this.cookieDomain;
		if (this.cookiePath != null)
			strURL += "&CP=" + this.cookiePath;

		// add fmt 
		strURL = strURL + "&fmt=" + fmt;
		return strURL;
	},

	SendUpdate : function(command, args, retry)
	{
		// Use AJAX to update page.  We'll allow a single retry before displaying an error.  
		var oThis = this;
		var oRetry = retry;
		var oCommand = command;
		var oArgs = args;
		var handleSuccess = function(o)
		{
			if(o.responseText !== undefined)
			{
				oThis.bInUpdate = false;
				oThis.UpdatePrintPageImage2("current", "");
//				oThis.UpdateFrameInfo();
			}
		};

		var handleFailure = function(o){
			if (oRetry < 1)
			{
				oThis.SendUpdate(oCommand, oArgs, ++oRetry);
				return;
			}
			alert(strJSCommError);
			oThis.bInUpdate = false;
		};

		var callback =
		{
		  success:handleSuccess,
		  failure:handleFailure
		};

		var postData = this.UpdatePrintPageURLData(command, args, this.printImageWidth, this.printImageHeight, "XML");
		var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
	},
	
	UpdatePrintPageImage : function(command, args)
	{
		if (this.thePrintImageObj == null)
			return;

		// Are we in an update?
		if (this.bInUpdate)
			return;

		if (this.workingDiv != null)
			this.workingDiv.style.visibility = "visible";
			
		// If this is a "current" update, then go directly to the image update
		if (command == "current")
			this.UpdatePrintPageImage2(command, args);
		else
		{
			this.bInUpdate = true;
			this.SendUpdate(command, args, 0);
		}
	},

	UpdatePrintPageImage2 : function(command, args)
	{
		if (this.thePrintImageObj == null)
			return;

		// Are we in an update?
		if (this.bInUpdate)
			return;
		this.bInUpdate = true;

		// Build URL for editing operation
		if (this.strImageServerPage != null)
			strURL = this.strImageServerPage;
		else
			strURL = this.strWebServerPage;
		if (strURL.indexOf("://") == -1)
			strURL = documentBase + strURL;

		// Set returned format
		var fmt = "JPEG";
		if (this.bShowGray)
			fmt = "KPEG";
		var strData = this.UpdatePrintPageURLData(command, args, this.printImageWidth, this.printImageHeight, fmt);

		// Construct URL for Get
		strURL = strURL + "?" + strData;
		
		// Replace source for printImageObj IMG to reflect new operation...
		this.printUpdateURL = strURL;
		this.retryCount = 0;
		if (this.workingDiv == null)
			this.thePrintImageObj.src = this.workingBitmap;
		else
		{
			this.workingDiv.style.visibility = "visible";
			this.ImageLoaded();
		}
	},

	UpdateFrameInfo : function(retry)
	{
		// Use Ajax to update frame info 
		var oThis = this;
		var oRetry = retry;

		var handleSuccess = function(o)
		{
			if(o.responseText !== undefined)
			{
				var i;

				var strFrameInfo = o.responseText.substring( o.responseText.indexOf("<FrameInfo>")+11, o.responseText.indexOf("</FrameInfo>") );
				var strFrameInfoEx = o.responseText.substring( o.responseText.indexOf("<FrameInfoEx>")+13, o.responseText.indexOf("</FrameInfoEx>") );
				var strEmptyList = null;
				if (o.responseText.indexOf("<EmptyList>") != -1)
					strEmptyList = o.responseText.substring( o.responseText.indexOf("<EmptyList>")+11, o.responseText.indexOf("</EmptyList>") );

				// Rebuild FrameInfo and FrameInfoEx
				var aFrameInfo = strFrameInfo.split("\\f");
				strFrameInfo = aFrameInfo[0];
				for (i = 1; i < aFrameInfo.length; i++)
					strFrameInfo += "\f" + aFrameInfo[i];

				var aFrameInfoEx = strFrameInfoEx.split("\\f");
				strFrameInfoEx = aFrameInfoEx[0];
				for (i = 1; i < aFrameInfoEx.length; i++)
					strFrameInfoEx += "\f" + aFrameInfoEx[i];

				oThis.SetFrameInfoEx(strFrameInfo, strFrameInfoEx, strEmptyList);

				// Get the background
				var idx = o.responseText.indexOf("<Background>");
				if (idx != -1)
				{
					var strBackground = o.responseText.substring( idx+12, o.responseText.indexOf("</Background>") );
					oThis.pageBackground = strBackground;
				}

				// Maintain active frame, if requested.
				if (oThis.resetFrame != -1)
					oThis.activeFrame = oThis.resetFrame;
				oThis.resetFrame = -1;

				// If this is the active page, then update the frame
				if (oThis == frameHandlerContainer.activeFrameHandler.printClient)
					FrameChanged();

				// If there's an update completion callback, invoike it
				if (oThis.updateCompleteCallback != null)
					oThis.updateCompleteCallback();
				oThis.bInUpdate = false;
				
				// If there's a layout changed callback and that's what we did, then invoke it.
				if (oThis.bChangedLayout && (oThis.layoutChangedCallback != null))
					oThis.layoutChangedCallback();
				oThis.bChangedLayout = false;
			}
		};

		var handleFailure = function(o){
			if (oRetry < 1)
			{
				oThis.UpdateFrameInfo(++oRetry);
				return;
			}
			alert(strJSCommError);
			oThis.bInUpdate = false;
		};

		var callback =
		{
		  success:handleSuccess,
		  failure:handleFailure
		};
		var postData = "Cmd=GetFrameInfo&ID=" + this.currPrintPageID;
		var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
	},

	ImageLoaded : function()
	{
		if (this.printUpdateURL != "")
		{
			var currImageURL = this.printUpdateURL;
			this.printUpdateURL = "";
			this.thePrintImageObj.src = currImageURL;
		}
		else
		{
			// If using a working div, then hide it.
			if (this.workingDiv != null)
				this.workingDiv.style.visibility = "hidden";

			// Use Ajax to update frame info 
			this.UpdateFrameInfo(0);
			/*
			var oThis = this;

			var handleSuccess = function(o)
			{
				if(o.responseText !== undefined)
				{
					var i;

					var strFrameInfo = o.responseText.substring( o.responseText.indexOf("<FrameInfo>")+11, o.responseText.indexOf("</FrameInfo>") );
					var strFrameInfoEx = o.responseText.substring( o.responseText.indexOf("<FrameInfoEx>")+13, o.responseText.indexOf("</FrameInfoEx>") );

					// Rebuild FrameInfo and FrameInfoEx
					var aFrameInfo = strFrameInfo.split("\\f");
					strFrameInfo = aFrameInfo[0];
					for (i = 1; i < aFrameInfo.length; i++)
						strFrameInfo += "\f" + aFrameInfo[i];

					var aFrameInfoEx = strFrameInfoEx.split("\\f");
					strFrameInfoEx = aFrameInfoEx[0];
					for (i = 1; i < aFrameInfoEx.length; i++)
						strFrameInfoEx += "\f" + aFrameInfoEx[i];

					oThis.SetFrameInfoEx(strFrameInfo, strFrameInfoEx);

					// Get the background
					var idx = o.responseText.indexOf("<Background>");
					if (idx != -1)
					{
						var strBackground = o.responseText.substring( idx+12, o.responseText.indexOf("</Background>") );
						oThis.pageBackground = strBackground;
					}

					// Maintain active frame, if requested.
					if (oThis.resetFrame != -1)
						oThis.activeFrame = oThis.resetFrame;
					oThis.resetFrame = -1;

					// If this is the active page, then update the frame
					if (oThis == frameHandlerContainer.activeFrameHandler.printClient)
						FrameChanged();

					// If there's an update completion callback, invoike it
					if (oThis.updateCompleteCallback != null)
						oThis.updateCompleteCallback();
					oThis.bInUpdate = false;
				}
			};

			var handleFailure = function(o){
				if(o.responseText !== undefined){
					alert("Request failed, status code " + o.status);
				}
			};

			var callback =
			{
			  success:handleSuccess,
			  failure:handleFailure
			};
			var postData = "Cmd=GetFrameInfo&ID=" + this.currPrintPageID;
			var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
			*/
		}
	},

	ImageAbort : function()
	{
		if (this.printUpdateURL != "")
		{
			var currImageURL = this.printUpdateURL;
			this.printUpdateURL = "";
			this.thePrintImageObj.src = currImageURL;
		}
	},

	ImageError : function()
	{
		if (this.printUpdateURL != "")
		{
			var currImageURL = this.printUpdateURL;
			this.printUpdateURL = "";
			this.thePrintImageObj.src = currImageURL;
		}
		else
		{
			// We've tried to load the actual image and failed.  We'll allow a single retry before failing.
			if (this.retryCount < 1)
			{
				this.thePrintImageObj.src = this.retryURL;
				this.retryCount = 1;
			}
			else
			{
				alert(strJSCommError);
				if (this.workingDiv != null)
					this.workingDiv.style.visibility = "hidden";
				this.bInUpdate = false;
			}
		}
	},

	HavePage : function()
	{
		if (this.clientType != kClientNone)
		{
			if (this.theClientObj.NumPrints() == 0)
				return false;
		}
		else
		{
			if (this.currPrintPageID == 0)
				return false;
		}
		return true;
	},

	SetCheckpoint : function()
	{
		// Use Ajax to add or delete page 
		var oThis = this;

		var handleSuccess = function(o)
		{
			if(o.responseText !== undefined)
			{
			}
		};

		var handleFailure = function(o){
			alert(strJSCommError);
		};

		var callback =
		{
		  success:handleSuccess,
		  failure:handleFailure
		};
		var postData = "Cmd=ModifyAlbumPage&ID=" + this.strRefID + "&Checkpoint=" + this.currPrintPageID;
		var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
	},

	UndoCheckpoint : function()
	{
		this.resetFrame = this.activeFrame;
		this.UpdatePrintPageImage("undoCheckpoint", "");
	},

	AddDeletePage : function(strAdd, strDelete)
	{
		// Use Ajax to add or delete page 
		var oThis = this;

		var handleSuccess = function(o)
		{
			if(o.responseText !== undefined)
			{
				// Update page information, if necessary
				if (o.responseText.indexOf("<PageInfo>") != -1)
				{
					var strPageInfo = o.responseText.substring(o.responseText.indexOf("<PageInfo>")+10, o.responseText.indexOf("</PageInfo>") );
					var aPages = strPageInfo.split(",");
					if (aPages.length != oThis.pageList.length)
					{
						if (oThis.pagesChangedCallback)
							oThis.pagesChangedCallback(aPages);
						else
							PagesChanged(aPages);
					}
				}
			}
		};

		var handleFailure = function(o){
			alert(strJSCommError);
		};

		var callback =
		{
		  success:handleSuccess,
		  failure:handleFailure
		};
		var postData = "Cmd=ModifyAlbumPage&ID=" + this.strRefID;
		if (strAdd != null)
			postData += "&Add=" + strAdd;
		if (strDelete != null)
			postData += "&Delete=" + strDelete;
		this.bChangedLayout = true;
		var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
	},

	ReorderPages : function(aNewPageList)
	{
		// Use Ajax to add or delete page 
		var oThis = this;

		var handleSuccess = function(o)
		{
			if(o.responseText !== undefined)
			{
				// Update page information, if necessary
				if (o.responseText.indexOf("<PageInfo>") != -1)
				{
					var strPageInfo = o.responseText.substring(o.responseText.indexOf("<PageInfo>")+10, o.responseText.indexOf("</PageInfo>") );
					var aPages = strPageInfo.split(",");
					if (oThis.pagesChangedCallback)
						oThis.pagesChangedCallback(aPages);
					else
						PagesChanged(aPages);
				}
			}
		};

		var handleFailure = function(o){
			alert(strJSCommError);
		};

		var callback =
		{
		  success:handleSuccess,
		  failure:handleFailure
		};
		this.bChangedLayout = true;
		var postData = "Cmd=ModifyAlbumPage&ID=" + this.strRefID + "&Reorder=" + aNewPageList.join(",");
		var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
	},

	NewPage : function(iLayoutID, bBefore)
	{
		if (this.clientType != kClientNone)
		{
			this.theClientObj.NewPage(parseInt(iLayoutID));
		}
		else
		{
			// Submit form to add a new page.  Use the current page as the basis.
			var options;
			if (this.currPrintPageID != "")
				options = this.currPrintPageID + "," + iLayoutID;
			else
				options = 0 + "," + iLayoutID;
			if (bBefore)
				options += ",1";
			this.AddDeletePage(options, null);
		}
	},

	DeletePage : function()
	{
		if (!this.HavePage())
			return false;
		if (this.clientType != kClientNone)
			this.theClientObj.DeletePage();
		else
			this.AddDeletePage(null, this.currPrintPageID);
		return true;
	},

	ChangeLinkedPageLayout : function(strLinkedPageId, strLayoutID)
	{
		// Use Ajax to add or delete page 
		var oThis = this;

		var handleSuccess = function(o)
		{
			if(o.responseText !== undefined)
			{
			}
		};

		var handleFailure = function(o){
			alert(strJSCommError);
		};

		var callback =
		{
		  success:handleSuccess,
		  failure:handleFailure
		};
		var postData = "Cmd=ModifyAlbumPage&ID=" + this.strRefID + "&LinkedPage=" + strLinkedPageId + "&LayoutID=" + strLayoutID;
		var request = YAHOO.util.Connect.asyncRequest('POST', strPixamiServer, callback, postData);
	},

	PrevPage : function()
	{
		if (this.clientType != kClientNone)
		{
			this.theClientObj.ScrollPage(-1);
		}
		else
		{
			if (this.prevPrintPageID != "")
			{
				if ((this.useCookies != 0) || true)
				{
					this.processPageInfo = 1;
					this.UpdatePrintPageImage("prev", "");
				}
				else
					this.DoPrintCommand("prev", "");
			}
		}
	},

	NextPage : function()
	{
		if (this.clientType != kClientNone)
		{
			this.theClientObj.ScrollPage(1);
		}
		else
		{
			if (this.nextPrintPageID != "")
			{
				if ((this.useCookies != 0) || true)
				{
					this.processPageInfo = 1;
					this.UpdatePrintPageImage("next", "");
				}
				else
					this.DoPrintCommand("next", "");
			}
		}
	},

	GotoPage : function(pageNum)
	{
		// Determine how much to scroll
		var info = this.GetPageInfo();
		var aInfo = info.split(",");
		var iScroll = pageNum - (parseInt(aInfo[0]) - 1);

		this.pageIndex = pageNum;
		if (this.clientType != kClientNone)
				this.theClientObj.ScrollPage(iScroll);
		else
		{
			// Determine page desired
			this.currPrintPageID = this.pageList[pageNum];
			this.UpdatePrintPageImage("current", "");
		}
	},

	AddImageToFrame : function(selection)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.AddImageRef(selection);
		else 
		{
			if (this.frameList.items.length == 0)
				return;

			// First make sure that we've got an image frame selected
			if ((this.activeFrame == -1) || ((this.frameList.items[this.activeFrame].type & 3) != 1))
			{
				for (var i = 0; i < this.frameList.items.length; i++)
				{
					if (((this.frameList.items[i].type & 3) == 1) && (!this.frameList.items[i].staticItem))
					{
						this.activeFrame = i;
						FrameChanged();
						break;
					}
				}
			}
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("addImage", selection);
		}
	},

	SetImage : function(imageRef, imageindex)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetImage(imageRef, imageindex);
		else
		{
			var options = imageRef + "," + imageindex;
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("setImage", options);
		}
	},

	SetCoverLayout : function(iLayoutId)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetLayout(parseInt(iLayoutId));
		else
		{
			var strParams = iLayoutId + "," + "0";
			this.bChangedLayout = true;
			this.UpdatePrintPageImage("changeLayout", strParams);
		}
	},

	SetLayout : function(iLayoutId)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetLayout(parseInt(iLayoutId));
		else
		{
			this.bChangedLayout = true;
			this.UpdatePrintPageImage("changeLayout", iLayoutId);
		}
	},

	SetSpreadLayout : function(iLayoutId, iMode)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetLayout(parseInt(iLayoutId));
		else
		{
			var strParams = iLayoutId + "," + "0" + "," + iMode;
			this.bChangedLayout = true;
			this.UpdatePrintPageImage("changeLayout", strParams);
		}
	},

	SetPageNumber : function(iLayoutId, iMode)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetLayout(parseInt(iLayoutId));
		else
		{
			var strParams = iLayoutId + "," + "0" + "," + iMode;
			this.bChangedLayout = true;
			this.UpdatePrintPageImage("addPageNumber", strParams);
		}
	},

	ChangeBackground : function(strBkgndName)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetBackground(strBkgndName);
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("changeBackground", strBkgndName);
		}
	},

	GetCurrPageDim : function()
	{
		var pageDim = "-";
		if (this.clientType != kClientNone && null != this.theClientObj )
		{
			// Get the dimensions of the current page from the client
			pageDim = new String(this.theClientObj.GetCurrPageDim());
		}
		else
			pageDim = this.currPageDim;
		return pageDim;
	},

	RotateImage90CW : function()
	{
		if (this.clientType != kClientNone)
			this.theClientObj.RotateImage90CW();
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("rotate90cw", "");
		}
	},

	RotateImage90CCW : function()
	{
		if (this.clientType != kClientNone)
			this.theClientObj.RotateImage90CCW();
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("rotate90ccw", "");
		}
	},

	RotateImage180 : function()
	{
		if (this.clientType == kClientNone)
			this.UpdatePrintPageImage("rotate180", "");
		else
		{
			this.resetFrame = this.activeFrame;
			this.theClientObj.RotateImage180();
		}
	},

	MirrorImage : function()
	{
		if (this.clientType != kClientNone)
			this.theClientObj.MirrorImage();
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("mirror", "");
		}
	},

	FlipImage : function()
	{
		if (this.clientType != kClientNone)
			this.theClientObj.FlipImage();
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("flip", "");
		}
	},

	LockFrame : function()
	{
		if (this.clientType != kClientNone)
			return;
		if (this.frameList == null)
			return;
		if (this.frameList.items.length == 0)
			return;

		if (this.GetActiveFrameLock() == true)
			return;

		var background = this.pageBackground;
		background = background.toLowerCase();
		if (background.indexOf(".png") == background.length-4)
		{
			if (confirm(strJSLockFramePNGPrompt) == 0)
			{
				FrameChanged();
				return; 
			}
		}

		this.frameList.items[this.activeFrame].rotatable = 0;
		this.frameList.items[this.activeFrame].resizable = 0;
		this.frameList.items[this.activeFrame].type &= ~8;
		this.resetFrame = this.activeFrame;
		this.UpdatePrintPageImage("lock", "");
		FrameChanged();
	},

	UnlockFrame : function()
	{
		if (this.clientType != kClientNone)
			return;
		if (this.frameList == null)
			return;
		if (this.frameList.items.length == 0)
			return;

		if (this.GetActiveFrameLock() == false)
			return;

		// Check background image
		var background = this.pageBackground;
		background = background.toLowerCase();
		if (background.indexOf(".png") == background.length-4)
		{
			alert(strJSCannotUnlockFrame);
			FrameChanged();
			return;
		}

		// Check whether this is the first image frame on first page for DieCut Book
		var srcIndex = this.activeFrame;
		var imgIndex = this.GetFrameIndexByType(1, 0);
		if ((this.GetCoverType() == "DieCut") && (this.GetPageIndex() == 0) && (srcIndex == imgIndex))
			return;

		this.frameList.items[this.activeFrame].rotatable = 1;
		this.frameList.items[this.activeFrame].resizable = 1;
		this.frameList.items[this.activeFrame].type |= 8;
		this.resetFrame = this.activeFrame;
		this.UpdatePrintPageImage("unlock", "");
		FrameChanged();
	},

	GetActiveFrameLock : function ()
	{
		if (this.clientType != kClientNone)
			return false;

		if (this.frameList == null)
			return false;
		if (this.frameList.items.length == 0)
			return false;

		if (this.frameList.items[this.activeFrame].rotatable == 1)
			return false;
		if (this.frameList.items[this.activeFrame].resizable == 1) 
			return false;
		if ((this.frameList.items[this.activeFrame].type & 8) == 8)
			return false;
		return true;
	},

	MagnifyImage : function()
	{
		if (this.clientType != kClientNone)
			this.theClientObj.MagnifyImage();
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("magnify", "");
		}
	},

	ReduceImage : function()
	{
		if (clientType != kClientNone)
			this.theClientObj.ReduceImage();
		else
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("reduce", "");
		}
	},

	ImageMove : function(dx, dy)
	{
		// Do nudging...
		if ((dx != 0) || (dy != 0))
		{
			if (this.clientType != kClientNone)
				this.theClientObj.MoveImage(dx, dy);
			else
			{
				this.resetFrame = this.activeFrame;
				this.UpdatePrintPageImage("move", dx + "," + dy);
			}
		}
	},

	ImageNudge : function(dx, dy)
	{
		// Scale Amount of delta is different for client and server editing
		var delta = 25;
	//	if (bClientType & 0x01)
	//		delta = 5;
		dx *= delta;
		dy *= delta;

		// Do nudging...
		if ((dx != 0) || (dy != 0))
		{
			if (this.clientType != kClientNone)
				this.theClientObj.MoveImage(dx, dy);
			else
			{
				this.resetFrame = this.activeFrame;
				this.UpdatePrintPageImage("move", dx + "," + dy);
			}
		}
	},

	FrameHitTest : function(x, y)
	{
		if (this.frameList == null)
			return -1;
		var frame = this.frameList.HitTest(x, y);
		if (frame != -1)
			this.activeFrame = frame;
		return frame;
	},

	GetActiveFrameType : function()
	{
		// Get frame type
		var currFrameType;
		if (this.clientType != kClientNone)
			currFrameType = this.theClientObj.GetActiveFrameType();
		else
		{
			if (this.frameList == null)
				return 0;
			if (this.frameList.items.length == 0)
				return 0;

			if ((this.activeFrame != -1) && (this.frameList != null))
			{
				// Make sure the active frame is a valid non-static frame
				if ((this.activeFrame >= this.frameList.items.length) || 
					(this.frameList.items[this.activeFrame].staticItem))
				{
					for (var i = 0; i < this.frameList.items.length; i++)
					{
						if (!this.frameList.items[i].staticItem)
						{
							this.activeFrame = i;
							break;
						}
					}
				}
				if (!this.frameList.items[this.activeFrame].loaded)
				{
					this.processPageInfo = this.useCookies;
					this.FrameQuery();
				}
				currFrameType = this.frameList.GetFrameType(this.activeFrame);
			}
			else
				currFrameType = 0;
		}
		return currFrameType;
	},

	SetImageEdge : function(strEdge)
	{
		var edgeInfoProps = this.GetImageEdgeInfo(this.activeFrame);
		this.SetImageEdgeFeather(strEdge, edgeInfoProps[1]);
	},

	SetImageFeather : function(featherVal)
	{
		var edgeInfoProps = this.GetImageEdgeInfo(this.activeFrame);
		this.SetImageEdgeFeather(edgeInfoProps[0], featherVal);
	},

	SetImageJnlInfo : function(aImageJnl)
	{
		var strImageJnl = this.GetImageJournal();
		var kj = new PxCompactJnl(strImageJnl);

		if (aImageJnl.length < 4)
			alert(strJSInvalidImageJnl);

		if (aImageJnl[0] != 0)		// gray
			kj.bGray = true;
		else
			kj.bGray = false;
		if (aImageJnl[1] != 0)		// sepia
			kj.bSepia = true;
		else
			kj.bSepia = false;
		if (aImageJnl[2] != 0)		// autoFix
		{
			kj.bAutoFix = true;
			kj.autoFix1 = 50;
			kj.autoFix2 = 50;
		}
		else
		{
			kj.bAutoFix = false;
			kj.autoFix1 = 0;
			kj.autoFix2 = 0;
		}
		if (aImageJnl[3] != 0)		// sharpen
		{
			kj.bSharpen = true;
			kj.sharpen = 30;
		}
		else
		{
			kj.bSharpen = false;
			kj.sharpen = 0;
		}
		if ((aImageJnl.length == 5) && (aImageJnl[4] != 0))		// transparency
		{
			kj.bOpacity = true;
			kj.opacity = aImageJnl[4];
		}
		else
		{
			kj.bOpacity = false;
			kj.opacity = 100;
		}
		strImageJnl = kj.GetCompactJnl();
		this.SetImageJournal(strImageJnl);
	},

	SetImageEdgeFeather : function(strEdge, featherVal)
	{
		// Pass to client
		if (this.clientType != kClientNone)
		{
			this.theClientObj.SetFrameEdgeFeather(strEdge, parseInt(featherVal));
		}
		else
		{
			if (this.frameList.items.length == 0)
				return;

			// Update information in edge definition
			this.frameList.items[this.activeFrame].edgeRef = strEdge;
			this.frameList.items[this.activeFrame].feather = featherVal;

			// Force refresh of image
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("setImageAttrib", 
									this.frameList.items[this.activeFrame].edgeRef + "," + 
								  this.frameList.items[this.activeFrame].feather);
		}
	},

	SetFrameAttributes : function(strType, strAttributes)
	{
		// Pass to client
		if (this.clientType != kClientNone)
		{
			if (strType == "BORDERS")
			{
				var strAttrib = "";
				var aBorders = strAttributes.split(",");
				var nBorders = aBorders.length / 5;
				for (var i = 0; i < nBorders; i++)
				{
					strAttrib += 
						'<LineBorder Width="' + aBorders[i*5] + '" Opacity="' + aBorders[i*5+1] + '">' + 
						' <Color Red="' + aBorders[i*5+2] + '"' + 
						' Green="' + aBorders[i*5+3] + '"' + 
						' Blue="' + aBorders[i*5+4] + '" />' + 
						'</LineBorder>';						
				}
				this.theClientObj.SetFrameAttributes(strType, strAttrib);
			}
			else if (strType == "SHADOW")
			{
				var aParams = strAttributes.split(",");
				var strAttrib =  '<Shadow Width="' + aParams[0] + '" Opacity="' + aParams[1] + '" ></Shadow>';						
				this.theClientObj.SetFrameAttributes(strType, strAttrib);
			}
			else
				this.theClientObj.SetFrameAttributes(strType, strAttributes);
		}
		else
		{
			if (this.frameList.items.length == 0)
				return;

			// Update information in edge definition
			if (strType == "BORDERS")
				this.frameList.items[this.activeFrame].borders = strAttributes;
			else if (strType == "SHADOW")
				this.frameList.items[this.activeFrame].shadow = strAttributes;


			// Force refresh of image
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("setFrameAttrib", strType + "," + strAttributes);
		}
	},

	GetFrameAttributes : function(strType)
	{
		var strAttributes = "";

		// Pass to client
		if (this.clientType != kClientNone)
		{
			if (strType == "BORDERS")
			{
				var strXML = this.theClientObj.GetFrameAttributes(strType);
				strAttributes = PxParseLineBorders(strXML);
			}
			else if (strType == "SHADOW")
			{
				var strXML = this.theClientObj.GetFrameAttributes(strType);
				strAttributes = PxParseShadow(strXML);
			}
		}
		else
		{
			if (this.frameList.items.length == 0)
				return strAttributes;

			// Update information in edge definition
			if (strType == "BORDERS")
				strAttributes = this.frameList.items[this.activeFrame].borders;
			else if (strType == "SHADOW")
			{
				strAttributes = this.frameList.items[this.activeFrame].shadow;
			}
		}
		return strAttributes;
	},

	GetImageEdgeInfo : function(iFrame)
	{
		var edgeInfoProps = new Array();
		if (this.clientType == kClientNone)
		{
			if (this.frameList.items.length == 0)
			{
				edgeInfoProps[0] = "";
				edgeInfoProps[1] = "0";
				return edgeInfoProps;
			}
			// For server-based, get information from Frame definition list
			if (this.frameList.items[iFrame].style == 1)
				edgeInfoProps[0] = "RECT";
			else if (this.frameList.items[iFrame].style == 2)
				edgeInfoProps[0] = "ELLIP";
			else if (this.frameList.items[iFrame].style == 3)
				edgeInfoProps[0] = "ALPHA";
			else
				edgeInfoProps[0] = this.frameList.items[iFrame].edgeRef;
			edgeInfoProps[1] = new String(this.frameList.items[iFrame].feather);
		}
		else
		{
			// Get text information from client
			var edgeInfo = new String(this.theClientObj.GetImageFrameAttrib());

			// Split into array
			edgeInfoProps = edgeInfo.split(",");
		}
		return edgeInfoProps;
	},

	GetImageJnlInfo : function()
	{
		var strImageJnl = "";
		if (this.clientType != kClientNone)
			strImageJnl = this.theClientObj.GetImageJournal();
		else
		{
			if (this.frameList.items.length != 0)
				strImageJnl = this.frameList.items[this.activeFrame].imageJnl;
		}

		var jnlInfoProps = new Array(5);
		if (strImageJnl.indexOf("g") != -1)
			jnlInfoProps[0] = 1;
		else
			jnlInfoProps[0] = 0;
		if (strImageJnl.indexOf("s") != -1)
			jnlInfoProps[1] = 1;
		else
			jnlInfoProps[1] = 0;
		if (strImageJnl.indexOf("a") != -1)
			jnlInfoProps[2] = 1;
		else
			jnlInfoProps[2] = 0;
		if (strImageJnl.indexOf("h") != -1)
			jnlInfoProps[3] = 1;
		else
			jnlInfoProps[3] = 0;	
		if (strImageJnl.indexOf("o") != -1)
		{
			var idx = strImageJnl.indexOf("o");
			var idx2 = strImageJnl.indexOf(",");
			var opacity;
			if (idx2 == -1)
				opacity = parseInt(strImageJnl.substring(idx+1));
			else
				opacity = parseInt(strImageJnl.substring(idx+1, idx2));
			jnlInfoProps[4] = opacity;
		}
		else
			jnlInfoProps[4] = 0;	
		return jnlInfoProps;
	},

	AddTextToFrame : function(text, minPts, maxPts)
	{
		// Convert end of line characters as needed
		var newText = text.replace(/\r\n/g, "\n");		// Windows new-lines
		newText = newText.replace(/\r/g, "\n");			// Macintosh new-lines
		var strEscapedText = PxEscapeText(newText);
		if (this.clientType != kClientNone)
		{
			this.theClientObj.AddTextToFrame(strEscapedText);
			if ((minPts != 0) && (maxPts != 0))
				this.theClientObj.AutoSizeText(minPts, maxPts);
		}
		else
		{
			if (this.frameList.items.length == 0)
				return;

			// Update information in frame definition if the text has changed.
			if (this.frameList.items[this.activeFrame].text != newText)
			{
				this.frameList.items[this.activeFrame].text = newText;
				this.resetFrame = this.activeFrame;
				this.UpdatePrintPageImage("addText", strEscapedText);
			}
		}
	},

	AddTextToLastFrame : function(text)
	{
		// Convert end of line characters as needed
		var newText = text.replace(/\r\n/g, "\n");		// Windows new-lines
		newText = newText.replace(/\r/g, "\n");			// Macintosh new-lines
		var strEscapedText = PxEscapeText(newText);
		if (this.clientType != kClientNone)
			this.theClientObj.AddTextToLastFrame(strEscapedText);
	},

	GetTextFrameInfo : function()
	{
		var textFontProps = new Array(4);
		if (this.clientType == kClientNone)
		{
			// For server-based, get information from Frame definition list
			if (this.frameList.items.length == 0)
			{
				textFontProps[0] = "";
				textFontProps[1] = "";
				textFontProps[2] = "";
				textFontProps[3] = "";
			}
			else
			{
				textFontProps[0] = PxUnescapeText(this.frameList.items[this.activeFrame].text);
				textFontProps[1] = this.frameList.items[this.activeFrame].font;
				textFontProps[2] = new String(this.frameList.items[this.activeFrame].ptSize);
				textFontProps[3] = this.frameList.items[this.activeFrame].color;
			}
		}
		else
		{
			// Get current text string from client
			var strUnescapedText = PxUnescapeText(new String(this.theClientObj.GetActiveFrameText()));
			textFontProps[0] = strUnescapedText;

			// Get text information from client
			var textInfo = new String(this.theClientObj.GetTextFrameFont());

			// Split into array
			var textFontInfo = textInfo.split(",");
			if (textFontInfo.length == 3)
			{
				textFontProps[1] = textFontInfo[0];
				textFontProps[2] = textFontInfo[1];
				textFontProps[3] = textFontInfo[2];
			}
		}

		// Convert new-lines in string to appropriate ones for platform
		if (is_win)
			textFontProps[0] = textFontProps[0].replace(/\n/g, "\r\n");
		else if (is_mac)
			textFontProps[0] = textFontProps[0].replace(/\n/g, "\r");
		return textFontProps;
	},

	SetTextFont : function(font, size, color)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetTextFrameFont(font, size, color);
		else
		{
			if (this.frameList.items.length == 0)
				return;

			// Update information in frame definition
			this.frameList.items[this.activeFrame].font   = font;
			this.frameList.items[this.activeFrame].ptSize = size;
			this.frameList.items[this.activeFrame].color  = color;

			// Force refresh of image
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("setTextAttrib", font + "," + size + "," + color);
		}
	},

	EnableMinResolutionCheck : function(minRes)
	{
		if (this.clientType != kClientNone)
		{
			this.theClientObj.SetMinFrameRes(minRes);
			this.theClientObj.EnableResCheck(1);
		}
		else
		{
			this.minResolution = minRes;
		}
	},

	GetPageInfo : function()
	{
		var info = null;
		if (this.clientType != kClientNone)
			info = this.theClientObj.GetPageInfo();
		else if (this.pageList != null)
		{
			var pageNum = 1;
			for (var i = 0; i < this.pageList.length; i++)
			{
				if (this.pageList[i] == this.currPrintPageID)
				{
					pageNum = i + 1;
					break;
				}
			}
			info = pageNum + "," + this.pageList.length;
		}
		return info;
	},

	GetPageIndex : function()
	{
		var strPageInfo = new String(this.GetPageInfo());
		var pageInfo = (null != strPageInfo) ? strPageInfo.split(",") : null;
		var pageIndex = 0;
		if ((null != pageInfo) && (pageInfo.length == 2))
			pageIndex = parseInt(pageInfo[0]) - 1;
		return pageIndex;
	},

	GetPageCount : function()
	{
		return this.pageList.length;
	},

	GetCoverType : function()
	{
		return this.strCoverType;
	}, 

	GetPageXML : function()
	{
		var strXML = "";
		if (this.clientType != kClientNone)
		{
			var strURL = this.strWebServerPage;
			if (this.strImageServerPage != null)
				strURL = this.strImageServerPage;
			if (strURL.indexOf("://") == -1)
				strURL = documentBase + strURL;
			strXML = this.theClientObj.SubmitPrints2(strURL);
		}
		return strXML;
	},

	ClipLeft : function(left)
	{
		if ( this.bPageMargins && !this.frameList.items[this.activeFrame].ignoreMargins && (left < this.pageLeftMargin))
			return this.pageLeftMargin;
		return left;
	},

	ClipTop : function(top)
	{
		if ( this.bPageMargins && !this.frameList.items[this.activeFrame].ignoreMargins && (top < this.pageTopMargin))
			return this.pageTopMargin;
		return top;
	},

	ClipRight : function(right)
	{
		var aDim = this.currPageDim.split(",");
		var width =  aDim[0] * 2;
		if ( this.bPageMargins && !this.frameList.items[this.activeFrame].ignoreMargins && (right + this.pageRightMargin > width))
			return width-this.pageRightMargin;
		return right;
	},

	ClipBottom : function(bottom)
	{
		var aDim = this.currPageDim.split(",");
		var height = aDim[1] * 2;
		if ( this.bPageMargins && !this.frameList.items[this.activeFrame].ignoreMargins && (bottom + this.pageBottomMargin > height))
			return height-this.pageBottomMargin;
		return bottom;
	},

	MoveFrame : function(left, top, right, bottom)
	{
		if (clientType == kClientNone)
		{
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("moveFrame", left+","+top+","+right+","+bottom);
		}
	},

	RotateFrame : function(rotation)
	{
		if (rotation == 0)
			return;
		if (clientType == kClientNone)
		{
			//if (this.frameList.items[this.activeFrame].rotatable == 0)
			//	return;

			// Double check rotation 
			var value = (this.frameList.items[this.activeFrame].rotate + rotation) % 3600;
			if (value == this.frameList.items[this.activeFrame].rotate)
				return;

			// Double check the boundary
			var left = this.frameList.items[this.activeFrame].left;
			var top = this.frameList.items[this.activeFrame].top;
			var right = this.frameList.items[this.activeFrame].right;
			var bottom = this.frameList.items[this.activeFrame].bottom;

			var newframe = new PxFrame2(left, top, right, bottom);
			newframe.Rotate(value);

			// out of bound check
			if (this.frameList.items[this.activeFrame].ignoreMargins)
			{
				if ((newframe.left < this.frameList.left) || 
					(newframe.right > this.frameList.right) || 
					(newframe.top < this.frameList.top) || 
					(newframe.bottom > this.frameList.bottom))
					return; 
			}
			else
			{
				if ((newframe.left < this.frameList.left+this.leftMargin) || 
					(newframe.right > this.frameList.right-this.rightMargin) ||
					(newframe.top < this.frameList.top+this.topMargin) || 
					(newframe.bottom > this.frameList.bottom-this.bottomMargin))
					return;
			}

			this.UpdatePrintPageImage("rotateFrame", rotation);

			// Update the rotation
			this.frameList.items[this.activeFrame].rotate = value;
			this.resetFrame = this.activeFrame;
			//FrameChanged();
		}
		else
			this.theClientObj.RotateFrame(rotation);
	},

	OrderFrame : function(offset)
	{
		if (clientType == kClientNone)
		{
			if (this.frameList.items.length <= 1)
				return;
			var frameIndex = this.activeFrame + offset;
			if (frameIndex < 0)
				frameIndex = 0;
			if (frameIndex >= this.frameList.items.length)
				frameIndex = this.frameList.items.length-1;
			var i;
			var background = this.pageBackground;
			background = background.toLowerCase();
			if (background.indexOf(".png") == background.length-4)
			{
				if (frameIndex < this.activeFrame)
				{
					for (i = this.activeFrame; i >= frameIndex; --i)
					{
						var bMovable = false;
						if ((this.frameList.items[i].type & 8) == 8)
							bMovable = true;
						else if ((this.frameList.items[i].type & 3) == 2)
							bMovable = false;
						if (!bMovable)
						{
							if (i == this.activeFrame)
								alert(strJSCannotMoveFrame);
							else
								alert(strJSCannotSendBackFrame);
							return;
						}
					}
				}
				else
				{
					for (i = this.activeFrame; i <= frameIndex; i++)
					{
						var bMovable = false;
						if ((this.frameList.items[i].type & 8) == 8)	
							bMovable = true;
						else if ((this.frameList.items[i].type & 3) == 2)
							bMovable = false;
						if (!bMovable)
						{
							if (i == this.activeFrame)
								alert(strJSCannotMoveFrame);
							else
								alert(strJSCannotBringFrontFrame);
							return;
						}
					}
				}
			}
			if (frameIndex == this.activeFrame)
			{
				if (frameIndex == 0)
					alert(strJSFrameAlreadyAtBottom);
				else if (frameIndex == this.frameList.items.length-1)
					alert(strJSFrameAlreadyAtTop);
				return;
			}
			this.UpdatePrintPageImage("orderFrame", frameIndex-this.activeFrame);

			if (this.activeFrame < frameIndex)
			{
				for (i = this.activeFrame; i < frameIndex; ++i)
				{
					var item = this.frameList.items[i+1];
					this.frameList.items[i+1] = this.frameList.items[i];
					this.frameList.items[i] = item;
				}
			}
			else
			{
				for (i = this.activeFrame; i > frameIndex; --i)
				{
					var item = this.frameList.items[i-1];
					this.frameList.items[i-1] = this.frameList.items[i];
					this.frameList.items[i] = item;
				}
			}

			this.resetFrame = frameIndex;
			this.activeFrame = this.resetFrame;
			FrameChanged();
		}
	},

	GetFrameIndexByType : function(type, reqIndex)
	{
		if (clientType != kClientNone)
			return -1;

		var typeIndex = -1;
		var i;
		for (i = 0; i < this.frameList.items.length; i++)
		{
			if ((this.frameList.items[i].type & 3) == type)
				typeIndex++;
			if (typeIndex == reqIndex)
				return i;
		}
		return -1;
	},

	AddPrintItem : function(strItemXML)
	{
			if (clientType != kClientNone)
			{
				this.theClientObj.AddPrintItem(strItemXML);
			}
			else
			{
				// Determine frame dimensions
				var left   = parseInt(PxGetValueFromXML(strItemXML, "Left"));
				var top    = parseInt(PxGetValueFromXML(strItemXML, "Top"));
				var right  = parseInt(PxGetValueFromXML(strItemXML, "Right"));
				var bottom = parseInt(PxGetValueFromXML(strItemXML, "Bottom"));
				var frameLeft   = Math.floor(left   * this.frameList.scale) + this.frameList.left;
				var frameTop    = Math.floor(top    * this.frameList.scale) + this.frameList.top;
				var frameRight  = Math.floor(right  * this.frameList.scale) + this.frameList.left;
				var frameBottom = Math.floor(bottom * this.frameList.scale) + this.frameList.top;

				// Determine frame attributes
				var movable = parseInt(PxGetValueFromXML(strItemXML, "Movable"));
				var resizable = parseInt(PxGetValueFromXML(strItemXML, "Resizable"));
				var fixedAspect = parseInt(PxGetValueFromXML(strItemXML, "FixedAspect"));
				var staticItem = parseInt(PxGetValueFromXML(strItemXML, "Static"));
				var rotatable = parseInt(PxGetValueFromXML(strItemXML, "Rotatable"));
				var rotate = parseInt(PxGetValueFromXML(strItemXML, "Rotate"));
				var showFrame = 2;
				var strShowFrame = PxGetValueFromXML(strItemXML, "ShowFrame");
				if(strShowFrame.length != 0)
					showFrame = parseInt(strShowFrame);
				var ignoreMargins = parseInt(PxGetValueFromXML(strItemXML, "IgnoreMargins"));

				// Determine type by looking into XML
				var strType = PxGetValueFromXML(strItemXML, "Type");
				if (strType == "Text")
				{
					// Get text parameters from XML
					var strText = PxGetValueFromXML(strItemXML, "String");
					var strFont = PxGetValueFromXML(strItemXML, "FontName");
					var iSize   = parseInt(PxGetValueFromXML(strItemXML, "Size"));

					// Determine text color
					var iRed  = parseInt(PxGetValueFromXML(strItemXML, "Red"));
					var iGreen  = parseInt(PxGetValueFromXML(strItemXML, "Green"));
					var iBlue  = parseInt(PxGetValueFromXML(strItemXML, "Blue"));
					var strRed = iRed.toString(16);
					var strGreen = iGreen.toString(16);
					var strBlue = iBlue.toString(16);
					if (strRed.length == 1)
						strRed = "0" + strRed;
					if (strGreen.length == 1)
						strGreen = "0" + strGreen;
					if (strBlue.length == 1)
						strBlue = "0" + strBlue;
					var strColor = "00" + strRed.toUpperCase() + strGreen.toUpperCase() + strBlue.toUpperCase();

					// Determine text attributes
					var alignment = PxGetValueFromXML(strItemXML, "Justification");
					var style = 0;
					if (parseInt(PxGetValueFromXML(strItemXML, "Bold")) == 1)
						style |= 0x01;
					if (parseInt(PxGetValueFromXML(strItemXML, "Italic")) == 1)
						style |= 0x02;
	//				if (parseInt(PxGetValueFromXML(strItemXML, "Underline")) == 1)
	//					style |= 0x04;
	//				if (parseInt(PxGetValueFromXML(strItemXML, "Strikeout")) == 1)
	//					style |= 0x08;

					// Add new text frame of floating type
					var type = 2;
					if (movable != 0)
						type |= 8;
					this.frameList.AddItemEx(type, frameLeft, frameTop, frameRight, frameBottom,
											style, 0, "", strText, strFont, iSize, strColor,
											staticItem, showFrame, style, alignment, "",
											fixedAspect, resizable, ignoreMargins, "",
											rotatable, rotate);
				}
				else
				{
					// Check the style of image 
					var style = 1;
					var strStyle = PxGetValueFromXML(strItemXML, "Style");
					if (strStyle == "Edges")
						style = 0;
					else if (strStyle == "Rectangle")
						style = 1;
					else if (strStyle == "Ellipse")
						style = 2;
					else if (strStyle == "Alpha")
						style = 3;
					// Add new image frame of floating type
					var type = 1;
					if (movable != 0)
						type |= 8;
					this.frameList.AddItemEx(type, frameLeft, frameTop, frameRight, frameBottom,
											style, 0, "", "", "", 0, "",
											staticItem, showFrame, style, 0, "", 
											fixedAspect, resizable, ignoreMargins, "",
											rotatable, rotate);
				}

				// Add the frame to the page
				this.resetFrame = this.frameList.items.length - 1;
				this.UpdatePrintPageImage("addPrintItem", PxEscapeText(strItemXML));
			}
	},

	DeleteFrame : function()
	{
		if (this.clientType == kClientNone)
		{
			if (this.frameList.items.length == 0)
				return;

			// Remove frame from frame list.  Since delete doesn't change the length
			// of the array, we need to build a new one.
			var newItems = new Array();
			for (var i = 0; i < this.frameList.items.length; i++)
			{
				// Add to new list if not the deleted one
				if (i != this.activeFrame)
					newItems[newItems.length] = this.frameList.items[i];
			}
			this.frameList.items = newItems;

			// Delete the frame
			this.bChangedLayout = true;
			this.UpdatePrintPageImage("deleteFrame", "");

			// Set active frame 
			for (var i = 0; i < this.frameList.items.length; i++)
			{
				if (((this.frameList.items[i].type & 3) == 1) && (!this.frameList.items[i].staticItem))
				{
					this.activeFrame = i;
					FrameChanged();
					break;
				}
			}
		}
		else
				this.theClientObj.DeleteActiveFrame();
	},

	SetProxyMode : function(proxyColor, proxyText, minProxySize)
	{
		if (this.clientType != kClientNone)
		{
			// Convert color to integer
			var intColor = PxConvertColorSpec(proxyColor);
			this.theClientObj.SetProxyMode(intColor, proxyText);
		}
		else
		{
			// Save for server-based operations
			this.strProxyText  = proxyText;
			this.strProxyColor = proxyColor;
			this.minProxySize = minProxySize;
		}
	},

	SetPageMargins : function(left, top, right, bottom)
	{
		if (this.clientType != kClientNone)
		{
			this.theClientObj.SetPageMargins(left, top, right, bottom);
		}
		else
		{
			// Save margins at page sizes
			this.pageLeftMargin   = left;
			this.pageTopMargin    = top;
			this.pageRightMargin  = right;
			this.pageBottomMargin = bottom;
			this.bPageMargins = true;

			// Set margins in screen coordinates
			if (this.frameList != null)
			{
				this.leftMargin   = Math.floor(this.pageLeftMargin   * this.frameList.scale);
				this.topMargin    = Math.floor(this.pageTopMargin    * this.frameList.scale);
				this.rightMargin  = Math.floor(this.pageRightMargin  * this.frameList.scale);
				this.bottomMargin = Math.floor(this.pageBottomMargin * this.frameList.scale);
			}
		}
	},

	SetClipArtInfo : function(strClipInfo)
	{
		// Allocate array for clip art information
		this.clipArtInfo = new Array();

		// Split up info string.  String is triplets of file name, width and height
		var aInfo = strClipInfo.split(",");
		var count = aInfo[0];

		// Construct PxClipArtInfo objects for each triplet
		for (i = 0; i < count; i++) 
		{
			var index = (i * 3) + 1;
			this.clipArtInfo[i] = 
				new PxClipArtInfo(aInfo[index], aInfo[index+1], aInfo[index+2]);
		}	
	},

	AddClipArt : function(strSelection, strPath, 
						  left, top, right, bottom)
	{
		// Find the width and height of the clipart.  Initialize to full bounding
		// rectangle.
		var clipWidth  = right - left;
		var clipHeight = bottom - top;
		if (this.clipArtInfo != null)
		{
			var count = this.clipArtInfo.length;
			for (var i = 0; i < count; i++)
			{
				// Is this the one we're looking for?
				if (strSelection == this.clipArtInfo[i].strFileName)
				{
					// Yes, update clip art width and height
					clipWidth  = this.clipArtInfo[i].width  / 200;
					clipHeight = this.clipArtInfo[i].height / 200;
					break;
				}
			} 
		}

		// Update bounding rectangle to be same aspect ratio as clip art
		var xScale = (right - left) / clipWidth;
		var yScale = (bottom - top) / clipHeight;
		if (yScale < xScale)
			xScale = yScale;
		right  = left + (clipWidth  * xScale);
		bottom = top  + (clipHeight * xScale);

		// Adjust to 200 dpi
		left = Math.floor(left * 200);
		top = Math.floor(top * 200);
		right = Math.floor(right * 200);
		bottom = Math.floor(bottom * 200);

		// Make sure it fits on the page
		var aDim = this.currPageDim.split(",");
		aDim[0] *= 2;
		aDim[1] *= 2;
		if ((right - left) > aDim[0])
		{
			var delta = (right - left) - aDim[0];
			left += delta;
			var xScale = (right - left) / clipWidth;
			var yScale = (bottom - top) / clipHeight;
			if (yScale < xScale)
				xScale = yScale;
			bottom = top + Math.floor(clipHeight * xScale);
		}
		if ((bottom - top) > aDim[1])
		{
			var delta = (bottom - top) - aDim[1];
			top += delta;
			var xScale = (right - left) / clipWidth;
			var yScale = (bottom - top) / clipHeight;
			if (yScale < xScale)
				xScale = yScale;
			right  = left + Math.floor(clipWidth  * xScale);
		}
		if (left < 0)
		{
			right -= left;
			left = 0;
		}
		if (top < 0)
		{
			bottom -= top;
			top = 0;
		}
		if (right > aDim[0])
		{
			var delta = right - aDim[0];
			left -= delta;
			right -= delta;
		}
		if (bottom > aDim[1])
		{
			var delta = bottom - aDim[1];
			top -= delta;
			bottom -= delta;
		}

		// Construct appropriate image reference

		// Build XML for new clip art item
		var strXML =
			'<PrintItem \n' +
			' Left="' + left + '"\n' +
			' Top="' + top + '"\n' +
			' Right="' + right + '"\n' +
			' Bottom="' + bottom + '"\n' +
			' Movable="1"\n' +				// Movable frame
			' Resizable="1"\n' +			// Resizable frame
			' Rotatable="1"\n' +				// Rotateable frame
			' FixedAspect="1"\n' +			// Maintain aspect ratio of clip art
			' IgnoreMargins="0"\n' +		// Do not ignore page margins
			' Type="Image"\n' +
			' Style="Alpha"\n' +
			' Feather="0"\n' +
			' EdgesRef=""\n' +
			' Session="0"\n' +
			' ImageRef="'  + strSelection.replace(/\&/g,"&amp;") + '">\n' +
			' <Image  \n' +
			'   Rotate="0"\n' +
			'   Fill="2"\n' +
			'   Scale="100"\n' +
			'   dx="0"\n' +
			'   dy="0"\n' +
			'/>\n' +
			'</PrintItem>';

		// Add to the page
		this.AddPrintItem(strXML);
	},

	AddFloatingImage : function(strSelection, strPath, left, top, right, bottom)
	{
		this.AddFloatimgImageEx(strSelection, strPath, left, top, right, bottom, "Rectangle", 0);
	},

	AddFloatingImageEx : function(strSelection, strPath, left, top, right, bottom, style, rotatable)
	{
		// Find the width and height of the clipart.  Initialize to full bounding
		// rectangle.
		var clipWidth  = right - left;
		var clipHeight = bottom - top;
		if (this.clipArtInfo != null)
		{
			var count = this.clipArtInfo.length;
			for (var i = 0; i < count; i++)
			{
				// Is this the one we're looking for?
				if (strSelection == this.clipArtInfo[i].strFileName)
				{
					// Yes, update clip art width and height
					clipWidth  = this.clipArtInfo[i].width  / 200;
					clipHeight = this.clipArtInfo[i].height / 200;
					break;
				}
			} 
		}

		// Update bounding rectangle to be same aspect ratio as clip art
		var xScale = (right - left) / clipWidth;
		var yScale = (bottom - top) / clipHeight;
		if (yScale < xScale)
			xScale = yScale;
		right  = left + (clipWidth  * xScale);
		bottom = top  + (clipHeight * xScale);

		// Construct appropriate image reference

		// Build XML for new clip art item
		var strXML =
			'<PrintItem \n' +
			' Left="' + Math.floor(left * 200) + '"\n' +
			' Top="' + Math.floor(top * 200) + '"\n' +
			' Right="' + Math.floor(right * 200) + '"\n' +
			' Bottom="' + Math.floor(bottom * 200) + '"\n' +
			' Movable="1"\n' +				// Movable frame
			' Resizable="1"\n' +			// Resizable frame
			' Rotatable="' + rotatable + '"\n' +			// Rotatable frame
			' Rotate="0"\n' +				// Rotation
			' FixedAspect="0"\n' +			// Maintain aspect ratio of clip art
			' Static="0"\n' +
			' ShowFrame="2"\n' +
			' IgnoreMargins="0"\n' +		// Do not ignore page margins
			' Type="Image"\n' +
			' Style="' + style + '"\n' +
			' Feather="0"\n' +
			' EdgesRef=""\n' +
			' Session="0"\n' +
			' ImageRef="'  + strPath + strSelection + '">\n' +
			' <Image  \n' +
			'   Rotate="0"\n' +
			'   Fill="1"\n' +
			'   Scale="100"\n' +
			'   dx="0"\n' +
			'   dy="0"\n' +
			'/>\n' +
			'</PrintItem>';

		// Add to the page
		this.AddPrintItem(strXML);
	},

	AddFloatingText : function(strSelection, strPath, strFontName, iFontSize, iJustification,
		red, green, blue, left, top, right, bottom)
	{
		this.AddFloatingTextEx(strSelection, strPath, strFontName, iFontSize, iJustification,
								red, green, blue, left, top, right, bottom, 0, 0);
	},

	AddFloatingTextEx : function(strSelection, strPath, strFontName, iFontSize, iJustification,
		red, green, blue, left, top, right, bottom, bold, italic)
	{
		this.AddFloatingTextExR(strSelection, strPath, strFontName, iFontSize, iJustification,
								red, green, blue, left, top, right, bottom, bold, italic, 0);
	},

	AddFloatingTextExR : function(strSelection, strPath, strFontName, iFontSize, iJustification,
		red, green, blue, left, top, right, bottom, bold, italic, rotatable)
	{
		// Dimensions for floating text item
		var width  = right - left;
		var height = bottom - top;

		// Build XML for new floating text item
		var strXML =
			'<PrintItem \n' +
			' Left="' + Math.floor(left * 200) + '"\n' +
			' Top="' + Math.floor(top * 200) + '"\n' +
			' Right="' + Math.floor(right * 200) + '"\n' +
			' Bottom="' + Math.floor(bottom * 200) + '"\n' +
			' Movable="1"\n' +			// Movable frame
			' Resizable="1"\n' +		// Resizable frame
			' Rotatable="' + rotatable + '"\n' +		// Rotatable frame
			' Rotate="0"\n' +			// Rotation
			' FixedAspect="0"\n' +		// Maintain aspect ratio of clip art
			' IgnoreMargins="0"\n' +	// Do not ignore page margins
			' Type="Text">\n' +
			' <Text \n' +
			'   String=""\n' +
			'   Justification="' + iJustification + '"> \n';

		var index = strSelection.indexOf("256.png");
		if ((strSelection != "normal256.png") && (strSelection != "text01_256.png") && (index != -1))
		{
			// Determine which bubble - strip off 256.png
			var strBalloon = strSelection.substr(0, index);

			// Text location will vary based on type of balloon
			var textLeft;
			var textTop;
			var textRight;
			var textBottom;
			if (strBalloon == "bubbleleft")
			{
				textLeft   = 58  / 256;
				textTop    = 49  / 256;
				textRight  = 223 / 256;
				textBottom = 173 / 256;
			}
			else if (strBalloon == "bubbleright")
			{
				textLeft   = 35  / 256;
				textTop    = 49  / 256;
				textRight  = 200 / 256;
				textBottom = 173 / 256;
			}
			else if (strBalloon == "pointerleft")
			{
				textLeft   = 43  / 256;
				textTop    = 55  / 256;
				textRight  = 219 / 256;
				textBottom = 178 / 256;
			}
			else if (strBalloon == "pointerright")
			{
				textLeft   = 35  / 256;
				textTop    = 55  / 256;
				textRight  = 211 / 256;
				textBottom = 178 / 256;
			}
			else
			{
				// Generic text frames.
				textLeft = 24 / 256;
				textTop = 24 / 256;
				textRight  = 232 / 256;
				textBottom = 232 / 256;
			}

			// Build URLs for each resolution
			var strLowRes = strPath + strBalloon + "256.png";
			var strHiRes  = strPath + strBalloon + "736.png";

			// Build multi-resolution URL if the path is an URL.  Otherwise, just use high res.
			var strURL = strHiRes;
			if (strPath.indexOf("://") != -1)
			{
				strURL = "imglst://" +
					PxEscapeText(strLowRes) + "(256-256)!" +
					PxEscapeText(strHiRes)  + "(736-736)";
			}

			// Add border style for balloon
			strXML +=
				'   <Border \n' +
				'     Type="ClipArt"\n' +
				'     Left="' + Math.floor(textLeft * 1000) + '"\n' +
				'     Top="' + Math.floor(textTop * 1000) + '"\n' +
				'     Right="' + Math.floor(textRight * 1000) + '"\n' +
				'     Bottom="' + Math.floor(textBottom * 1000) + '"\n' +
				'     ClipArtRef="' + strURL + '"\n' +
				'   />\n';
		}

		strXML +=
			'   <Font \n' +
			'     FontName="' + strFontName + '"\n' +
			'     Size="' + iFontSize + '"\n' +
			'     Bold="' + bold + '"\n' +
			'     Italic="' + italic + '"\n' +
			'     Underline="' + 0 + '"\n' +
			'     StrikeOut="' + 0 + '"\n' +
			'   />\n' +
			'   <Color \n' +
			'     Red="' + red + '"\n' +
			'     Green="' + green + '"\n' +
			'     Blue="' + blue + '"\n' +
			'   />\n' +
			' </Text>\n' +
			'</PrintItem>';

		// Add to the page
		this.AddPrintItem(strXML);
	},

	GetTextAttributes : function()
	{
		var textAttributes = new Array(5);
		if (this.clientType == kClientNone)
		{
			// For server-based, get information from Frame definition list
			if (this.frameList.items.length == 0)
			{
				textAttributes[0] = "";
				textAttributes[1] = "";
				textAttributes[2] = "";
				textAttributes[3] = "";
				textAttributes[4] = "";
			}
			else
			{
				textAttributes[0] = this.frameList.items[this.activeFrame].font;
				textAttributes[1] = new String(this.frameList.items[this.activeFrame].ptSize);
				textAttributes[2] = this.frameList.items[this.activeFrame].color;
				textAttributes[3] = new String(this.frameList.items[this.activeFrame].style);
				textAttributes[4] = new String(this.frameList.items[this.activeFrame].alignment);
			}
		}
		else
		{
			// Get current text string from client
			var strTextAttributesXML = new String(this.theClientObj.GetTextAttributes());

			// Parse the text attributes
			textAttributes[0] = PxGetValueFromXML(strTextAttributesXML, "Font");
			textAttributes[1] = PxGetValueFromXML(strTextAttributesXML, "Size");
			textAttributes[2] = PxGetValueFromXML(strTextAttributesXML, "Color");
			textAttributes[3] = PxGetValueFromXML(strTextAttributesXML, "Style");
			textAttributes[4] = PxGetValueFromXML(strTextAttributesXML, "Alignment");
		}
		return textAttributes;
	},

	SetTextAttributes : function(textAttributes)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetTextAttributes(textAttributes);
		else
		{
			if (this.frameList.items.length == 0)
				return;

			// Parse the text attributes
			var strFont = PxGetValueFromXML(textAttributes, "Font");
			var strSize = PxGetValueFromXML(textAttributes, "Size");
			var strColor = PxGetValueFromXML(textAttributes, "Color");
			var strStyle = PxGetValueFromXML(textAttributes, "Style");
			var strAlignment = PxGetValueFromXML(textAttributes, "Alignment");

			// Update information in frame definition
			if (strFont != "")
				this.frameList.items[this.activeFrame].font = strFont;
			if (strSize != "")
				this.frameList.items[this.activeFrame].ptSize = parseInt(strSize);
			if (strColor != "")
				this.frameList.items[this.activeFrame].color = strColor;
			if (strStyle != "")
				this.frameList.items[this.activeFrame].style = parseInt(strStyle);
			if (strAlignment != "")
				this.frameList.items[this.activeFrame].alignment = parseInt(strAlignment);

			// Force refresh of image
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("setTextAttribEx", textAttributes);
		}
	},

	SubmitPrintsToServer : function(strUser, strPartner, strInitPgs, strRefID)
	{
		// Only for client-based
		if (this.clientType != kClientNone)
			this.theClientObj.SubmitPrintsToServer(strUser, strPartner, strInitPgs, strRefID);
	},

	SubmitPrintsToServerEx : function(strUser, strPartner, strInitPgs, strRefID, strParams)
	{
		// Only for client-based
		if (this.clientType != kClientNone)
			this.theClientObj.SubmitPrintsToServerEx(strUser, strPartner, strInitPgs, strRefID, strParams);
	},

	SetAutoSaveMode : function(strUser, strPartner, strInitPgs, strRefID, timerInterval, saveOnExit)
	{
		// Only for client-based
		if (this.clientType != kClientNone)
			this.theClientObj.SetAutoSaveMode(strUser, strPartner, strInitPgs, strRefID, timerInterval, saveOnExit);
	},

	SetAutoSaveModeEx : function(
		strUser, strPartner, strInitPgs, strRefID, timerInterval, saveOnExit, strParams)
	{
		// Only for client-based
		if (this.clientType != kClientNone)
			this.theClientObj.SetAutoSaveModeEx(strUser, strPartner, strInitPgs, strRefID, timerInterval, saveOnExit, strParams);
	},

	FrameLoaded : function()
	{
		SetFrameBaseCookies();
	},

	FrameAbort : function()
	{
	},

	FrameError : function()
	{
	},

	SetFrameInfoObj : function(frameObj)
	{
		// Save IMG object for server-based editing.
		this.theFrameQueryObj = frameObj;

		// Set up callbacks
		var oThis = this;
		this.theFrameQueryObj.onload  = function() { FrameLoaded() };
		this.theFrameQueryObj.onabort = function() { FrameAbort() };
		this.theFrameQueryObj.onerror = function() { FrameError() };
	},

	SetFrameBase : function(strFrameBase)
	{
		// Only need for server-based
		if (this.clientType != kClientNone)
			return;
		if (strFrameBase == "")
			return;
		// Extract frameBase from **FrameBase**
		this.frameList = new FrameList();
		
		var frameArray = strFrameBase.split("\f");

		// Get dimensions of page from frame info
		var width = parseInt(frameArray[0]);
		var height = parseInt(frameArray[1]);
		this.currPageDim = Math.floor(width / 2) + "," + Math.floor(height / 2);

		// Get mapping to screen coordinates
		var xScale = this.printImageWidth  / width;
		var yScale = this.printImageHeight / height;
		if (yScale < xScale)
			xScale = yScale;
		var targetWidth  = Math.floor(width  * xScale);
		var targetHeight = Math.floor(height * xScale);
		var xOffset = Math.floor((this.printImageWidth - targetWidth) / 2);
		var yOffset = Math.floor((this.printImageHeight - targetHeight) / 2);

		for (var i = 2; i < frameArray.length; )
		{
			var type = parseInt(frameArray[i]);
			var left = Math.floor((parseInt(frameArray[i+1])   * xScale) + xOffset);
			var top = Math.floor((parseInt(frameArray[i+2])    * xScale) + yOffset);
			var right = Math.floor((parseInt(frameArray[i+3])  * xScale) + xOffset);
			var bottom = Math.floor((parseInt(frameArray[i+4]) * xScale) + yOffset);
			var staticItem = parseInt(frameArray[i+5]);
			var movable = parseInt(frameArray[i+6]);
			var fixedAspect = parseInt(frameArray[i+7]);
			if (movable != 0)
				type |= 8;
			this.frameList.AddItemEx(type, left, top, right, bottom,
										0, 0, "",
										"", "", 0, "", 
										staticItem, 2, 0, 0, "",
										fixedAspect, 0, 0, "",
										0, 0);
			this.frameList.items[this.frameList.items.length-1].loaded = 0;
			i = i + 8;
		}

		// Save coordinate info in frame list object for later retrieval
		this.frameList.width  = width;
		this.frameList.height = height;
		this.frameList.scale  = xScale;
		this.frameList.left   = xOffset;
		this.frameList.top    = yOffset;
		this.frameList.right  = xOffset + targetWidth;
		this.frameList.bottom = yOffset + targetHeight;

		// Set margins in screen coordinates
		this.leftMargin   = Math.floor(this.pageLeftMargin   * this.frameList.scale);
		this.topMargin    = Math.floor(this.pageTopMargin    * this.frameList.scale);
		this.rightMargin  = Math.floor(this.pageRightMargin  * this.frameList.scale);
		this.bottomMargin = Math.floor(this.pageBottomMargin * this.frameList.scale);

		// First make sure that we've got a non-static frame selected
		if ((this.activeFrame >= this.frameList.items.length) || 
			(this.frameList.items[this.activeFrame].staticItem))
		{
			for (var i = 0; i < this.frameList.items.length; i++)
			{
				if (! this.frameList.items[i].staticItem)
				{
					this.activeFrame = i;
					break;
				}
			}
		}
	},

	SetFrameQuery : function(strFrameInfo)
	{
		// Only need for server-based
		if (this.clientType != kClientNone)
			return;
		if (strFrameInfo == "")
			return;

		var frameArray = strFrameInfo.split("\f");

		// Get dimensions of page from frame info
		var activeFrame = parseInt(frameArray[0]);
		if (activeFrame >= this.frameList.items.length)
			return;

		// ignore type,left,top,right,bottom,staticItem,movable,fixedAspect 
		this.frameList.items[activeFrame].showFrame = parseInt(frameArray[9]);
		this.frameList.items[activeFrame].resizable = parseInt(frameArray[10]);
		this.frameList.items[activeFrame].ignoreMargins = parseInt(frameArray[11]);
		this.frameList.items[activeFrame].borders = new String(frameArray[12]);
		this.frameList.items[activeFrame].style = parseInt(frameArray[13]);
		if ((this.frameList.items[activeFrame].type & 3) == 1)
		{
			this.frameList.items[activeFrame].feather = parseInt(frameArray[14]);
			this.frameList.items[activeFrame].edgeRef = new String(frameArray[15]);
			if (frameArray.length >= 19)
			{
				this.frameList.items[activeFrame].imageJnl = new String(frameArray[16]);
				this.frameList.items[activeFrame].rotatable = parseInt(frameArray[17]);
				this.frameList.items[activeFrame].rotate = parseInt(frameArray[18]);
			}
			if (frameArray.length >= 20)
				this.frameList.items[activeFrame].shadow = new String(frameArray[19]);
		}
		else 
		{
			this.frameList.items[activeFrame].alignment = parseInt(frameArray[14]);
			this.frameList.items[activeFrame].text = new String(frameArray[15]);
			this.frameList.items[activeFrame].font = new String(frameArray[16]);
			this.frameList.items[activeFrame].ptSize = parseInt(frameArray[17]);
			this.frameList.items[activeFrame].color = new String(frameArray[18]);
			if (frameArray.length >= 21)
			{
				this.frameList.items[activeFrame].rotatable = parseInt(frameArray[19]);
				this.frameList.items[activeFrame].rotate = parseInt(frameArray[20]);
			}
			if (frameArray.length >= 22)
				this.frameList.items[activeFrame].shadow = new String(frameArray[21]);
		}
		this.frameList.items[activeFrame].loaded = 1;
	},

	FrameQuery : function()
	{
		if (this.theFrameQueryObj == null)
			return;

		// Build URL for editing operation
		if (this.strImageServerPage != null)
			strURL = this.strImageServerPage;
		else
			strURL = this.strWebServerPage;
		if (strURL.indexOf("://") == -1)
			strURL = documentBase + strURL;

		strURL = strURL + "?Cmd=FrameQuery&ID=" + this.currPrintPageID;

		// Append frame number
		strURL = strURL + "&Frame=" + this.activeFrame;

		// Append time in milliseconds so nothing get's cached
		var currTime = new Date();
		strURL = strURL + "&tm=" + currTime.getTime();

		// Add RefID (album ID) if available
		if (this.strRefID != null)
			strURL += "&RefID=" + this.strRefID;

		// Add cookie information if available
		if (this.cookieDomain != null)
			strURL += "&CD=" + this.cookieDomain;
		if (this.cookiePath != null)
			strURL += "&CP=" + this.cookiePath;

		// Replace source for frameInfoObj IMG ...
		this.theFrameQueryObj.src = strURL;
	},

	SetFrameBaseCookies : function()
	{
		var strFrameBase = "";
		var strPageInfo = "";
		var strFrameInfo = "";

		// Process cookies
		var	strCookie	=	document.cookie;
		var	astrCookie = strCookie.split(";");
		for	(var i = 0;	i	<	astrCookie.length; ++i)
		{
			var	astrFields = astrCookie[i].split("=");
			var	strName	= astrFields[0];
			strName	= strName.replace(/\s/g,"");
			var	bDeleteIt = false;
			var strValue = "";
			if (astrFields.length >= 2)
			{
				strValue = astrFields[1];
				strValue = unescape(strValue.replace(/\+/g,' '));
			}
			
			if (strName	== "PxPageInfo")
			{
				//alert(strValue.replace(/-/g, "\n"));
				strPageInfo = strValue;
				bDeleteIt	=	true;
			}	
			else if	(strName ==	"PxFrameBase")
			{
				strFrameBase = strValue;
				bDeleteIt	=	true;
			}
			else if	(strName ==	"PxFrameInfo")
			{
				strFrameInfo = strValue;
				bDeleteIt	=	true;
			}
			
			if (bDeleteIt)
			{
				// Delete	the	cookie
				var	dtExpires	=	new	Date();
				dtExpires.setFullYear(dtExpires.getFullYear()-1);
				var strCookie =	strName	+ "=; Expires="+dtExpires.toGMTString();
				if (this.cookieDomain != null)
					strCookie += "; domain=" + this.cookieDomain;
				if (this.cookiePath != null)
					strCookie += "; path=" + this.cookiePath;
				document.cookie = strCookie;
			}
		}	// for 
		this.processPageInfo = 0;

		// If we got page info, then update
		if (strPageInfo != "")
		{
			var aPageInfo = strPageInfo.split("-");
			this.thePrintForm.elements["Curr"].value = aPageInfo[0];
			aPageInfo = aPageInfo.slice(1);
			this.thePrintForm.elements["Pgs"].value = aPageInfo.join(",");
			this.SetPageInfo(this.thePrintForm.elements["Curr"].value, this.thePrintForm.elements["Pgs"].value);
			if (this.pageUpdateCallback != null)
				eval(this.pageUpdateCallback);
		}

		// If we got frame info, then update
		if (strFrameBase != "")
		{
			// Get page width, page height
			var strFrameBase2 = "";
			strFrameBase2 += Decode64(strFrameBase.charCodeAt(0)) * 64 + Decode64(strFrameBase.charCodeAt(1));
			strFrameBase2 += '\f';
			strFrameBase2 += Decode64(strFrameBase.charCodeAt(2)) * 64 + Decode64(strFrameBase.charCodeAt(3));

			// Get frame info
			var i;
			for (i = 4;  i < strFrameBase.length; i+=9)
			{
				var type, staticItem, movable, fixedAspect;
				var left, top, right, bottom;
				var flags;

				flags = Decode64(strFrameBase.charCodeAt(i+0));
				if (flags & 0x01)
					type = 2;
				else
					type = 1;
				if (flags & 0x02)
					staticItem = 1;
				else
					staticItem = 0;
				if (flags & 0x04)
					movable = 1;
				else
					movable = 0;
				if (flags & 0x08)
					fixedAspect = 1;
				else
					fixedAspect = 0;
				left =	Decode64(strFrameBase.charCodeAt(i+1)) * 64 + Decode64(strFrameBase.charCodeAt(i+2));
				right =	Decode64(strFrameBase.charCodeAt(i+3)) * 64 + Decode64(strFrameBase.charCodeAt(i+4));
				top =	Decode64(strFrameBase.charCodeAt(i+5)) * 64 + Decode64(strFrameBase.charCodeAt(i+6));
				bottom =Decode64(strFrameBase.charCodeAt(i+7)) * 64 + Decode64(strFrameBase.charCodeAt(i+8));

				strFrameBase2 += '\f' + type + 
								'\f' + left + 
								'\f' + right + 
								'\f' + top + 
								'\f' + bottom + 
								'\f' + staticItem + 
								'\f' + movable + 
								'\f' + fixedAspect;
			}
			this.SetFrameBase(strFrameBase2);
	//		this.SetFrameBase(strFrameBase);
		}
		if (strFrameInfo != "")
			this.SetFrameQuery(strFrameInfo);
		FrameChanged();
	},

	SetImageJournal : function(strImageJnl)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.SetImageJournal(strImageJnl);
		else
		{
			if (this.frameList.items.length == 0)
				return;

			this.frameList.items[this.activeFrame].imageJnl = strImageJnl;

			// Force refresh of image
			this.resetFrame = this.activeFrame;
			this.UpdatePrintPageImage("setImageJnl", strImageJnl);
		}
	},

	GetImageJournal : function()
	{
		var strImageJnl = "";

		// Pass to client
		if (this.clientType != kClientNone)
			strImageJnl = this.theClientObj.GetImageJournal();
		else
		{
			if (this.frameList.items.length != 0)
				strImageJnl = this.frameList.items[this.activeFrame].imageJnl;
		}
		return strImageJnl;
	},

	PrintPages : function(strPageList, resolution)
	{
		if (this.clientType != kClientNone)
			this.theClientObj.PrintPages(strPageList, resolution);
	},

	SetProductID : function(iProductID)
	{
		this.iProductID = iProductID;
		if (this.clientType != kClientNone)
			this.theClientObj.SetProductID(iProductID);
	},

	SetCropEdges : function(strCropEdges)
	{
		this.cropEdges = strCropEdges;
		if (this.clientType != kClientNone)
			this.theClientObj.SetCropEdges(strCropEdges);
	},

	SetCropLines : function(strCropLines)
	{
		this.cropLines = strCropLines;
		if (this.clientType != kClientNone)
			this.theClientObj.SetCropLines(strCropLines);
	},

	SetCoverType : function(strCover)
	{
		this.strCoverType = strCover;
	}

};

//----------------------------------------------------------------------------
// JavaScript object for managing the Pixami CompactJournal
//----------------------------------------------------------------------------

function PxCompactJnl(strCompactJnl)
{
	// Set image journal 
	this.SetCompactJnl(strCompactJnl);
}

PxCompactJnl.prototype = {

	SetCompactJnl : function(strCompactJnl)
	{
		// Initialize params
		this.bSepia = false;
		this.bGray = false;
		this.bAutoFix = false;
		this.bSharpen = false;
		this.bTone = false;
		this.bOpacity = false;
		this.autoFix1 = 50;
		this.autoFix2 = 50;
		this.sharpen = 30;
		this.tone1 = 0;
		this.tone2 = 0;
		this.opacity = 100;

		if (strCompactJnl == null)
			return;
		if (strCompactJnl == "")
			return;

		var aImageJnl = strCompactJnl.split(";");
		var i;
		for (i = 0; i < aImageJnl.length; i++)
		{
			var strImageJnl = aImageJnl[i];
			var op = strImageJnl.substring(0, 1);
			var params = strImageJnl.substring(1, strImageJnl.length);
			if (op == "g")
				this.bGray = true;
			else if (op == "s")
				this.bSepia = true;
			else if (op == "a")
			{
				var aParams = params.split(",");
				if (aParams.length >= 2)
				{
					this.bAutoFix = true;
					this.autoFix1 = parseInt(aParams[0]);
					this.autoFix2 = parseInt(aParams[1]);
				}
			}
			else if (op == "h")
			{
				this.bSharpen = true;
				this.sharpen = parseInt(params);
			}
			else if (op == "t")
			{
				var aParams = params.split(",");
				if (aParams.length >= 2)
				{
					this.bTone = true;
					this.tone1 = parseInt(aParams[0]);
					this.tone2 = parseInt(aParams[1]);
				}
			}
			else if (op == "o")
			{
				this.bOpacity = true;
				this.opacity = parseInt(params);
			}
		}
	},

	GetCompactJnl : function()
	{
		var strCompactJnl = "";
		if (this.bGray)
			strCompactJnl += "g";
		if (this.bSepia)
		{
			if (strCompactJnl.length != 0)
				strCompactJnl += ";s";
			else
				strCompactJnl += "s";
		}
		if (this.bAutoFix)
		{
			if (strCompactJnl.length != 0)
				strCompactJnl += ";a";
			else
				strCompactJnl += "a";
			strCompactJnl += this.autoFix1;
			strCompactJnl += ",";
			strCompactJnl += this.autoFix2;
		}
		if (this.bSharpen)
		{
			if (strCompactJnl.length != 0)
				strCompactJnl += ";h";
			else
				strCompactJnl += "h";
			strCompactJnl += this.sharpen;
		}
		if (this.bTone)
		{
			if (strCompactJnl.length != 0)
				strCompactJnl += ";t";
			else
				strCompactJnl += "t";
			strCompactJnl += this.tone1 + "," + this.tone2;
		}
		if (this.bOpacity)
		{
			if (strCompactJnl.length != 0)
				strCompactJnl += ";o";
			else
				strCompactJnl += "o";
			strCompactJnl += this.opacity;
		}
		return strCompactJnl;
	}
};

function PxConvertColorSpec(strColor)
{
	// Convert string to integer.
	var color = parseInt(strColor, 16);

	// Now specify as BGR, so swap bytes
	var r = (color & 0xff0000) >> 16;
	var g = (color & 0x00ff00) >> 8;
	var b = (color & 0x0000ff);
	var newColor = (b << 16) + (g << 8) + r;
	return newColor;
}

// Class for managing tokens for escaping strings.  Pair of character and token
function EscToken(chr, token)
{
	this.chr   = chr;
	this.token = token;
}

// Class for managing a list of escape tokens.
function EscTokenList()
{
	this.list = new Array();
}

EscTokenList.prototype = {

	// Add a new token to the list
	Add : function(chr, token)
	{
		this.list[this.list.length] = new EscToken(chr, token); 
	},

	// See if there's an escape token for the character and return if there is.
	Escape : function(chr)
	{
		// Search through the list for the character
		for (var i = 0; i < this.list.length; i++)
		{
			if (this.list[i].chr == chr)
				return this.list[i].token;
		}

		// Not found - return character as is.
		return chr;
	},

	// Return the character for a token
	Unescape : function(token)
	{
		// Search through the list for the token
		for (var i = 0; i < this.list.length; i++)
		{
			if (this.list[i].token == token)
				return this.list[i].chr;
		}

		// Not found - return token as is.
		return token;
	}
};

var bTokenListInit = false;
var tokenList = null;
var tokenList2 = null;

function InitTokenList()
{
	// Check to see if already initialized
	if (bTokenListInit)
		return;
	bTokenListInit = true;

	// Allocate a token list
	tokenList = new EscTokenList();
	tokenList2 = new EscTokenList();

	// Add all tokens to the list
	tokenList.Add("{", "$lb");
	tokenList.Add("}", "$rb");
	tokenList.Add("(", "$lp");
	tokenList.Add(")", "$rp");
	tokenList.Add("[", "$ls");
	tokenList.Add("]", "$rs");
	tokenList.Add("<", "$lt");
	tokenList.Add(">", "$gt");
	tokenList.Add(";", "$se");
	tokenList.Add(":", "$co");
	tokenList.Add("$", "$do");
	tokenList.Add("-", "$da");
	tokenList.Add("!", "$ex");
	tokenList.Add("&", "$am");
	tokenList.Add("?", "$qm");
	tokenList.Add("%", "$pe");
	tokenList.Add("'", "$ap");
	tokenList.Add("`", "$ag");
	tokenList.Add("\"","$qu");
	tokenList.Add("~", "$ti");
	tokenList.Add("^", "$ca");
	tokenList.Add("#", "$po");
	tokenList.Add("|", "$pi");
	tokenList.Add("/", "$sl");
	tokenList.Add("\\","$bs");
	tokenList.Add("@", "$at");
	tokenList.Add("=", "$eq");
	tokenList.Add("+", "$pl");
	tokenList.Add(",", "$cm");
	tokenList.Add("\n","$nl");

	tokenList2.Add("{", "$lb");
	tokenList2.Add("}", "$rb");
	tokenList2.Add("[", "$ls");
	tokenList2.Add("]", "$rs");
	tokenList2.Add("<", "$lt");
	tokenList2.Add(">", "$gt");
	tokenList2.Add("$", "$do");
	tokenList2.Add("&", "$am");
	tokenList2.Add("%", "$pe");
	tokenList2.Add("'", "$ap");
	tokenList2.Add("`", "$ag");
	tokenList2.Add("\"","$qu");
	tokenList2.Add("~", "$ti");
	tokenList2.Add("^", "$ca");
	tokenList2.Add("|", "$pi");
	tokenList2.Add("\\","$bs");
	tokenList2.Add("+", "$pl");
	tokenList2.Add("\n","$nl");
}

function PxEscapeText(text0)
{
	// Make sure token list is initialized
	InitTokenList();

	// Unescape first to prevent double escaping
	var text = PxUnescapeText(text0);

	// Set up an array to hold the escaped characters.  After some tests, creating an array and 
	// then using join to create the string appears to be over 4 times faster than concatenating
	// a single character to the string.
	var aEsc = new Array();

	// Process each character in the string
	var textlen = text.length;
	for (var si = 0; si < textlen; si++)
	{
		var chr = text.charAt(si);

		// Check whether unicode character
		var n = chr.charCodeAt(0);
		var strEsc = 0;
		if (n >= 128)
			strEsc = PxUnicodeEscape(n);
		else
			strEsc = tokenList2.Escape(chr);

		// Add to array of escaped characters
		aEsc.push(strEsc);
	}
	return aEsc.join("");
}

function PxEscapeTextOld(text0)
{
	// Make sure token list is initialized
	InitTokenList();

	// Unescape first to prevent double escaping
	var text = PxUnescapeText(text0);

	// Set up an array to hold the escaped characters.  After some tests, creating an array and 
	// then using join to create the string appears to be over 4 times faster than concatenating
	// a single character to the string.
	var aEsc = new Array();

	// Process each character in the string
	var textlen = text.length;
	for (var si = 0; si < textlen; si++)
	{
		var chr = text.charAt(si);

		// Check whether unicode character
		var n = chr.charCodeAt(0);
		var strEsc = 0;
		if (n >= 128)
			strEsc = PxUnicodeEscape(n);
		else
			strEsc = tokenList.Escape(chr);

		// Add to array of escaped characters
		aEsc.push(strEsc);
	}
	return aEsc.join("");
}

function PxUnicodeEscape(n)
{
	var hexString = n.toString(16);
	hexString = hexString.toUpperCase();
	if (hexString.length == 2)
		hexString = "00" + hexString;
	else if (hexString.length == 3)
		hexString = "0" + hexString;
	return "$u" + hexString;
}

function PxUnicodeUnescape(hexString)
{
	return unescape(hexString);
}

function PxUnescapeText(text)
{
	// Make sure token list is initialized
	InitTokenList();

	// Set up an array to hold the unescaped characters.  After some tests, creating an array and 
	// then using join to create the string appears to be over 4 times faster than concatenating
	// a single character to the string.
	var aUnEsc = new Array();

	// Process the entire string
	var textlen = text.length;
	for (var si = 0; si < textlen; )
	{
		// Get next character from the string
		var chr = text.charAt(si++);

		// If it's a '$' and there's enough room for the token, try to find token
		var strUnEsc = 0;
		if ((chr == "$") && (text.charAt(si) == "u") && (si <= (textlen - 5)))
		{
			// Map to unicode and add to string
			var hexString = "%u";
			for (var i = 1; i < 5; i++)
			{
				var chr = text.charAt(si+i);
				if (("0" <= chr) && (chr <= "9"))
					hexString += chr;
				else if (("A" <= chr) && (chr <= "F"))
					hexString += chr;
				else if (("a" <= chr) && (chr <= "f"))
					hexString += chr;
				else
					break;
			}
			if (i == 5)
			{
				strUnEsc = PxUnicodeUnescape(hexString);
				si += 5;
			}
			else
				strUnEsc = chr;
		}
		// If it's a $ and there's enough room for the token, try to find token
		else if ((chr == "$") && (si <= (textlen - 2)))
		{
			// Build escape token
			var token = "$" + text.charAt(si) + text.charAt(si+1);

			// Map to a character and add to string
			strUnEsc = tokenList.Unescape(token);

			// Point past token
			si += 2;
		}
		else
			strUnEsc = chr;

		// Add to array of escaped characters
		aUnEsc.push(strUnEsc);
	}
	return aUnEsc.join("");
}

function PxUnescapeTextForMarkup(text)
{
	// Perform Pixami unescaping
	var text = PxUnescapeText(text);
	
	// Replace special characters with entity strings
	text = text.replace(/\&/g,"&amp;");
	text = text.replace(/\</g,"&lt;");
	text = text.replace(/\>/g,"&gt;");
	return text;
}

function PxGetValueFromXML(strXML, strKeyword)
{
	// Look for keyword in XML
	var index = strXML.indexOf(strKeyword);
	if (index == -1)
		return "";

	// Skip over keyword, equals and first quote
	index += strKeyword.length + 2;

	// Search for next quote
	var index2 = strXML.indexOf('"', index);
	if (index2 == -1)
		return "";

	// Return value
	var strValue = strXML.substr(index, index2 - index);
	return strValue;
}

function PxGetTagValueFromXML(strXML, strTag, strEndTag)
{
	// Look for keyword in XML
	var index = strXML.indexOf(strTag);
	if (index == -1)
		return "";

	// Skip over tag
	index += strTag.length;

	// Search for next quote
	var index2 = strXML.indexOf(strEndTag, index);
	if (index2 == -1)
		return "";

	// Return value
	var strValue = strXML.substr(index, index2 - index);
	return strValue;
}

function PxParseLineBorders(strXML)
{
	var strValue = "";
	var strLineBorderHeader = "<LineBorder ";
	var index1 = strXML.indexOf(strLineBorderHeader);
	if (index1 != -1)
	{
		var index2 = strXML.indexOf(strLineBorderHeader, index1+1);
		var strBorder;
		if (index2 == -1)
			strBorder = strXML.substr(index1);
		else
			strBorder = strXML.substr(index1, index2);
		if (strValue.length != 0)
			strValue += ",";
		strValue += PxGetValueFromXML(strBorder, "Width") + "," + 
					PxGetValueFromXML(strBorder, "Opacity") + "," + 
					PxGetValueFromXML(strBorder, "Red") + "," + 
					PxGetValueFromXML(strBorder, "Green")  + "," + 
					PxGetValueFromXML(strBorder, "Blue");
	}
	return strValue;
}

function PxParseShadow(strXML)
{
	var strValue = "";
	var strShadowHeader = "<Shadow ";
	var index1 = strXML.indexOf(strShadowHeader);
	if (index1 != -1)
		strValue = PxGetValueFromXML(strXML, "Width") + "," + PxGetValueFromXML(strXML, "Opacity");
	return strValue;
}

function Decode64(charCode)
{
	if ((0x30 <= charCode) && (charCode <= 0x39))
		return (charCode-0x30);
	else if ((0x41 <= charCode) && (charCode <= 0x5A))
		return (10 + charCode-0x41);
	else if ((0x61 <= charCode) && (charCode <= 0x7A))
		return (10+26 + charCode-0x61);
	else if (charCode == 0x28)	// "("
		return 10+26+26+1;
	else if (charCode == 0x29)	// ")"
		return 10+26+26+2;
	else if (charCode == 0x2D)	// "-"
		return 10+26+26+3;
	else if (charCode == 0x2E)	// "."
		return 10+26+26+4;
	else if (charCode == 0x2F)	// "/"
		return 10+26+26+5;
	else
		return 10+26+26+5;
}


