if (window.console === undefined) {
	window.console = function() {}
	window.console.log = function() {}
}

var _Photocopa = Class.create({
	initialize: function(_args) {
		this._photocopaVersion = 1.0,this._loadingBarTimeout = 500,this._confirmOnBeforeUnloadMsg = "Navigating away from this page will cause you to lose your work!",this._onbeforeunload = window.onbeforeunload,this._domIsLoaded = false,this._tileImageLimits = {_x: 0,_y: 0},
		this._tileImageMatrices = [],this._tileImages = [],this._photoDragBounds = [],this._currentPhotoZoomLevel = 0,this._constrainPhoto = true,this._photoOverviewWindowWasJustDrug = false,this._photoZoomSlider = false,this._colorWidthSlider = {},this._currentSelectedColorSquareIndex = 0,
		this._colorSquareOrder = [0,1,2,3,4],this._currentlySelectedHex = false,this._colorSquareIDHexs = ["","","","",""],this._scratchBoxIDHexs = [],this._currentRightColTab = "photos",this._numColorSquares = 5,this._currentPhotoColorSet = {},this._defaults = {},this._flickrQueryResult = {},
		this._currentPhotocopaID = 0,this._currentPaletteID = 0,this._validURLRegEx = /https?:\/\/[\-a-zA-Z0-9+&@#\/%?=~_|$!:,.;]*[a-zA-Z0-9+&@#\/%=~_|$]/,this._openPhotocopaResult = {},this._eventDelineation = [],this._photoColorsHexByElementID = [],
		this._paletteMeta = {_title: "",_desc: "",_inspirationLink: "",_inspirationDesc: "",_useLink: "",_tags: "",_groups: []},
		this._timeouts = {_loadingBar: 0,_photoHoverPreview: 0,_updateExtractedPhotoColors: 0,_toolTipHide: 0,_addPhotocopaImage: 0,_photosToolTipShow: [],_colorSquare: []}
		this._ui = {
			_photoOverviewImageDimmensions: {_width: 0,_height: 0},
			_photoOverviewCumulativeOffset: {left: 0,top: 0},
			_photoContainerDimmensions: {width: 0,height: 0},
			_photoContainerMarginTop: 0,
			_photoZoomTrackHeight: 0,
			_photoOverviewWindowDimmensions: {_width: 0,_height: 0},
			_photoDimmensions: [],
			_photoContainerCumulativeOffset: {left: 0,top: 0},
			_photoCumulativeOffset: {left: 0,top: 0},
			_colorSquareContainerWidth: 0,
			_colorWidthSliderWidth: 0
		};


		// Pre-load images
		["/images/v3.5/_/photocopa/loadingTile.png","/images/v3.5/_/photocopa/colorNotSetBG.png"].each(function (_image) {
			(new Image()).src = getStaticURL(_image);
		})


		// Parse _args
		this._isLoggedIn = (_args._isLoggedIn === true);
		this._currentPhotocopaImageID = parseInt(_args._currentPhotocopaImageID,10);
		this._currentPhotocopaImageTitle = _args._currentPhotocopaImageTitle;
		this._currentPhotocopaImageAuthor = _args._currentPhotocopaImageAuthor;
		this._currentPhotoURIPrefix = getStaticURL("/" + this.getNumericallyShardedDirectory("images/photocopa/%s/",this._currentPhotocopaImageID));
		this._currentPhotocopaImageDimmensions = _args._currentPhotocopaImageDimmensions;
		this._currentPhotocopaImageOverviewDimmensions = _args._currentPhotocopaImageOverviewDimmensions;

		this._initColorHexs = _args._initColorHexs;
		this._initColorWidths = _args._initColorWidths;
		this._initScratchBoxHexs = _args._initScratchBoxHexs;

		// Verify this._initColorHexs - we want 5 valid hex values || blank strings
		if ((Object.isArray(this._initColorHexs) === true) && (this._initColorHexs.length === this._numColorSquares)) {
			for (var _i=0;_i<this._numColorSquares;_i++) {
				if (this._initColorHexs[_i] === "") {
					continue;
				}
				if ((this._initColorHexs[_i] === undefined) || (this.isValidHex(this._initColorHexs[_i]) === false)) {
					this._initColorHexs[_i] = "";
				}
			}
		} else {
			this._initColorHexs = ["","","","",""];
		}
		this._colorSquareIDHexs = this._initColorHexs;

		// Verify this._initColorWidths - we want this._numColorSquares floats which total to 1.0
		if ((Object.isArray(this._initColorWidths) === true) && (this._initColorWidths.length === this._numColorSquares)) {
			if (this._initColorWidths.sum() !== 1.0) {
				this._initColorWidths = [0.2,0.2,0.2,0.2,0.2];
			} else {
				for (var _i=0;_i<this._numColorSquares;_i++) {
					if ((this._initColorWidths[_i] === undefined) || (isNaN(this._initColorWidths[_i]) === true)) {
						this._initColorWidths = [0.2,0.2,0.2,0.2,0.2];
						break;
					}
				}
			}
		} else {
			this._initColorWidths = [0.2,0.2,0.2,0.2,0.2];
		}
		_args._initDefaultSelectedColorSquareIndex = parseInt(_args._initDefaultSelectedColorSquareIndex,10);
		if (this.isWithinRange(_args._initDefaultSelectedColorSquareIndex,0,this._numColorSquares) === false) {
			_args._initDefaultSelectedColorSquareIndex = 0;
		}
		this._currentSelectedColorSquareIndex = _args._initDefaultSelectedColorSquareIndex;

		// these should be populated when a photocopaID is being loaded
		if (_args._currentPhotocopaID !== undefined) {
			this._currentPhotocopaID = parseInt(_args._currentPhotocopaID,10);
		}
		if (_args._currentPaletteID !== undefined) {
			this._currentPaletteID = parseInt(_args._currentPaletteID,10);
		}
		if (_args._paletteMeta !== undefined) {
			this._paletteMeta = {
				_title: _args._paletteMeta._title,
				_desc: _args._paletteMeta._desc,
				_inspirationLink: _args._paletteMeta._inspirationLink,
				_inspirationDesc: _args._paletteMeta._inspirationDesc,
				_useLink: _args._paletteMeta._useLink,
				_tags: _args._paletteMeta._tags,
				_groups: _args._paletteMeta._groups
			};
		}
		if ((Object.isArray(this._initScratchBoxHexs) === true) && (this._initScratchBoxHexs.length === 11)) {
			for (var _i=0;_i<11;_i++) {
				if ((this.isValidHex(this._initScratchBoxHexs[_i]) === false) && (this._initScratchBoxHexs[_i] !== "")) {
					this._initScratchBoxHexs[_i] = "";
				}
			}
		} else {
			this._initScratchBoxHexs = undefined;
		}
		// Done loading _args


		// Photocopa events
		Event.observe(window,"unload",function(_event) {
			this.unload(_event);
		}.bind(this));

		Event.observe(window,"resize",function(_event) {
			this.initOverview();
			this.showPhotoAtZoomLevel(this._currentPhotoZoomLevel);
		}.bind(this));

		document.observe("dom:loaded",function(_event) {
			this._ui._photoContainerDimmensions = $("photoContainer").getDimensions();
			this._ui._photoZoomTrackHeight = $("photoZoomTrack").getDimensions().height;
			this._ui._colorSquareContainerWidth = $("colorSquareContainer").getWidth();

			// Interface events //
			Event.observe("photoContainer","mouse:wheel",function(_event) {
				_event.stop();
				var _mouseCoordinates = {_x: Event.pointerX(_event.memo._event),_y: Event.pointerY(_event.memo._event)};

				if (this._currentPhotoZoomLevel !== -1) { // If not drawing new BG container divs
					if (_event.memo._delta < 0) {
						this.showPhotoAtZoomLevel(this._currentPhotoZoomLevel - 1,_mouseCoordinates);
					} else {
						this.showPhotoAtZoomLevel(this._currentPhotoZoomLevel + 1,_mouseCoordinates);
					}
				}
			}.bind(this));

			Event.observe("photoZoomIn","click",function(_event) {
				this.showPhotoAtZoomLevel(this._currentPhotoZoomLevel + 1);
				this.centerPhoto();
			}.bind(this));

			Event.observe("photoZoomOut","click",function(_event) {
				this.showPhotoAtZoomLevel(this._currentPhotoZoomLevel - 1);
				this.centerPhoto();
			}.bind(this));

			Event.observe("photoOverviewShowHide","click",function(_event) {
				if (this._ui._photoContainerMarginTop === 0) {
					this._ui._photoContainerMarginTop = parseInt($("photoContainer").style.marginTop,10);
				}

				if ($("photoOverview").style.display === "none") {
					$("photoOverview").show();
					$("photoContainer").style.marginTop = (this._ui._photoContainerMarginTop.toString() + "px");
					$("photoOverviewShowHide").style.backgroundPosition = "-118px 0";
					this.updatePhotoOverviewInterface();
				} else {
					$("photoOverview").hide();
					$("photoContainer").style.marginTop = 0;
					$("photoOverviewShowHide").style.backgroundPosition = "-134px 0";
				}
			}.bind(this));

			Event.observe("photoOverviewOverlay","click",function(_event) {
				if ((this.isPartOfPhotoHidden() === true) && (this._photoOverviewWindowWasJustDrug === false)) {
					var _top = (_event.clientY - this._ui._photoOverviewCumulativeOffset.top - (this._ui._photoOverviewWindowDimmensions._height / 2) + document.viewport.getScrollOffsets().top);
					var _left = ((_event.clientX - this._ui._photoOverviewCumulativeOffset.left - (this._ui._photoOverviewWindowDimmensions._width / 2)));

					var _tmp = this.getPhotoOverviewWindowContraints(_left,_top);
					_left = _tmp[0];
					_top = _tmp[1];

					this.updatePhotoOverviewInterface(false,undefined,undefined,_top,_left);
					this.photoOverviewWindowOnDrag();
					this.loadPhotoTilesInView();
					this.updateExtractedPhotoColorsDelegator();
				}
				this._photoOverviewWindowWasJustDrug = false;
			}.bind(this));

			Event.observe("photo","mouseout",function(_event) {
				clearTimeout(this._timeouts._photoHoverPreview);
			}.bind(this));
			Event.observe("photo","mousedown",function(_event) {
				this._eventDelineation["lastPhotoMouseDown"] = (new Date()).getTime();
			}.bind(this));
			Event.observe("photo","mousemove",function(_event) {
				this._eventDelineation["lastPhotoMouseMove"] = (new Date()).getTime();
				clearTimeout(this._timeouts._photoHoverPreview);
				this._timeouts._photoHoverPreview = (this.showPhotoHoverPreview.bind({_this: this,_event: _event})).delay(0.55);

				$("photoHoverPreview").hide();
			}.bind(this));
			Event.observe("photo","mouseup",function(_event) {
				if (this._eventDelineation["lastPhotoMouseDown"] > this._eventDelineation["lastPhotoMouseMove"]) {
					var _photoCumulativeOffset = $("photo").cumulativeOffset();
					var _x = (Event.pointerX(_event) - _photoCumulativeOffset.left);
					var _y = (Event.pointerY(_event) - _photoCumulativeOffset.top);

					if ((this.isWithinRange(_x,0,this._ui._photoDimmensions[this._currentPhotoZoomLevel]._width) === true) && (this.isWithinRange(_y,0,this._ui._photoDimmensions[this._currentPhotoZoomLevel]._height) === true)) {
						clearTimeout(this._timeouts._photoHoverPreview); // Don't get the hover preview for this color

						new Ajax.Request("/ajax/photocopaGetHexAtPixel?pID=" + this._currentPhotocopaImageID.toString() + "&c=" + _x + "," + _y + "&z=" + this._currentPhotoZoomLevel.toString(),{
							onSuccess: function(_transport) {
								var _hex = _transport.responseText;
								if (this.isValidHex(_hex) === true) {
									this.updateCurrentSelectedColor(_hex);
									this.updateColorSquareSelectedIndicator();
								}
							}.bind(this)
						});
					}
				}
			}.bind(this));

			new Draggable("photo",{
				onDrag: function(_photoDraggableElement,_mouseCoordinates) {
					this.updatePhotoOverviewInterface();
				}.bind(this),
				onEnd: function(_photoDraggableElement,_mouseCoordinates) {
					this.loadPhotoTilesInView();
					this.updateExtractedPhotoColorsDelegator();
				}.bind(this),
				zindex: 1,
				starteffect: null,
				reverteffect: null,
				endeffect: null,
				snap: function(_x,_y,_photoDraggableElement) {
					if (this._constrainPhoto === true) {
						var _tmp = this.getPhotoContainerContraints(_x,_y);

						_x = _tmp[0];
						_y = _tmp[1];
					}

					return [_x,_y];
				}.bind(this)
			});

			new Draggable("photoOverviewWindow",{
				onDrag: function(_photoOverviewWindowDraggableElement,_mouseCoordinates) {
					this.photoOverviewWindowOnDrag();
				}.bind(this),
				onEnd: function(_photoOverviewWindowDraggableElement,_mouseCoordinates) {
					this.loadPhotoTilesInView();
					this._photoOverviewWindowWasJustDrug = true; // Used for #photoOverviewOverlay.click()
					this.updateExtractedPhotoColorsDelegator();
				}.bind(this),
				zindex: 2,
				starteffect: null,
				reverteffect: null,
				endeffect: null,
				snap: function(_x,_y,_photoDraggableElement) {
					var _tmp = this.getPhotoOverviewWindowContraints(_x,_y);
					_x = _tmp[0];
					_y = _tmp[1];

					this.updatePhotoOverviewInterface(false,(_x * -1),(_y * -1));

					return [_x,_y];
				}.bind(this)
			});

			/***** init UI && events *****/
			this.loadPhoto();
			this.updateSaveBtn();

			// Tool Tip
			document.getElementsByTagName("body")[0].appendChild(Builder.node("div",{id: "toolTipDiv",style: "display: none;"},[
				Builder.node("table",{id: "toolTipTable",cellpadding: 0,cellspacing: 0,className: "vt"},[
					Builder.node("tr",[Builder.node("td",{className: "c0"}),Builder.node("td",{className: "c1"}),Builder.node("td",{className: "c2"})]),
					Builder.node("tr",[Builder.node("td",{className: "c3"}),Builder.node("td",{id: "toolTipContent",className: "c4"}),Builder.node("td",{className: "c5"})]),
					Builder.node("tr",[Builder.node("td",{className: "c6"}),Builder.node("td",{className: "c7"}),Builder.node("td",{className: "c8"})])
				])
			]));
			Event.observe("toolTipDiv","mouseover",function(_event) {
				clearTimeout(this._timeouts._toolTipHide);
			}.bind(this));
			Event.observe("toolTipDiv","mouseout",function(_event) {
				this._timeouts._toolTipHide = this.fadeTooltip.delay(5);
			}.bind(this));

			// Overlay onclick()
			Event.observe("clOverlay","click",function(_event) {
				this.hideOverlay();
			}.bind(this));

			// Color squares > DOM
			var _colorSquareIDs = [];
			var _colorSquareRMBTNIDs = [];
			for (var _i=0;_i<this._numColorSquares;_i++) {
				_colorSquareIDs[_i] = ("colorSquare_" + _i.toString());
				_colorSquareRMBTNIDs[_i] = ("colorSquareRMBTN_" + _i.toString());
				$("colorSquareContainer").appendChild(Builder.node("div",{
					className: "s",
					id: _colorSquareIDs[_i],
					style: "background: " + ((this._initColorHexs[_i] === "") ? "url(" + getStaticURL("/images/v3.5/_/photocopa/colorNotSetBG.png") + ") center 0 no-repeat" : ("#" + this._initColorHexs[_i])) + ";"
				},Builder.node("div",{id: _colorSquareRMBTNIDs[_i],style: "display: none;"})));

				Droppables.add(_colorSquareIDs[_i],{
					containment: ["photoColors_sampledColors","photoColors_bright","photoColors_muted","photoColors_analogous","photoColors_chance","photoColors_dark","photoColors_light"],
					hoverclass: "hover",
					onDrop: function(_draggableElement,_droppableElement,_event) {
						var _hex = this._this._photoColorsHexByElementID[_draggableElement.id];

						if (this._this.isValidHex(_hex) === true) {
							this._this._colorSquareIDHexs[this._this.getNumericIDFromElementID(_droppableElement.id)] = _hex;
							this._this.updateColorSquareBackgrounds();
						}
					}.bind({_this: this,_colorSquareID: _colorSquareIDs[_i]})
				});

				Event.observe(_colorSquareIDs[_i],"mouseout",function(_event) {
					clearTimeout(this._timeouts._colorSquare[this.getNumericIDFromElementID(Event.element(_event).id)]);
					this.hideTooltip();
				}.bind(this));

				Event.observe(_colorSquareIDs[_i],"mousemove",function(_event) {
					var _id = this.getNumericIDFromElementID(Event.element(_event).id);

					clearTimeout(this._timeouts._colorSquare[_id]);
					clearTimeout(this._timeouts._toolTipHide);

					this._timeouts._colorSquare[_id] = (function() {
						var _element = Event.element(this._event);
						var _id = this._this.getNumericIDFromElementID(_element.id);

						if (this._this.isValidHex(this._this._colorSquareIDHexs[_id]) === true) {
							var _hex = this._this._colorSquareIDHexs[_id];
							var _rgb = this._this.hex2rgb(_hex);
							_rgb = (_rgb[0].toString() + "," + _rgb[1].toString() + "," + _rgb[2].toString());
							var _hsv = this._this.hex2hsv(_hex);
							_hsv = (_hsv[0].toString() + "," + _hsv[1].toString() + "," + _hsv[2].toString());

							$("toolTipContent").update();
							$("toolTipContent").appendChild(Builder.node("strong","Hex: "));
							$("toolTipContent").appendChild(Builder.node("span",_hex));
							$("toolTipContent").appendChild(Builder.node("br"));

							$("toolTipContent").appendChild(Builder.node("strong","RGB: "));
							$("toolTipContent").appendChild(Builder.node("span",_rgb));
							$("toolTipContent").appendChild(Builder.node("br"));

							$("toolTipContent").appendChild(Builder.node("strong","HSV: "));
							$("toolTipContent").appendChild(Builder.node("span",_hsv));

							var _offset = _element.cumulativeOffset();
							var _dimensions = _element.getDimensions();
							var _toolTipDimensions = $("toolTipDiv").getDimensions();

							this._this.positionShowTooltip({
								_top: (_offset.top - _toolTipDimensions.height + 4),
								_left: (_offset.left + (_dimensions.width / 2))
							},"vb");
						}
					}.bind({_this: this,_event: _event})).delay(0.3);
				}.bind(this));
			}

			// Color sliders > DOM
			var _colorWidthSliderIDs = [];
			for (var _i=0;_i<(this._numColorSquares - 1);_i++) {
				_colorWidthSliderIDs[_i] = ("colorWidthSlider_" + _i.toString());
				$("colorWidthSlidersContainer").appendChild(Builder.node("div",{className: "s",id: _colorWidthSliderIDs[_i]}));
			}
			this._ui._colorWidthSliderWidth = $("colorWidthSlider_0").getWidth();

			// Turn this._initColorWidths in pixels
			var _sliderValue = this.sliderValues2absoluteFromPercentage(this._initColorWidths);

			// Init colorSquareWitdh sliders
			this._colorWidthSlider = new Control.Slider(_colorWidthSliderIDs,"colorWidthSlidersContainer",{
				range: $R(0,this._ui._colorSquareContainerWidth),
				values: $R((this._ui._colorWidthSliderWidth / 2),this._ui._colorSquareContainerWidth - (this._ui._colorWidthSliderWidth / 2)),
				sliderValue: _sliderValue,
				restricted: true,
				restrictedOffset: this._ui._colorWidthSliderWidth,
				onSlide: function(_values) {
					this.updateColorSquareWidths();
					this.updateColorSquareSelectedIndicator();
				}.bind(this)
			});

			// Init these widths
			this.updateColorSquareWidths();
			this.updateColorSquareSelectedIndicator();

			// Click events for colorSquares
			_colorSquareIDs.each(function(_elementID) {
				Event.observe(_elementID,"mouseover",function(_event) {
					var _colorSquareRMBTNID = this.getNumericIDFromElementID(Event.element(_event).id);
					if (this._colorSquareIDHexs[_colorSquareRMBTNID] !== "") {
						$("colorSquareRMBTN_" + _colorSquareRMBTNID.toString()).show();
					}
				}.bind(this));

				Event.observe(_elementID,"mousedown",function(_event) {
					this._currentSelectedColorSquareIndex = this.getNumericIDFromElementID(Event.element(_event).id);
					this.updateColorSquareSelectedIndicator();
				}.bind(this));

				Event.observe(_elementID,"mouseout",function(_event) {
					var _colorSquareRMBTNID = this.getNumericIDFromElementID(Event.element(_event).id);
					$("colorSquareRMBTN_" + _colorSquareRMBTNID.toString()).hide();
				}.bind(this));
			}.bind(this));

			// Click events for _colorSquareRMBTNs
			_colorSquareRMBTNIDs.each(function(_elementID) {
				Event.observe(_elementID,"click",function(_event) {
					this.rmColorFromColorSquare(this.getNumericIDFromElementID(Event.element(_event).id));
				}.bind(this));
			}.bind(this));

			// Color square sortable
			Sortable.create("colorSquareContainer",{
				tag: "div",
				overlap: "horizontal",
				constraint: "",
				onChange: function() {
					this.updateColorSquareOrder();
					this.updateColorWidthSliders();
					this.updateColorSquareSelectedIndicator();
				}.bind(this)
			});

			// Scratch boxes
			var _scratchBoxIDs = [];
			for (var _i=0;_i<11;_i++) {
				var _hex = (this._initScratchBoxHexs !== undefined) ? this._initScratchBoxHexs[_i] : "171717";
				this._scratchBoxIDHexs[_i] = (this._initScratchBoxHexs !== undefined) ? this._initScratchBoxHexs[_i] : "";

				_scratchBoxID = ("scratchBox_" + _i.toString());
				$("scratchBoxContainer").appendChild(Builder.node("div",{
					id: _scratchBoxID,
					style: "background-color: #" + _hex + "; border-color: #" + _hex + ";"
				}));

				Event.observe(_scratchBoxID,"dblclick",function(_event) {
					// Set the currently selected colorSquare to this cratchBox's bg color.
					_hex = this._scratchBoxIDHexs[this.getNumericIDFromElementID(Event.element(_event).id)];
					if (this.isValidHex(_hex) === true) {
						this.updateCurrentSelectedColor(_hex);
						this.updateColorSquareSelectedIndicator();
					}
				}.bind(this));

				Droppables.add(_scratchBoxID,{
					containment: ["colorSquareContainer","photoColors_sampledColors","photoColors_bright","photoColors_muted","photoColors_analogous","photoColors_chance","photoColors_dark","photoColors_light"],
					hoverclass: "hover",
					onDrop: function(_draggableElement,_droppableElement,_event) {
						if (/colorSquare_[0-9]/.test(_draggableElement.id) === true) {
							var _hex = this._this._colorSquareIDHexs[this._this.getNumericIDFromElementID(_draggableElement.id)];
						} else {
							var _hex = this._this._photoColorsHexByElementID[_draggableElement.id];
						}

						this._this._scratchBoxIDHexs[this._this.getNumericIDFromElementID(this._scratchBoxID)] = _hex;
						if (_hex !== "") {
							$(this._scratchBoxID).removeClassName("hover");
							new Effect.Morph(this._scratchBoxID,{
								style: {
									backgroundColor: ("#" + _hex),
									borderColor: ("#" + _hex)
								},
								duration: 0.5
							});
						}
					}.bind({_this: this,_scratchBoxID: _scratchBoxID})
				});
			}

			// Scratch box sortable
			Sortable.create("scratchBoxContainer",{
				tag: "div",
				overlap: "horizontal",
				constraint: "horizontal"
			});

			// Reset color widths
			Event.observe("resetColorSquareWidths","click",function(_event) {
				if (confirm("Are you sure you want to reset this palette's widths?\n\nClick OK to reset widths\nClick Cancel to keep your current widths") === true) {
					this.resetColorSquareWidths();
				}
			}.bind(this));

			// Fullscreen Palette Preview
			Event.observe("showFullscreenPalettePreview","click",function(_event) {
				this.showFullscreenPalettePreview();
			}.bind(this));

			// Nav btns
			////////////// PUBLISH //////////////
			Event.observe("publishBtn","click",function(_event) {
				if (this._isLoggedIn === false) {
					alert("Please log in to use this feature");
					return;
				}

				if (this.areColorSquaresSequential() === false) {
					alert("In order to publish your palette, all un-set colors need to be on the far-left side of your palette.");
					return;
				}

				var _hexs = this.getColorSquaresInOrder().without("");
				if (this.isWithinRange(_hexs.length,2,this._numColorSquares) === false) {
					alert("Palettes need at least two colors.");
					return;
				}

				$("clOverlayContent").update();
				$("clOverlayContent").appendChild(Builder.node("div",{className: "overlayContent loadingBG"},"Loading ..."));
				this.showOverlay();

				new Ajax.Request("/ajax/photocopaPreparePublishForm",{
					sanitizeJSON: true,
					method: "post",
					parameters: "hexs=" + encodeURIComponent(Object.toJSON(_hexs)) + "&currentPaletteID=" + this._currentPaletteID.toString() + "&currentPhotocopaImageID=" + this._currentPhotocopaImageID.toString(),
					onSuccess: function(_transport) {
						var _json = _transport.responseJSON;

						if (_json._errMsg !== undefined) {
							alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\n" + _json._errMsg);
							this.hideOverlay();
						} else {
							if ((_json._hexs === undefined) || (_json._groups === undefined)) {
								alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\nPhotocopa Prepare Publish Form: Bad Response");
								return false;
							}

							var _inspirationLink = (this._paletteMeta._inspirationLink !== "") ? this._paletteMeta._inspirationLink : _json._photocopaImageTitleURL;
							var _useLink = (this._paletteMeta._useLink !== "") ? this._paletteMeta._useLink : "http://";

							$("clOverlayContent").update();
							$("clOverlayContent").appendChild(Builder.node("div",{className: "publishPhotocopaFormDiv"},[
								Builder.node("form",{id: "publishPhotocopaForm",action: "#",method: "post",onsubmit: "return false;"},[
									Builder.node("div",{id: "publishPhotocopaFormDivLeftPanel",className: "p left"},[
										Builder.node("h2","Publish Palette"),
										Builder.node("div",{className: "clear"}),

										Builder.node("label",{htmlFor: "photocopaPaletteTitle"},"Palette Name"),
										Builder.node("input",{type: "text",id: "photocopaPaletteTitle",className: "textInput",maxlength: 20,value: this._paletteMeta._title}),
										Builder.node("div",{className: "clear"}),

										Builder.node("label",{htmlFor: "photocopaPaletteDesc"},"Palette Description"),
										Builder.node("textarea",{id: "photocopaPaletteDesc",rows: 0,cols: 0}),
										Builder.node("div",{className: "clear"}),

										Builder.node("label",{htmlFor: "photocopaPaletteInspirationLink"},"Inspiration Link"),
										Builder.node("input",{type: "text",id: "photocopaPaletteInspirationLink",title: _inspirationLink,className: "textInput"}),
										Builder.node("div",{className: "clear"}),

										Builder.node("label",{htmlFor: "photocopaPaletteInspirationDesc"},"Inspiration Desc."),
										Builder.node("textarea",{id: "photocopaPaletteInspirationDesc",rows: 0,cols: 0}),
										Builder.node("div",{className: "clear"}),

										Builder.node("label",{htmlFor: "photocopaPaletteUseLink"},"Use Link"),
										Builder.node("input",{type: "text",id: "photocopaPaletteUseLink",title: _useLink,className: "textInput"}),
										Builder.node("div",{className: "clear"}),

										Builder.node("label",{htmlFor: "photocopaPaletteTags"},"Tags"),
										Builder.node("input",{type: "text",id: "photocopaPaletteTags",className: "textInput",value: this._paletteMeta._tags}),
										Builder.node("div",{className: "clear"})
									])
								])
							]));

							// Do these here, Builder wont insert the value for these
							$("photocopaPaletteDesc").value = this._paletteMeta._desc;
							$("photocopaPaletteInspirationDesc").value = this._paletteMeta._inspirationDesc;

							this._defaults["photocopaPaletteInspirationLink"] = $("photocopaPaletteInspirationLink").title;
							if ($("photocopaPaletteInspirationLink").value === "") {
								 $("photocopaPaletteInspirationLink").value = this._defaults["photocopaPaletteInspirationLink"];
							}
							Event.observe("photocopaPaletteInspirationLink","focus",function(_event) {
								$("photocopaPaletteInspirationLink").select();
							}.bind(this));
							Event.observe("photocopaPaletteInspirationLink","blur",function(_event) {
								if ($("photocopaPaletteInspirationLink").value.strip() === "") {
									$("photocopaPaletteInspirationLink").value = this._defaults["photocopaPaletteInspirationLink"];
								}
							}.bind(this));

							this._defaults["photocopaPaletteUseLink"] = $("photocopaPaletteUseLink").title;
							if ($("photocopaPaletteUseLink").value === "") {
								 $("photocopaPaletteUseLink").value = this._defaults["photocopaPaletteUseLink"];
							}
							Event.observe("photocopaPaletteUseLink","focus",function(_event) {
								$("photocopaPaletteUseLink").select();
							}.bind(this));
							Event.observe("photocopaPaletteUseLink","blur",function(_event) {
								if ($("photocopaPaletteUseLink").value.strip() === "") {
									$("photocopaPaletteUseLink").value = this._defaults["photocopaPaletteUseLink"];
								}
							}.bind(this));

							// Hexs
							if (_json._hexs.length !== 0) {
								$("publishPhotocopaFormDivLeftPanel").appendChild(Builder.node("br"));
								$("publishPhotocopaFormDivLeftPanel").appendChild(Builder.node("h2","Name These Colors"));
								$("publishPhotocopaFormDivLeftPanel").appendChild(Builder.node("div",{className: "clear"}));

								for (var _i=0;_i<_json._hexs.length;_i++) {
									$("publishPhotocopaFormDivLeftPanel").appendChild(Builder.node("label",{htmlFor: "photocopaPaletteColor_" + _i.toString(),style: "background-color: #" + _json._hexs[_i] + ";"}," "));
									$("publishPhotocopaFormDivLeftPanel").appendChild(Builder.node("input",{type: "text",id: "photocopaPaletteColor_" + _i.toString(),className: "textInput",maxlength: 20,title: _json._hexs[_i]}));
									$("publishPhotocopaFormDivLeftPanel").appendChild(Builder.node("div",{className: "clear"}));
								}
							}

							// Groups
							if (_json._groups.length !== 0) {
								$("publishPhotocopaForm").appendChild(Builder.node("div",{id: "publishPhotocopaFormDivRightPanel",className: "p pwb right"}));

								$("publishPhotocopaFormDivRightPanel").appendChild(Builder.node("h2","Add to Group[s]"));
								$("publishPhotocopaFormDivRightPanel").appendChild(Builder.node("div",{className: "clear"}));
								$("publishPhotocopaFormDivRightPanel").appendChild(Builder.node("div",{id: "publishPhotocopaFormGroupContainer"}));
								$("publishPhotocopaFormDivRightPanel").appendChild(Builder.node("input",{type: "hidden",id: "numGroups",value: _json._groups.length}));

								for (var _i=0;_i<_json._groups.length;_i++) {
									$("publishPhotocopaFormGroupContainer").appendChild(Builder.node("input",{type: "checkbox",id: "photocopaPaletteGroup_" + _i.toString(),name: "groupIDs",className: "inputCheckbox",value: _json._groups[_i]._groupID}));
									$("publishPhotocopaFormGroupContainer").appendChild(Builder.node("label",{htmlFor: "photocopaPaletteGroup_" + _i.toString(),className: "groupTitle"},_json._groups[_i]._groupTitle));
									$("publishPhotocopaFormGroupContainer").appendChild(Builder.node("div",{className: "clear"}));

									if ((_json._groups[_i]._groupSelected === true) || (this._paletteMeta._groups.indexOf(_json._groups[_i]._groupID) !== -1)) {
										$("photocopaPaletteGroup_" + _i.toString()).checked = true;
									}
								}

								var _leftPanelDimensions = $("publishPhotocopaFormDivLeftPanel").getDimensions();
								var _rightPanelDimensions = $("publishPhotocopaFormDivRightPanel").getDimensions();
								if (_leftPanelDimensions.height > _rightPanelDimensions.height) {
									$("publishPhotocopaFormDivRightPanel").setStyle({height: (_leftPanelDimensions.height.toString() + "px")});
								}
							}

							$("publishPhotocopaForm").appendChild(Builder.node("div",{className: "clear"}));
							$("publishPhotocopaForm").appendChild(Builder.node("input",{type: "submit",className: "inputSubmit"}));
							$("publishPhotocopaForm").appendChild(Builder.node("input",{type: "button",className: "inputButtonCancel cancelBtn"}));

							$$(".cancelBtn").each(function(_element) {
								Event.observe(_element,"click",function(_event) {
									this.hideOverlay();
								}.bind(this));
							}.bind(this));

							Event.observe("publishPhotocopaForm","submit",function(_event) {
								var _photocopaPaletteTitle = $("photocopaPaletteTitle").value.strip().stripLowerASCII();
								var _photocopaPaletteDesc = $("photocopaPaletteDesc").value.strip().stripLowerASCII();
								var _photocopaPaletteInspirationLink = $("photocopaPaletteInspirationLink").value.strip().stripLowerASCII();
								var _photocopaPaletteInspirationDesc = $("photocopaPaletteInspirationDesc").value.strip().stripLowerASCII();
								var _photocopaPaletteUseLink = $("photocopaPaletteUseLink").value.strip().stripLowerASCII();
								var _photocopaPaletteTags = $("photocopaPaletteTags").value.strip().stripLowerASCII();
								var _colorHexTitlesArray = $H();

								_photocopaPaletteInspirationLink = (_photocopaPaletteInspirationLink === "http://") ? "" : _photocopaPaletteInspirationLink;
								_photocopaPaletteUseLink = (_photocopaPaletteUseLink === "http://") ? "" : _photocopaPaletteUseLink;

								// Title
								if (_photocopaPaletteTitle === "") {
									alert("Please Name This Palette!");
									$("photocopaPaletteTitle").activate();
									return false;
								}

								// Color Titles
								for (var _i=0;_i<this._numColorSquares;_i++) {
									var _colorHexTitle = $("photocopaPaletteColor_" + _i.toString());
									if (_colorHexTitle !== null) {
										var _colorHexTitleValue = _colorHexTitle.value.strip().stripLowerASCII();
										_colorHexTitlesArray.set(_colorHexTitle.title,_colorHexTitleValue);

										if (_colorHexTitleValue === "") {
											alert("Please Name This Color!");
											$("photocopaPaletteColor_" + _i.toString()).activate();
											return false;
										}
									}
								}

								// Groups
								var _groupIDs = $("publishPhotocopaForm").getInputs("checkbox","groupIDs"), _groupIDsArray = [];
								if (Object.isArray(_groupIDs) === true) {
									for (var _i=0;_i<_groupIDs.length;_i++) {
										if (_groupIDs[_i].checked === true) {
											_groupIDsArray.push(parseInt(_groupIDs[_i].value,10));
										}
									}
								}

								// Inspiration Link
								if (_photocopaPaletteInspirationLink !== "") {
									if (this._validURLRegEx.test(_photocopaPaletteInspirationLink) === false) {
										alert("Please enter a Valid URL!");
										$("photocopaPaletteInspirationLink").activate();
										return false;
									}
								}

								// Use Link
								if (_photocopaPaletteUseLink !== "") {
									if (this._validURLRegEx.test(_photocopaPaletteUseLink) === false) {
										alert("Please enter a Valid URL!");
										$("photocopaPaletteUseLink").activate();
										return false;
									}
								}

								this._paletteMeta = {
									_title: _photocopaPaletteTitle,
									_desc: _photocopaPaletteDesc,
									_inspirationLink: _photocopaPaletteInspirationLink,
									_inspirationDesc: _photocopaPaletteInspirationDesc,
									_useLink: _photocopaPaletteUseLink,
									_tags: _photocopaPaletteTags,
									_groups: _groupIDsArray
								};

								var _args = {
									_overwrite: false,
									_action: "publish",
									_photocopaPaletteTitle: _photocopaPaletteTitle,
									_photocopaPaletteDesc: _photocopaPaletteDesc,
									_photocopaPaletteInspirationLink: _photocopaPaletteInspirationLink,
									_photocopaPaletteInspirationDesc: _photocopaPaletteInspirationDesc,
									_photocopaPaletteUseLink: _photocopaPaletteUseLink,
									_photocopaPaletteTags: _photocopaPaletteTags,
									_colorHexTitles: _colorHexTitlesArray,
									_groupIDs: _groupIDsArray
								};

								if (this._currentPaletteID === 0) {
									this.publishSavePhotocopa(_args);
								} else {
									$("clOverlayContent").update();
									$("clOverlayContent").appendChild(Builder.node("div",{className: "publishPhotocopaFormDiv"},[
										Builder.node("form",{id: "publishPhotocopaForm",action: "#",method: "post",onsubmit: "return false;"},[
											Builder.node("h2","Publish Palette"),
											Builder.node("div",{className: "xBtn cancelBtn"}),
											Builder.node("div",{className: "clear"}),
											Builder.node("br"),
											Builder.node("div","Would you like to overwrite the existing Palette or save a new one?"),
											Builder.node("br"),

											Builder.node("div",{onclick: "return false;",id: "photocopaPublishOverwrite"}),
											Builder.node("div",{onclick: "return false;",id: "photocopaPublishNew"})
										])
									]));

									Event.observe("photocopaPublishOverwrite","click",function(_event) {
										this._args._overwrite = true;
										this._this.publishSavePhotocopa(this._args);
									}.bind({_this: this,_args: _args}));

									Event.observe("photocopaPublishNew","click",function(_event) {
										this._this.publishSavePhotocopa(this._args);
									}.bind({_this: this,_args: _args}));

									$$(".cancelBtn").each(function(_element) {
										Event.observe(_element,"click",function(_event) {
											this.hideOverlay();
										}.bind(this));
									}.bind(this));

									this.showOverlay();
								}
							}.bind(this));

							$("photocopaPaletteTitle").activate();
							this.showOverlay();
						}
					}.bind(this)
				});
			}.bind(this));


			////////////// SAVE //////////////
			Event.observe("saveBtn","click",function(_event) {
				if (this._isLoggedIn === false) {
					alert("Please log in to use this feature");
					return;
				}

				if (this._currentPaletteID !== 0) {
					// You can only save unpublished palettes
					return;
				}

				$("clOverlayContent").update();
				$("clOverlayContent").appendChild(Builder.node("div",{className: "savePhotocopaFormDiv"},[
					Builder.node("form",{id: "savePhotocopaForm",action: "#",method: "post",onsubmit: "return false;"},[
						Builder.node("div",{id: "savePhotocopaFormDivLeftPanel",className: "p"},[
							Builder.node("h2","Save Photocopa"),
							Builder.node("div",{className: "clear"}),

							Builder.node("label",{htmlFor: "photocopaPaletteTitle"},"Palette Name"),
							Builder.node("input",{type: "text",id: "photocopaPaletteTitle",className: "textInput",maxlength: 20,value: this._paletteMeta._title}),
							Builder.node("div",{className: "clear"})
						])
					])
				]));

				$("savePhotocopaForm").appendChild(Builder.node("div",{className: "clear"}));
				$("savePhotocopaForm").appendChild(Builder.node("input",{type: "submit",className: "inputSubmit"}));
				$("savePhotocopaForm").appendChild(Builder.node("input",{type: "button",className: "inputButtonCancel cancelBtn"}));

				$$(".cancelBtn").each(function(_element) {
					Event.observe(_element,"click",function(_event) {
						this.hideOverlay();
					}.bind(this));
				}.bind(this));

				Event.observe("savePhotocopaForm","submit",function(_event) {
					var _photocopaPaletteTitle = $("photocopaPaletteTitle").value.strip().stripLowerASCII();

					// Title
					if (_photocopaPaletteTitle === "") {
						alert("Please Name This Palette!");
						$("photocopaPaletteTitle").activate();
						return false;
					}

					this._paletteMeta._title = _photocopaPaletteTitle;

					var _args = {
						_overwrite: false,
						_action: "save",
						_currentPaletteID: this._currentPaletteID,
						_currentPhotocopaID: this._currentPhotocopaID,
						_currentPhotocopaImageID: this._currentPhotocopaImageID,
						_photocopaPaletteTitle: _photocopaPaletteTitle,
						_currentSelectedColorSquareIndex: this._currentSelectedColorSquareIndex,
						_scratchBoxIDHexs: this._scratchBoxIDHexs
					};

					if (this._currentPhotocopaID === 0) {
						this.publishSavePhotocopa(_args);
					} else {
						$("clOverlayContent").update();
						$("clOverlayContent").appendChild(Builder.node("div",{className: "savePhotocopaFormDiv"},[
							Builder.node("form",{id: "savePhotocopaForm",action: "#",method: "post",onsubmit: "return false;"},[
								Builder.node("h2","Save Palette"),
								Builder.node("div",{className: "xBtn cancelBtn"}),
								Builder.node("div",{className: "clear"}),
								Builder.node("br"),
								Builder.node("div","Would you like to overwrite the existing Project or save a new one?"),
								Builder.node("br"),

								Builder.node("div",{onclick: "return false;",id: "photocopaSaveOverwrite"}),
								Builder.node("div",{onclick: "return false;",id: "photocopaSaveNew"})
							])
						]));

						Event.observe("photocopaSaveOverwrite","click",function(_event) {
							this._args._overwrite = true;
							this._this.publishSavePhotocopa(this._args);
						}.bind({_this: this,_args: _args}));

						Event.observe("photocopaSaveNew","click",function(_event) {
							this._this.publishSavePhotocopa(this._args);
						}.bind({_this: this,_args: _args}));

						$$(".cancelBtn").each(function(_element) {
							Event.observe(_element,"click",function(_event) {
								this.hideOverlay();
							}.bind(this));
						}.bind(this));

						this.showOverlay();
					}
				}.bind(this));

				$("photocopaPaletteTitle").activate();
				this.showOverlay();
			}.bind(this));


			////////////// OPEN //////////////
			Event.observe("openBtn","click",function(_event) {
				if (this._isLoggedIn === false) {
					alert("Please log in to use this feature");
					return;
				}

				new Ajax.Request("/ajax/photocopaOpen",{
					sanitizeJSON: true,
					onSuccess: function(_transport) {
						this._openPhotocopaResult = _transport.responseJSON;

						if (this._openPhotocopaResult._errMsg !== undefined) {
							alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\n" + this._openPhotocopaResult._errMsg);
						} else {
							if ((this._openPhotocopaResult._projects === undefined) || (Object.isArray(this._openPhotocopaResult._projects) === false)) {
								alert("It looks like you don't have any Projects to open!");
								return false;
							}

							var _numProjects = this._openPhotocopaResult._projects.length, _elements = [];
							for (var _i=0;_i<_numProjects;_i++) {
								if (this._openPhotocopaResult._projects[_i]._version === "1.0") {
									var _thumbnail = "", _palette = "", _info = "", _dateInfo = [];
									var _currentPhotocopaImageID = this._openPhotocopaResult._projects[_i]._data._currentPhotocopaImageID;
									var _colorSquaresInOrder = this._openPhotocopaResult._projects[_i]._data._colorSquaresInOrder;
									var _photocopaPaletteTitle = this._openPhotocopaResult._projects[_i]._data._photocopaPaletteTitle;
									var _colorWidthSliderValuesPalette = this._openPhotocopaResult._projects[_i]._data._colorWidthSliderValuesPalette;
									var _colorWidthSliderValues = this._openPhotocopaResult._projects[_i]._data._colorWidthSliderValues;
									var _photocopaDate = this._openPhotocopaResult._projects[_i]._data._photocopaDate;
									var _photocopaDateModified = this._openPhotocopaResult._projects[_i]._data._photocopaDateModified;
									var _numColorSquares = _colorSquaresInOrder.without("").length;

									if (_currentPhotocopaImageID !== 0) {
										_thumbnail = Builder.node("div",{className: "thumbnail oPClickable",id: "oPT_" + _i.toString(),style: "background: url(" + getStaticURL("/" + this.getNumericallyShardedDirectory("images/photocopa/%s/overview.jpg",_currentPhotocopaImageID)) + ") no-repeat 50% 50%;"});
									}

									if (_numColorSquares !== 0) {
										var _paletteColors = [], _paletteColorWidths = [];

										for (var _j=0;_j<_numColorSquares;_j++) {
											_paletteColorWidths.push(Math.round(168 * _colorWidthSliderValuesPalette[_j]));
										}
										_paletteColorWidths[_numColorSquares - 1] += Math.round(168 - _paletteColorWidths.sum());
										for (var _j=0;_j<_numColorSquares;_j++) {
											_paletteColors.push(Builder.node("div",{className: "color oPClickable",id: "_" + _j.toString()+ "_oPP_" + _i.toString(),style: "width: " + _paletteColorWidths[_j].toString() + "px; background-color: #" + _colorSquaresInOrder[_j] + ";"}));
										}

										_palette = Builder.node("div",{className: "paletteContainer"},_paletteColors);
									}

									if (_photocopaDate === _photocopaDateModified) {
										_dateInfo.push(Builder.node("p","Created: " + _photocopaDate));
									} else {
										_dateInfo.push(Builder.node("p","Created: " + _photocopaDate));
										_dateInfo.push(Builder.node("p","Modified: " + _photocopaDateModified));
									}

									_info = Builder.node("div",{className: "info"},[
										Builder.node("span",{id: "oPI_" + _i.toString(),className: "oPClickable"},_photocopaPaletteTitle),
										_dateInfo
									]);

									_elements.push(Builder.node("div",{className: "project"},[_thumbnail,_palette,_info,Builder.node("div",{className: "clear"})]));
								}
							}

							$("clOverlayContent").update();
							$("clOverlayContent").appendChild(Builder.node("div",{className: "openPhotocopaDiv"},[
								Builder.node("h2","Open an existing project"),
								Builder.node("div",{className: "xBtn cancelBtn"}),
								Builder.node("div",{className: "clear"}),
								_elements
							]));

							$$(".oPClickable").each(function(_element) {
								Event.observe(_element,"click",function(_event) {
									var _index = this.getNumericIDFromElementID(Event.element(_event).id);
									if (confirm("Are you sure you want to load this Project?\n\nClick OK to continue loading\nClick Cancel to stay on your current Project") === true) {
										this.openPhotocopa(_index);
									}
								}.bind(this));
							}.bind(this));

							$$(".cancelBtn").each(function(_element) {
								Event.observe(_element,"click",function(_event) {
									this.hideOverlay();
								}.bind(this));
							}.bind(this));

							this.showOverlay();
						}
					}.bind(this)
				});
			}.bind(this));


			////////////// HELP //////////////
			Event.observe("helpBtn","click",function(_event) {
				alert("Coming Soon");
			}.bind(this));


			// Tabs
			["photos","colors","gallery"].each(function (_elementID) {
				Event.observe(_elementID + "Tabs","click",function(_event) {
					this._this._currentRightColTab = this._elementID;
					this._this.updateCurrentRightColTab();
					this._this.updateRightColTabDisplay();
				}.bind({_this: this,_elementID: _elementID}));
			}.bind(this));
			this.updateCurrentRightColTab();
			this.updateRightColTabDisplay();

			// Colors tab
			for (var _i=0;_i<48;_i++) {
				$("photoColors_sampledColors").appendChild(Builder.node("div",{id: ("photoColors_sampledColors_" + _i.toString())}));

				new Draggable(("photoColors_sampledColors_" + _i.toString()),{revert: true,ghosting: true});

				// These next three event observers ensure that the "mouseup" event only gets fired when: [mousedown + mouseup] ... not when: [mousedown + mousemove + mouseup]
				Event.observe(("photoColors_sampledColors_" + _i.toString()),"mousedown",function(_event) {
					this._eventDelineation["lastMouseDown_" + Event.element(_event).id] = (new Date()).getTime();
				}.bind(this));
				Event.observe(("photoColors_sampledColors_" + _i.toString()),"mousemove",function(_event) {
					this._eventDelineation["lastMouseMove_" + Event.element(_event).id] = (new Date()).getTime();
				}.bind(this));
				Event.observe(("photoColors_sampledColors_" + _i.toString()),"mouseup",function(_event) {
					if (this._eventDelineation["lastMouseDown_" + Event.element(_event).id] > this._eventDelineation["lastMouseMove_" + Event.element(_event).id]) {
						var _hex = this._currentPhotoColorSet["sampledColors"][this.getNumericIDFromElementID(Event.element(_event).id)];
						_hex = (_hex === undefined) ? "171717" : _hex;

						this.updateCurrentSelectedColor(_hex);
						this.updateColorSquareSelectedIndicator();
					}
				}.bind(this));
			}

			$("photoColors_sampledColors").appendChild(Builder.node("span",{className: "block clear noDim"}));
			$("photoColors_sampledColors").appendChild(Builder.node("span"));
			["bright","muted","analogous","chance","dark","light"].each(function(_elementID) {
				$("photoColors_" + _elementID).appendChild(Builder.node("span"));
				for (var _i=0;_i<5;_i++) {
					$("photoColors_" + _elementID).appendChild(Builder.node("div",{id: ("photoColors_" + _elementID + "_" + _i.toString())}));

					new Draggable(("photoColors_" + _elementID + "_" + _i.toString()),{revert: true,ghosting: true});

					// These next three event observers ensure that the "mouseup" event only gets fired when: [mousedown + mouseup] ... not when: [mousedown + mousemove + mouseup]
					Event.observe(("photoColors_" + _elementID + "_" + _i.toString()),"mousedown",function(_event) {
						this._eventDelineation["lastMouseDown_" + Event.element(_event).id] = (new Date()).getTime();
					}.bind(this));
					Event.observe(("photoColors_" + _elementID + "_" + _i.toString()),"mousemove",function(_event) {
						this._eventDelineation["lastMouseMove_" + Event.element(_event).id] = (new Date()).getTime();
					}.bind(this));
					Event.observe(("photoColors_" + _elementID + "_" + _i.toString()),"mouseup",function(_event) {
						if (this._this._eventDelineation["lastMouseDown_" + Event.element(_event).id] > this._this._eventDelineation["lastMouseMove_" + Event.element(_event).id]) {
							var _hex = this._this._currentPhotoColorSet[_elementID][this._this.getNumericIDFromElementID(Event.element(_event).id)];
							_hex = (_hex === undefined) ? "171717" : _hex;

							this._this.updateCurrentSelectedColor(_hex);
							this._this.updateColorSquareSelectedIndicator();
						}
					}.bind({_this: this,_elementID: _elementID}));
				}
				$("photoColors_" + _elementID).appendChild(Builder.node("span",{className: "block clear noDim"}));
			}.bind(this));

			// Photos tab
			this._defaults["photoURL"] = $("photoURL").title;
			if ($("photoURL").value === "") {
				 $("photoURL").value = this._defaults["photoURL"];
			}
			Event.observe("photoURL","focus",function(_event) {
				$("photoURL").select();
			}.bind(this));
			Event.observe("photoURL","blur",function(_event) {
				if ($("photoURL").value.strip() === "") {
					$("photoURL").value = this._defaults["photoURL"];
				}
			}.bind(this));

			Event.observe("photosTabForm_photoURL","submit",function(_event) {
				var _url = $("photoURL").value;

				if (this._isLoggedIn === false) {
					alert("Please log in to use this feature");
					return;
				}

				if (this._validURLRegEx.test(_url) === false) {
					alert("Please enter a valid URL");
					$("photoURL").select();
					return;
				}

				if (confirm("Are you sure you want to import this photo into PHOTOCOPA?\n\nDoing so will cause you to lose your current project!") === true) {
					$("photoURL").blur();
					$("photoURL").value = "http://";

					this.addPhotocopaImage({_url: _url});
				}
			}.bind(this));

			Event.observe("photosTabForm_flickrQuery","submit",function(_event) {
				$("flickrQuery").blur();
				var _flickrQuery = $("flickrQuery").value.strip().stripLowerASCII();

				if (this._isLoggedIn === false) {
					alert("Please log in to use this feature");
					return;
				}

				if (_flickrQuery === "") {
					alert("Please enter something to search for");
					$("flickrQuery").select();
					return;
				}

				$$("#flickrQueryResultDiv div.thumbnail").each(function(_element) {
					_element.fade({duration: 0.2});
				}.bind(this));

				var _paging = $$("#flickrQueryResultDiv div.pagingContainer")[0];
				if (_paging !== undefined) {
					_paging.fade({duration: 0.2});
					(function() {
						$("flickrQueryResultDiv").update();
						$("flickrQueryResultDiv").appendChild(Builder.node("div",{className: "flickrQueryResultLoading"}));
					}).delay(0.2);
				} else {
					$("flickrQueryResultDiv").update();
					$("flickrQueryResultDiv").appendChild(Builder.node("div",{className: "flickrQueryResultLoading"}));
				}

				new Ajax.Request("/ajax/photocopaFlickrAPIQuery",{
					sanitizeJSON: true,
					method: "post",
					parameters: "flickrQuery=" + encodeURIComponent(_flickrQuery),
					onSuccess: function(_transport) {
						this._flickrQueryResult = _transport.responseJSON;

						if (this._flickrQueryResult._errMsg !== undefined) {
							$("flickrQueryResultDiv").update(this._flickrQueryResult._errMsg);
						} else {
							this.updateFlickrAPIQueryResults();
						}
					}.bind(this)
				});
			}.bind(this));

			if (this._isLoggedIn === false) {
				$("photoURL").disable();
				$("flickrQuery").disable();
			}

			// Gallery Tab
			this.galleryQuery({_page: 1});


			// Save this user's session from the gc
			new PeriodicalExecuter(function() {
				new Ajax.Request("/ajax/echo");
			},300);

			this._domIsLoaded = true;

			this.updateExtractedPhotoColorsDelegator();

			// Photocopa is done building!
		}.bind(this));
	},

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

	updateScratchBoxes: function() {
		for (var _i=0;_i<11;_i++) {
			var _hex = (this._scratchBoxIDHexs[_i] !== undefined) ? this._scratchBoxIDHexs[_i] : "171717";
			$("scratchBox_" + _i.toString()).setStyle({
				backgroundColor: "#" + _hex,
				borderColor: "#" + _hex
			});
		}
	},

	updateSaveBtn: function() {
		$("saveBtn").className = "";
		if (this._currentPaletteID !== 0) {
			$("saveBtn").className = "saved";
		}
	},

	openPhotocopa: function(_openPhotocopaResultIndex) {
		_openPhotocopaResultIndex = parseInt(_openPhotocopaResultIndex,10);

		if (this._openPhotocopaResult._projects[_openPhotocopaResultIndex] !== undefined) {
			var _data = this._openPhotocopaResult._projects[_openPhotocopaResultIndex]._data;

			// Photocopa
			this._currentPhotocopaID = parseInt(this._openPhotocopaResult._projects[_openPhotocopaResultIndex]._id,10);
			this._currentPaletteID = parseInt(_data._currentPaletteID,10);
			this._currentPhotocopaImageID = parseInt(_data._currentPhotocopaImageID,10);
			this._currentSelectedColorSquareIndex = parseInt(_data._currentSelectedColorSquareIndex,10);

			// Palette Meta
			this._paletteMeta._title = _data._photocopaPaletteTitle;
			this._paletteMeta._desc = _data._photocopaPaletteDesc;
			this._paletteMeta._inspirationLink = _data._photocopaPaletteInspirationLink;
			this._paletteMeta._inspirationDesc = _data._photocopaPaletteInspirationDesc;
			this._paletteMeta._useLink = _data._photocopaPaletteUseLink;
			this._paletteMeta._tags = _data._photocopaPaletteTags;
			this._paletteMeta._groups = _data._groupIDs;

			// Scratch
			this._scratchBoxIDHexs = _data._scratchBoxIDHexs;
			this.updateScratchBoxes();

			// Color Squares / Widths
			var _colorWidthSliderValues = this.sliderValues2absoluteFromPercentage(_data._colorWidthSliderValues.collect(function(_value) {
				return parseFloat(_value);
			}));
			for (var _i=0;_i<(this._numColorSquares - 1);_i++) {
				this._colorWidthSlider.setValue(_colorWidthSliderValues[_i],_i);
			}

			this._colorSquareIDHexs = _data._colorSquaresInOrder;
			this.updateColorSquareWidths();
			this.updateColorSquareBackgrounds();

			this.updateSaveBtn();
			this.loadPhoto(_data._photocopaImageArgs);
			this.hideOverlay();
		}
	},

	galleryQuery: function(_args) {
		_args = (_args === undefined) ? {} : _args;
		_args._page = (_args._page === undefined) ? 1 : _args._page;

		var _this = (_args._this !== undefined) ? _args._this : this;
		var _query = {_page: _args._page};

		new Ajax.Request("/ajax/photocopaGalleryQuery",{
			sanitizeJSON: true,
			method: "post",
			parameters: "args=" + encodeURIComponent(Object.toJSON(_query)),
			onSuccess: function(_transport) {
				var _galleryQueryResult = _transport.responseJSON;

				if (_galleryQueryResult._errMsg !== undefined) {
					$("galleryQueryResultDiv").update(_galleryQueryResult._errMsg);
				} else {
					this.updateGalleryQueryResults(_galleryQueryResult);
				}
			}.bind(_this)
		});
	},

	updateGalleryQueryResults: function(_args) {
		if ((_args === undefined) || (_args._page === undefined) || (_args._photos === undefined) || (_args._length === undefined)) {
			alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\nPhotocopa Bad Gallery Response");
			return false;
		}
		_args._page = (_args._page === undefined) ? 1 : _args._page;

		var _page = parseInt(_args._page,10);
		_page = ((isNaN(_page) === true) || (_page < 1)) ? 1 : _page;

		if (Object.isArray(_args._photos) === true) {
			var _resultsPerPage = 15, _id = "", _length = _args._photos.length, _elements;

			$("galleryQueryResultDiv").update();
			for (var _i=0;_i<_length;_i++) {
				_id = ("galleryQueryResultThumbnail_" + _i.toString());

				if ((_args._photos[_i]._a !== undefined) && (_args._photos[_i]._a === true)) {
					// Adult Content
					_elements = [
						Builder.node("div",{id: "acDiv_hg_" + _id.toString(),style: "background: transparent url(" + getStaticURL("/images/v3.5/_/stripedOverlayBG.png") + ") repeat scroll 0 0; position: absolute; height: 75px; width: 75px;"},[
							Builder.node("div",{style: "border: 1px solid #aaaaaa; margin: 4px auto 0 auto; padding: 5px 2px 2px 2px; width: 61px; height: 57px; background-color: #434343; text-align: center;"},[
								Builder.node("div",{style: "margin-bottom: 5px; font-size: 9px; width: 60px; height: 30px; text-align: center; color: #aaaaaa;"},"This Photo is Adult Content"),
								Builder.node("a",{href: "#",style: "color: #dddddd; font-size: 9px;",onclick: "Effect.Fade('acDiv_hg_" + _id.toString() + "',{duration: 0.5}); return false;"},"Hide this"),
							])
						]),
						Builder.node("div",{id: _id,style: "background: url(" + getStaticURL(_args._photos[_i]._t) + ") no-repeat 50% 50%;"})
					];
				} else {
					_elements = [Builder.node("div",{id: _id,style: "background: url(" + getStaticURL(_args._photos[_i]._t) + ") no-repeat 50% 50%;"})];
				}

				$("galleryQueryResultDiv").appendChild(Builder.node("div",{className: "thumbnail"},_elements));

				Event.observe(_id,"click",function(_event) {
					if (confirm("Are you sure you want to import this photo into PHOTOCOPA?\n\nDoing so will cause you to lose your current project!") === true) {
						this._this.addPhotocopaImage(this._args._photos[this._this.getNumericIDFromElementID(Event.element(_event).id)],false);
					}
				}.bind({_this: this,_args: _args}));
			}
			$("galleryQueryResultDiv").appendChild(Builder.node("div",{className: "clear"}));

			var _paging = this.getPaging(_args._length,_resultsPerPage,_page,this.galleryQuery);
			$("galleryQueryResultDiv").appendChild(_paging._elements);
			_paging._assignPagingEvents();
		}
	},

	getBestFitZoomLevel: function() {
		var _buffer = 75;
		for (var _i=this._maxZoomLevel;_i>=0;_i--) {
			if ((this._ui._photoDimmensions[_i]._width <= (this._ui._photoContainerDimmensions.width + _buffer)) && (this._ui._photoDimmensions[_i]._height <= (this._ui._photoContainerDimmensions.height + _buffer))) {
				return _i;
			}
		}
		return 0;
	},

	publishSavePhotocopa: function(_args) {
		if ((_args._action === "save") && (this._currentPaletteID !== 0)) {
			return false;
		}

		var _data = {
			_action: _args._action,
			_currentPaletteID: this._currentPaletteID,
			_currentPhotocopaID: this._currentPhotocopaID,
			_currentPhotocopaImageID: this._currentPhotocopaImageID,
			_overwrite: _args._overwrite,
			_photocopaPaletteTitle: _args._photocopaPaletteTitle,
			_photocopaPaletteDesc: _args._photocopaPaletteDesc,
			_colorHexTitles: _args._colorHexTitles,
			_groupIDs: _args._groupIDs,
			_photocopaPaletteInspirationLink: _args._photocopaPaletteInspirationLink,
			_photocopaPaletteInspirationDesc: _args._photocopaPaletteInspirationDesc,
			_photocopaPaletteUseLink: _args._photocopaPaletteUseLink,
			_photocopaPaletteTags: _args._photocopaPaletteTags,
			_colorWidthSliderValuesPalette: this.sliderValues2percentage(),
			_colorWidthSliderValues: this.sliderValues2percentage(true),
			_colorSquaresInOrder: this.getColorSquaresInOrder(),
			_currentSelectedColorSquareIndex: this._currentSelectedColorSquareIndex,
			_scratchBoxIDHexs: this._scratchBoxIDHexs
		};

		$("clOverlayContent").update();
		$("clOverlayContent").appendChild(Builder.node("div",{className: "overlayContent loadingBG"},((_data._action === "publish") ? "Publishing ..." : "Saving ...")));
		this.showOverlay();

		new Ajax.Request("/ajax/photocopaPublishSave",{
			sanitizeJSON: true,
			method: "post",
			parameters: "data=" + encodeURIComponent(Object.toJSON(_data)),
			onSuccess: function(_transport) {
				var _json = _transport.responseJSON;

				if (_json._errMsg !== undefined) {
					alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\n" + _json._errMsg);
				} else {
					this._currentPaletteID = parseInt(_json._currentPaletteID,10);
					this._currentPhotocopaID = parseInt(_json._currentPhotocopaID,10);

					$("toolTipContent").update();
					if (_json._published === true) {
						var _offset = $("publishBtn").cumulativeOffset();
						var _dimensions = $("publishBtn").getDimensions();

						$("toolTipContent").appendChild(Builder.node("h2","Your Project has been published!"));

						$("toolTipContent").appendChild(Builder.node("strong","Palette: "));
						$("toolTipContent").appendChild(Builder.node("a",{href: "/palette/" + this._currentPaletteID.toString() + "/" + _json._titleURL},this._paletteMeta._title));
						$("toolTipContent").appendChild(Builder.node("br"));

						$("toolTipContent").appendChild(Builder.node("strong","Photocopa: "));
						$("toolTipContent").appendChild(Builder.node("a",{href: "/photocopa/" + this._currentPhotocopaImageID.toString() + "," + this._currentPhotocopaID.toString() + "/" + _json._photocopaImageTitleURL},_json._photocopaImageTitle));
						$("toolTipContent").appendChild(Builder.node("br"));
						$("toolTipContent").appendChild(Builder.node("br"));
					} else if (_json._saved === true) {
						var _offset = $("saveBtn").cumulativeOffset();
						var _dimensions = $("saveBtn").getDimensions();

						$("toolTipContent").appendChild(Builder.node("h2","Your Project has been saved!"));
					}

					$("toolTipContent").appendChild(Builder.node("a",{href: "#",className: "outline closeBtn",onclick: "return false;"},"Close"));

					$$(".closeBtn").each(function(_element) {
						Event.observe(_element,"click",function(_event) {
							this.hideTooltip();
						}.bind(this));
					}.bind(this));

					this.updateSaveBtn();
					this.positionShowTooltip({
						_top: (_offset.top + _dimensions.height),
						_left: (_offset.left + Math.round(_dimensions.width / 2))
					});
				}

				this.hideOverlay();
			}.bind(this)
		});
		return true;
	},

	fadeTooltip: function() {
		if ($("toolTipDiv").style.display !== "none") {
			$("toolTipDiv").fade({duration: 0.5});
		}
	},

	hideTooltip: function() {
		if ($("toolTipDiv").style.display !== "none") {
			$("toolTipDiv").hide();
		}
	},

	positionShowTooltip: function(_coordinates,_toolTipTableClassName) {
		$("toolTipTable").className = (_toolTipTableClassName === undefined) ? "vt" : _toolTipTableClassName;

		$("toolTipDiv").setStyle({
			top: (_coordinates._top.toString() + "px"),
			left: ((_coordinates._left - ($("toolTipDiv").getDimensions().width / 2)).toString() + "px")
		});

		$("toolTipDiv").appear({duration: 0.5});
		this._timeouts._toolTipHide = this.fadeTooltip.delay(5);
	},

	areColorSquaresSequential: function() {
		var _foundBlankIndex = false, _colorSquareIDHexsOrdered = this.getColorSquaresInOrder();

		for (var _i=0;_i<this._numColorSquares;_i++) {
			if ((_foundBlankIndex === true) && (_colorSquareIDHexsOrdered[_i] !== "")) {
				return false;
			}
			if (_colorSquareIDHexsOrdered[_i] === "") {
				_foundBlankIndex = true;
			}
		}
		return true;
	},

	getColorSquaresInOrder: function() {
		var _colorSquareIDHexsOrdered = [];
		for (var _i=0;_i<this._numColorSquares;_i++) {
			_colorSquareIDHexsOrdered[_i] = this._colorSquareIDHexs[this._colorSquareOrder[_i]];
		}
		return _colorSquareIDHexsOrdered;
	},

	loadPhoto: function(_args) {
		if (_args !== undefined) {
			this._currentPhotocopaImageID = parseInt(_args._currentPhotocopaImageID,10);
			this._currentPhotocopaImageTitle = _args._currentPhotocopaImageTitle;
			this._currentPhotocopaImageAuthor = _args._currentPhotocopaImageAuthor;
			this._currentPhotoURIPrefix = getStaticURL("/" + this.getNumericallyShardedDirectory("images/photocopa/%s/",this._currentPhotocopaImageID));
			this._currentPhotocopaImageDimmensions = _args._currentPhotocopaImageDimmensions;
			this._currentPhotocopaImageOverviewDimmensions = _args._currentPhotocopaImageOverviewDimmensions;
		}

		// Avoid a bug where The current image is at a zoom which is bigger than a the greatest zoom level of the incomming image. Since we don't know the imcomming image's greatest zoom level, assume it is 0.
		this.showPhotoAtZoomLevel(0);

		this.initPhotoTiles();
		this.initOverview();
		this.initZoomInterface();

		this.showPhotoAtZoomLevel(this.getBestFitZoomLevel());

		// Load colors tab
		this._currentRightColTab = "colors";
		this.updateCurrentRightColTab();
		this.updateRightColTabDisplay();
	},

	getPaging: function(_numRecords,_numRecordsPerPage,_page,_callback) {
		var _numPages = Math.ceil(_numRecords / _numRecordsPerPage), _elements = [];
		var _inflation = 6, _numLinks = 0, _start = (_page - (_inflation / 2)), _stop = (_start + _inflation);
		var _prevPage = (_page - 1), _nextPage = (_page + 1), _lastPageOffset = (_numPages > _inflation) ? (_numPages - (_inflation / 2)) : ((_inflation / 2) - _numPages);
		_prevPage = (_prevPage < 1) ? 1 : _prevPage;
		_nextPage = (_nextPage > _numPages) ? _numPages : _nextPage;
		_lastPageOffset = (_lastPageOffset < 0) ? 0 : _lastPageOffset;

		if (_start < 1) {
			_start = 1;
			_stop = (_numPages < _inflation) ? _numPages : _inflation;
		} else if (_stop > _numPages) {
			_stop = _numPages;
			_start = (_stop - (_inflation - 1));
			if (_start < 1) {
				_start = 1;
			}
		}

		// Previous page
		_elements.push(Builder.node("a",{title: _prevPage,id: ("pagingElement_" + _numLinks.toString()),href: "#",onclick: "return false;"},"<"));
		_numLinks++;

		// First page
		if (_page > ((_inflation / 2) + 1)) {
			_elements.push(Builder.node("a",{title: 1,id: ("pagingElement_" + _numLinks.toString()),href: "#",onclick: "return false;"},"1"));
			_elements.push(Builder.node("span","..."));
			_numLinks++;
		}

		// Middle pages
		for (var _i=_start;_i<=_stop;_i++) {
			if (_page === _i) {
				_elements.push(Builder.node("a",{title: _i,id: ("pagingElement_" + _numLinks.toString()),href: "#",onclick: "return false;",className: "on"},_i.toString()));
			} else {
				_elements.push(Builder.node("a",{title: _i,id: ("pagingElement_" + _numLinks.toString()),href: "#",onclick: "return false;"},_i.toString()));
			}
			_numLinks++;
		}

		// Last page
		if ((_page < _lastPageOffset) && (_numPages >= _inflation)) {
			_elements.push(Builder.node("span","..."));
			_elements.push(Builder.node("a",{title: _numPages,id: ("pagingElement_" + _numLinks.toString()),href: "#",onclick: "return false;"},_numPages.toString()));
			_numLinks++;
		}

		// Next page
		_elements.push(Builder.node("a",{title: _nextPage,id: ("pagingElement_" + _numLinks.toString()),href: "#",onclick: "return false;"},">"));

		return {_elements: Builder.node("div",{className: "pagingContainer"},_elements),_assignPagingEvents: function() {
			for (var _i=0;_i<=this._numLinks;_i++) {
				Event.observe(("pagingElement_" + _i.toString()),"click",function(_event) {
					this._callback({
						_page: parseInt(Event.element(_event).title,10),
						_this: this._this
					});
				}.bind(this));
			}
		}.bind({_this: this,_callback: _callback,_numLinks: _numLinks})}; // Say that 3 times fast ;-)
	},

	updateFlickrAPIQueryResults: function(_args) {
		_args = (_args === undefined) ? {} : _args;
		_args._page = (_args._page === undefined) ? 1 : _args._page;

		var _this = (_args._this !== undefined) ? _args._this : this;
		var _page = parseInt(_args._page,10);
		_page = ((isNaN(_page) === true) || (_page < 1)) ? 1 : _page;

		$("flickrQueryResultDiv").update();

		if (Object.isArray(_this._flickrQueryResult._photos) === true) {
			var _length = _this._flickrQueryResult._photos.length;
			var _resultsPerPage = 12, _id = "";
			var _start = ((_page - 1) * _resultsPerPage);
			var _stop = (_resultsPerPage + _start);
			_stop = (_stop > _length) ? _length : _stop;

			$("flickrQueryResultDiv").appendChild(Builder.node("div",{className: "summary"},[
				Builder.node("strong",_length.toString())," photos found for \"" + _this._flickrQueryResult.flickrQuery + "\""
			]));

			for (var _i=_start;_i<_stop;_i++) {
				_id = ("flickrQueryResultThumbnail_" + _i.toString());

				$("flickrQueryResultDiv").appendChild(Builder.node("div",{className: "thumbnail"},[
					Builder.node("div",{
						id: _id,
						style: "background: url(" + _this._flickrQueryResult._photos[_i]._t + ") no-repeat;"
					})
				]));

				Event.observe(_id,"click",function(_event) {
					if (confirm("Are you sure you want to import this photo into PHOTOCOPA?\n\nDoing so will cause you to lose your current project!\n\n(It may take up to several minutes.)") === true) {
						this.addPhotocopaImage(this._flickrQueryResult._photos[this.getNumericIDFromElementID(Event.element(_event).id)]);
					}
				}.bind(_this));

				Event.observe(_id,"mouseout",function(_event) {
					clearTimeout(this._timeouts._photosToolTipShow[this.getNumericIDFromElementID(Event.element(_event).id)]);
					this.hideTooltip();
				}.bind(_this));

				Event.observe(_id,"mousemove",function(_event) {
					var _id = this.getNumericIDFromElementID(Event.element(_event).id);

					clearTimeout(this._timeouts._photosToolTipShow[_id]);
					clearTimeout(this._timeouts._toolTipHide);

					this._timeouts._photosToolTipShow[_id] = (function() {
						$("toolTipContent").update();
						$("toolTipContent").appendChild(Builder.node("h2",this._this._flickrQueryResult._photos[_i]._i));
						$("toolTipContent").appendChild(Builder.node("span","by "));
						$("toolTipContent").appendChild(Builder.node("strong",this._this._flickrQueryResult._photos[_i]._f));

						var _element = Event.element(this._event);
						var _offset = _element.cumulativeOffset();
						var _dimensions = _element.getDimensions();
						var _toolTipDimensions = $("toolTipDiv").getDimensions();
						var _id = this._this.getNumericIDFromElementID(_element.id);

						this._this.positionShowTooltip({
							_top: (_offset.top + Math.round(_dimensions.height / 2) - (_toolTipDimensions.height / 2)),
							_left: (_offset.left - Math.round(_toolTipDimensions.width / 2) - 0)
						},"hr");
					}.bind({_this: this,_event: _event})).delay(0.3);
				}.bind(_this));
			}
			$("flickrQueryResultDiv").appendChild(Builder.node("div",{className: "clear"}));

			var _paging = _this.getPaging(_length,_resultsPerPage,_page,_this.updateFlickrAPIQueryResults);
			$("flickrQueryResultDiv").appendChild(_paging._elements);
			_paging._assignPagingEvents();
		} else {
			$("flickrQueryResultDiv").appendChild(Builder.node("div",{className: "summary"},[
				Builder.node("strong","0")," photos found for \"" + _this._flickrQueryResult.flickrQuery + "\""
			]));
		}
	},

	showOverlay: function() {
		// Put #clOverlayContent at 0,0 to prevent it's content from being squished against the right side of the view port, therefore misconstruing the getDimensions() measurements below
		$("clOverlayContent").setStyle({left: 0,top: 0});

		var _documentWindowDimmensions = this.getDocumentWindowDimmensions();
		var _clOverlayContentDimmensions = $("clOverlayContent").getDimensions();

		$("clOverlay").setStyle({height: (_documentWindowDimmensions[1] + "px")});
		$("clOverlayContent").setStyle({
			left: ((_documentWindowDimmensions[2] / 2) - (_clOverlayContentDimmensions.width / 2)).toString() + "px",
			top: ((_documentWindowDimmensions[3] / 2) - (_clOverlayContentDimmensions.height / 2) + document.viewport.getScrollOffsets().top).toString() + "px"
		});

		$("clOverlay").appear({duration: 0.5});
		$("clOverlayContent").appear({duration: 0.5});
	},

	hideOverlay: function(_args) {
		$("clOverlay").fade({duration: 0.5});
		$("clOverlayContent").fade({duration: 0.5});
	},

	getDocumentWindowDimmensions: function(_args) {
		// From lightbox :-)
		var _xScroll = _yScroll = _windowWidth = _windowHeight = 0;

		if (window.innerHeight && window.scrollMaxY) {
			_xScroll = document.body.scrollWidth;
			_yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight){
			_xScroll = document.body.scrollWidth;
			_yScroll = document.body.scrollHeight;
		} else {
			_xScroll = document.body.offsetWidth;
			_yScroll = document.body.offsetHeight;
		}

		if (self.innerHeight) {
			_windowWidth = self.innerWidth;
			_windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) {
			_windowWidth = document.documentElement.clientWidth;
			_windowHeight = document.documentElement.clientHeight;
		} else if (document.body) {
			_windowWidth = document.body.clientWidth;
			_windowHeight = document.body.clientHeight;
		}

		return [((_xScroll < _windowWidth) ? _windowWidth : _xScroll),((_yScroll < _windowHeight) ? _windowHeight : _yScroll),_windowWidth,_windowHeight];
	},

	addPhotocopaImage: function(_args,_showLoadingOverlay) {
		_showLoadingOverlay = (_showLoadingOverlay === undefined) ? true : _showLoadingOverlay;
		this._processAddPhotocopaImageSuccess = true; // Avoid adding an image if it was cancelled by cancelBtn.click()

		if (_showLoadingOverlay === true) {
			$("clOverlayContent").update();
			$("clOverlayContent").appendChild(Builder.node("div",{className: "overlayContent loadingBG"},"Loading Photo ..."));
			$("clOverlayContent").appendChild(Builder.node("a",{href: "#",className: "cancelLink cancelBtn",onclick: "return false;"},"Cancel Import"));

			$$(".cancelBtn").each(function(_element) {
				Event.observe(_element,"click",function(_event) {
					this._processAddPhotocopaImageSuccess = false;
					this.hideOverlay();
				}.bind(this));
			}.bind(this));
			this.showOverlay();
		}

		new Ajax.Request("/ajax/photocopaAddImage",{
			sanitizeJSON: true,
			method: "post",
			parameters: "args=" + encodeURIComponent(Object.toJSON(_args)),
			onSuccess: function(_transport) {
				clearTimeout(this._timeouts._addPhotocopaImage);

				if (this._processAddPhotocopaImageSuccess === true) {
					var _json = _transport.responseJSON;
					if (_json._errMsg !== undefined) {
						alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\n" + _json._errMsg);
					} else {
						// Force the current state into a new project
						this._currentPhotocopaID = 0;
						this._currentPaletteID = 0;
						this._paletteMeta = {_title: "",_desc: "",_inspirationLink: "",_inspirationDesc: "",_useLink: "",_tags: "",_groups: []};
						this._currentlySelectedHex = false;
						this._currentSelectedColorSquareIndex = 0;
						this._colorSquareIDHexs = ["","","","",""];
						this._scratchBoxIDHexs = [];
						this.updateColorSquareBackgrounds();
						this.updateScratchBoxes();
						this.updateSaveBtn();
						this.resetColorSquareWidths();

						this.loadPhoto(_transport.responseJSON);
					}
					this.hideOverlay();
				}
			}.bind(this)
		});

		this._timeouts._addPhotocopaImage = (function() {
			$("clOverlayContent").update();
			$("clOverlayContent").appendChild(Builder.node("div",{className: "overlayContent"},"Oops, looks like this is taking too long to import."));
			$("clOverlayContent").appendChild(Builder.node("a",{href: "#",className: "cancelLink cancelBtn",onclick: "return false;"},"Click here to cancel"));

			$$(".cancelBtn").each(function(_element) {
				Event.observe(_element,"click",function(_event) {
					this._processAddPhotocopaImageSuccess = false;
					this.hideOverlay();
				}.bind(this));
			}.bind(this));
			this.showOverlay();

			this.hideOverlay.delay(10);
		}.bind(this)).delay(60);
	},

	updateExtractedPhotoColors: function() {
		new Ajax.Request("/ajax/photocopaGetColorsFromPhoto",{
			sanitizeJSON: true,
			method: "post",
			parameters: "pID=" + this._this._currentPhotocopaImageID.toString() + "&c=" + encodeURIComponent(this._coordinatesJSON.toString()) + "&z=" + this._this._currentPhotoZoomLevel.toString(),
			onSuccess: function(_transport) {
				var _json = _transport.responseJSON;
				if (_json === undefined) {
					return;
				}

				if (_json._errMsg !== undefined) {
					alert("We're sorry but there was an error. Please contact us with the following message enclosed:\n\n" + _json._errMsg);
				} else {
					this._currentPhotoColorSet = _transport.responseJSON;
					var _hex = "";

					for (var _i=0;_i<48;_i++) {
						_hex = ((this._currentPhotoColorSet["sampledColors"] !== undefined) && (this._currentPhotoColorSet["sampledColors"][_i] !== undefined)) ? this._currentPhotoColorSet["sampledColors"][_i] : "171717";
						$("photoColors_sampledColors_" + _i.toString()).style.backgroundColor = ("#" + _hex);
						this._photoColorsHexByElementID["photoColors_sampledColors_" + _i.toString()] = _hex;
					}

					["bright","muted","analogous","chance","dark","light"].each(function(_elementID) {
						for (var _i=0;_i<5;_i++) {
							_hex = ((this._currentPhotoColorSet[_elementID] !== undefined) && (this._currentPhotoColorSet[_elementID][_i] !== undefined)) ? this._currentPhotoColorSet[_elementID][_i] : "171717";
							$("photoColors_" + _elementID + "_" + _i.toString()).style.backgroundColor = ("#" + _hex);
							this._photoColorsHexByElementID["photoColors_" + _elementID + "_" + _i.toString()] = _hex;
						}
					}.bind(this));
				}
			}.bind(this._this)
		});
	},

	updateExtractedPhotoColorsDelegator: function() {
		if (this._domIsLoaded === true) {
			this._ui._photoCumulativeOffset = $("photo").cumulativeOffset();
			this._ui._photoContainerCumulativeOffset = $("photoContainer").cumulativeOffset();

			var _photoX = (this._ui._photoCumulativeOffset.left - this._ui._photoContainerCumulativeOffset.left) * -1;
			var _photoY = (this._ui._photoCumulativeOffset.top - this._ui._photoContainerCumulativeOffset.top) * -1;
			var _coordinatesJSON = Object.toJSON({
				_tlX: _photoX,
				_tlY: _photoY,
				_brX: (this._ui._photoContainerDimmensions.width + _photoX),
				_brY: (this._ui._photoContainerDimmensions.height + _photoY)
			});

			if ((this.previousPhotoCoordinatesJSON === undefined) || (this.previousPhotoCoordinatesJSON !== _coordinatesJSON)) {
				clearTimeout(this._timeouts._updateExtractedPhotoColors);
				this._timeouts._updateExtractedPhotoColors = (this.updateExtractedPhotoColors.bind({_this: this,_coordinatesJSON: _coordinatesJSON})).delay(0.55);
			}
			this.previousPhotoCoordinatesJSON = _coordinatesJSON;
		}
	},

	showFullscreenPalettePreview: function() {
		var _url = "/paletteImgDetail/" + getPageSize()[0] + "/" + getPageSize()[1];
		var _hexs = this.getColorSquaresInOrder().without(""), _length = _hexs.length;
		for (_i=0;_i<_length;_i++) {
			_url += ("/" + _hexs[_i]);
		}

		showLightbox({
			href: (_url + "/Copaso_preview.png?cPW=" + this.sliderValues2percentage().toString().base64_encode()),
			getAttribute: function() {
				return false;
			}
		});
	},

	resetColorSquareWidths: function() {
		var _values = this.sliderValues2absoluteFromPercentage([0.2,0.2,0.2,0.2,0.2]);

		for (var _i=0;_i<(this._numColorSquares - 1);_i++) {
			this._colorWidthSlider.setValue(_values[_i],_i);
		}
		this.updateColorSquareWidths();
		this.updateColorSquareSelectedIndicator();
	},

	rmColorFromColorSquare: function(_colorSquareID) {
		_colorSquareID = parseInt(_colorSquareID,10);

		if (this._colorSquareIDHexs[_colorSquareID] !== "") {
			$("colorSquareRMBTN_" + _colorSquareID.toString()).hide();
			this._colorSquareIDHexs[_colorSquareID] = "";
			this.updateColorSquareBackgrounds();
		}
	},

	updateCurrentRightColTab: function() {
		["photos","colors","gallery"].each(function (_elementID) {
			$(_elementID + "TabContent").hide();
		}.bind(this));

		$(this._currentRightColTab + "TabContent").show();
	},

	updateRightColTabDisplay: function() {
		var _backgroundPosition = "0 -31px";

		switch (this._currentRightColTab) {
			case "photos": _backgroundPosition = "0 -31px"; break;
			case "colors": _backgroundPosition = "0 -57px"; break;
			case "gallery": _backgroundPosition = "0 -83px"; break;
		}

		$$("div.photocopaTabsContent")[0].style.backgroundPosition = _backgroundPosition;
	},

	getNumericIDFromElementID: function(_elementID) {
		// Expects something like: someID_123
		return parseInt(/^[^\s]+_([0-9]+)$/.exec(_elementID)[1],10);
	},

	updateCurrentSelectedColor: function(_hex) {
		if (this.isValidHex(_hex) === true) {
			this._currentlySelectedHex = _hex;
			this._colorSquareIDHexs[this._currentSelectedColorSquareIndex] = _hex; // Not ordered, array keyed by DOM id
			this.updateColorSquareBackgrounds();
		}
	},

	updateColorSquareBackgrounds: function() {
		var _length = this._colorSquareIDHexs.length;

		for (var _i=0;_i<_length;_i++) {
			var _iStr = _i.toString();
			if (this._colorSquareIDHexs[_i] === "") {
				$("colorSquare_" + _iStr).style.background = "url(" + getStaticURL("/images/v3.5/_/photocopa/colorNotSetBG.png") + ") center 0 no-repeat";
			} else {
				$("colorSquare_" + _iStr).style.background = "";
				$("colorSquare_" + _iStr).style.backgroundColor = ("#" + this._colorSquareIDHexs[_i]);
			}
		}
		this.updateColorSquareSelectedIndicator();
	},

	updateColorSquareOrder: function() {
		this._colorSquareOrder = Sortable.sequence("colorSquareContainer").collect(function(_value) {
			return parseInt(_value,10);
		});
	},

	updateColorWidthSliders: function() {
		// there is a little precision loss pulling the widths from the colorSquares
		var _values = [], _valuesOrdered = [];

		for (var _i=0;_i<this._numColorSquares;_i++) {
			_values.push($("colorSquare_" + _i.toString()).getWidth());
		}
		for (var _i=0;_i<this._numColorSquares;_i++) {
			_valuesOrdered[_i] = _values[this._colorSquareOrder[_i]];
		}
		_valuesOrdered = this.sliderValues2absolute(_valuesOrdered);

		for (var _i=0;_i<(this._numColorSquares - 1);_i++) {
			this._colorWidthSlider.setValue(_valuesOrdered[_i],_i);
		}
	},

	updateColorSquareWidths: function() {
		var _colorSquareWidths = this.sliderValues2relative(this._colorWidthSlider.values);

		for (var _i=0;_i<this._numColorSquares;_i++) {
			$("colorSquare_" + _i.toString()).style.width = ((_colorSquareWidths[this.getColorSquareOrder(_i)]).toString() + "px");
		}
	},

	updateColorSquareSelectedIndicator: function() {
		var _marginLeft = 0, _id = 0, _hex = "";

		this.getColorSquareOrder().each(function(_id) {
			if (_id === this._currentSelectedColorSquareIndex) {
				throw $break;
			}
			_marginLeft += $("colorSquare_" + _id.toString()).getWidth();
		}.bind(this));

		_hex = ((this._colorSquareIDHexs[this._currentSelectedColorSquareIndex] === "") ? "c10000" : this._colorSquareIDHexs[this._currentSelectedColorSquareIndex]);
		$("colorSquareSelectedIndicatorDiv").setStyle({
			backgroundColor: ("#" + _hex),
			width: $("colorSquare_" + this._currentSelectedColorSquareIndex.toString()).style.width,
			marginLeft: (_marginLeft + "px")
		});
	},

	getCurrentSelectedColorSquareOrder: function() {
		return this._colorSquareOrder.indexOf(this._currentSelectedColorSquareIndex);
	},

	getColorSquareOrder: function(_index) {
		if (_index === undefined) {
			return this._colorSquareOrder;
		} else {
			return this._colorSquareOrder.indexOf(_index);
		}
	},

	sliderValues2relative: function(_values,_trackLength) {
		_trackLength = (_trackLength === undefined) ? this._ui._colorSquareContainerWidth : _trackLength;

		// ex: _values = [25,50,75,100]
		var _relativeValues = [];
		for (var _i=0;_i<this._numColorSquares;_i++) {
			if (_i === (this._numColorSquares - 1)) {
				_relativeValues[_i] = (_trackLength - _values[_i - 1]);
			} else {
				_relativeValues[_i] = ((_i === 0) ? _values[_i] : (_values[_i] - _values[_i - 1]));
			}
		}
		return _relativeValues;
	},

	sliderValues2absolute: function(_values) {
		// ex: _values = [25,25,25,25,25]
		var _absoluteValues = [];
		for (var _i=0;_i<(this._numColorSquares - 1);_i++) {
			_absoluteValues[_i] = ((_i === 0) ? _values[_i] : (_values[_i] + _absoluteValues[_i - 1]));
		}
		return _absoluteValues;
	},

	sliderValues2percentage: function(_returnAllValues) {
		// bias this towards how many hexs are actually set to something, so filter out "" values and .length [if false]
		_returnAllValues = (_returnAllValues === undefined) ? false : _returnAllValues;

		var _sliderValuesRelative = this.sliderValues2relative(this._colorWidthSlider.values);
		var _sliderValuesAbsolute = this._colorWidthSlider.values, _percentages = [];
		var _numSetColorSquares = ((_returnAllValues === true) ? this._colorSquareIDHexs : this._colorSquareIDHexs.without("")).length;
		_sliderValuesAbsolute.push(this._ui._colorSquareContainerWidth);

		for (var _i=0;_i<_numSetColorSquares;_i++) {
			_percentages.push((_sliderValuesRelative[_i] / _sliderValuesAbsolute[_numSetColorSquares - 1]).toFixed(2));
		}
		return _percentages;
	},

	sliderValues2absoluteFromPercentage: function(_values) {
		var _sliderValue = [];
		for (var _i=0;_i<(this._numColorSquares - 1);_i++) {
			_sliderValue[_i] = ((_i === 0) ? (_values[_i] * this._ui._colorSquareContainerWidth) : ((_values[_i] * this._ui._colorSquareContainerWidth) + _sliderValue[_i - 1]));
		}
		return _sliderValue;
	},

	showPhotoHoverPreview: function() {
		var _photoCumulativeOffset = $("photo").cumulativeOffset();
		var _x = (Event.pointerX(this._event) - _photoCumulativeOffset.left);
		var _y = (Event.pointerY(this._event) - _photoCumulativeOffset.top);

		if ((this._this.isWithinRange(_x,0,this._this._ui._photoDimmensions[this._this._currentPhotoZoomLevel]._width) === true) && (this._this.isWithinRange(_y,0,this._this._ui._photoDimmensions[this._this._currentPhotoZoomLevel]._height) === true)) {
			new Ajax.Request("/ajax/photocopaGetHexAtPixel?pID=" + this._this._currentPhotocopaImageID.toString() + "&c=" + _x + "," + _y + "&z=" + this._this._currentPhotoZoomLevel.toString(),{
				onSuccess: function(_transport) {
				if (this._this.isValidHex(_transport.responseText) === true) {
						$("photoHoverPreview").style.backgroundColor = ("#" + _transport.responseText);
						$("photoHoverPreview").style.left = ((Event.pointerX(this._event) - 30) + "px");
						$("photoHoverPreview").style.top = ((Event.pointerY(this._event) - 30) + "px");
						$("photoHoverPreview").show();
					}
				}.bind({_this: this._this,_event: this._event})
			});
		}
	},

	getPhotoOverviewWindowContraints: function(_x,_y) {
		if (_x < 0) {
			_x = 0;
		} else if (_x > (this._ui._photoOverviewImageDimmensions._width - this._ui._photoOverviewWindowDimmensions._width)) {
			_x = (this._ui._photoOverviewImageDimmensions._width - this._ui._photoOverviewWindowDimmensions._width);
		}
		if (_y < 0) {
			_y = 0;
		} else if (_y > (this._ui._photoOverviewImageDimmensions._height - this._ui._photoOverviewWindowDimmensions._height)) {
			_y = (this._ui._photoOverviewImageDimmensions._height - this._ui._photoOverviewWindowDimmensions._height);
		}

		return [_x,_y];
	},

	getPhotoContainerContraints: function(_x,_y,_zoomLevel) {
		_zoomLevel = (_zoomLevel !== undefined) ? _zoomLevel : this._currentPhotoZoomLevel;

		if (_x < this._photoDragBounds[_zoomLevel]._topLeft._x) {
			_x = this._photoDragBounds[_zoomLevel]._topLeft._x;
		} else if (_x > this._photoDragBounds[_zoomLevel]._bottomRight._x) {
			_x = this._photoDragBounds[_zoomLevel]._bottomRight._x;
		}
		if (_y < this._photoDragBounds[_zoomLevel]._topLeft._y) {
			_y = this._photoDragBounds[_zoomLevel]._topLeft._y;
		} else if (_y > this._photoDragBounds[_zoomLevel]._bottomRight._y) {
			_y = this._photoDragBounds[_zoomLevel]._bottomRight._y;
		}

		return [_x,_y];
	},

	initZoomInterface: function() {
		this._photoZoomSlider = new Control.Slider("photoZoomScrollbar","photoZoomTrack",{
			axis: "vertical",
			range: $R(0,this._maxZoomLevel),
			minimum: 0,
			maximum: this._maxZoomLevel,
			increment: (this._ui._photoZoomTrackHeight / this._maxZoomLevel),
			onChange: function(_value) {
				if ((isNaN(_value) === true) || (_value < 0) || (_value > this._maxZoomLevel)) {
					return;
				}
				var _roundedValue = Math.round(_value);
				if ((this._domIsLoaded === true) && (_roundedValue !== _value)) {
					_value = _roundedValue;
					this._photoZoomSlider.setValue(_value);
					this.showPhotoAtZoomLevel(Math.abs(_value - this._maxZoomLevel)); // Invert _value
				}
			}.bind(this)
		});

		$("photoZoomTicks").update();
		for (var _i=0;_i<=this._maxZoomLevel;_i++) {
			$("photoZoomTicks").appendChild(Builder.node("div",{style: "height: " + (Math.round(this._ui._photoZoomTrackHeight / this._maxZoomLevel) - 1).toString() + "px;"})); // -1 for border-bottom
		}
	},

	updateZoomInterface: function() {
		this._photoZoomSlider.setValue(Math.abs(this._currentPhotoZoomLevel - this._maxZoomLevel)); // Invert this._currentPhotoZoomLevel
	},

	photoOverviewWindowOnDrag: function() {
		var _photoOverviewWindowCumulativeOffset = $("photoOverviewWindow").cumulativeOffset();

		var _left = Math.round((this._ui._photoOverviewCumulativeOffset.left - _photoOverviewWindowCumulativeOffset.left) / (this._ui._photoOverviewImageDimmensions._width / this._ui._photoDimmensions[this._currentPhotoZoomLevel]._width));
		var _top = Math.round((this._ui._photoOverviewCumulativeOffset.top - _photoOverviewWindowCumulativeOffset.top) / (this._ui._photoOverviewImageDimmensions._height / this._ui._photoDimmensions[this._currentPhotoZoomLevel]._height));

		$("photo").style.top = (_top.toString() + "px");
		$("photo").style.left = (_left.toString() + "px");
	},

	updatePhotoOverviewInterface: function(_updateDimmensions,_x,_y,_top,_left) {
		_updateDimmensions = (_updateDimmensions === undefined) ? false : _updateDimmensions;

		if ((_top === undefined) && (_left === undefined)) {
			if (this.isPartOfPhotoHidden()) {
				this._ui._photoCumulativeOffset = $("photo").cumulativeOffset();

				if (_updateDimmensions === true) {
					this._ui._photoOverviewWindowDimmensions = {
						_width: Math.ceil(this._ui._photoOverviewImageDimmensions._width / (this._ui._photoDimmensions[this._currentPhotoZoomLevel]._width / this._ui._photoContainerDimmensions.width)),
						_height: Math.ceil(this._ui._photoOverviewImageDimmensions._height / (this._ui._photoDimmensions[this._currentPhotoZoomLevel]._height / this._ui._photoContainerDimmensions.height))
					};
				}

				var _left = Math.round((this._ui._photoContainerCumulativeOffset.left - this._ui._photoCumulativeOffset.left) / (this._ui._photoDimmensions[this._currentPhotoZoomLevel]._width / this._ui._photoOverviewImageDimmensions._width));
				var _top = Math.round((this._ui._photoContainerCumulativeOffset.top - this._ui._photoCumulativeOffset.top) / (this._ui._photoDimmensions[this._currentPhotoZoomLevel]._height / this._ui._photoOverviewImageDimmensions._height));
			} else {
				var _top = 0, _left = 0;
				if (_updateDimmensions === true) {
					this._ui._photoOverviewWindowDimmensions = {
						_width: this._ui._photoOverviewImageDimmensions._width,
						_height: this._ui._photoOverviewImageDimmensions._height
					};
				}
			}
		}

		$("photoOverviewWindow").style.top = (_top.toString() + "px");
		$("photoOverviewWindow").style.left = (_left.toString() + "px");
		$("photoOverviewWindow").style.width = (this._ui._photoOverviewWindowDimmensions._width.toString() + "px");
		$("photoOverviewWindow").style.height = (this._ui._photoOverviewWindowDimmensions._height.toString() + "px");

		if ((_x === undefined) && (_y === undefined)) {
			var _photoOverviewWindowCumulativeOffset = $("photoOverviewWindow").cumulativeOffset();
			_x = (_photoOverviewWindowCumulativeOffset[0] - this._ui._photoOverviewCumulativeOffset.left) * -1;
			_y = (_photoOverviewWindowCumulativeOffset[1] - this._ui._photoOverviewCumulativeOffset.top) * -1;
		}
		$("photoOverviewWindow").style.backgroundPosition = (_x + "px " + _y + "px");
	},

	isPartOfPhotoHidden: function() {
		return ((this._ui._photoDimmensions[this._currentPhotoZoomLevel]._width > this._ui._photoContainerDimmensions.width) || (this._ui._photoDimmensions[this._currentPhotoZoomLevel]._height > this._ui._photoContainerDimmensions.height));
	},

	centerPhoto: function() {
		$("photo").style.left = (((this._ui._photoContainerDimmensions.width / 2) - this._ui._photoDimmensions[this._currentPhotoZoomLevel]._width / 2).toString() + "px");
		$("photo").style.top = (((this._ui._photoContainerDimmensions.height / 2) - this._ui._photoDimmensions[this._currentPhotoZoomLevel]._height / 2).toString() + "px");

		this.loadPhotoTilesInView();
	},

	loadPhotoTilesInView: function() {
		this._ui._photoCumulativeOffset = $("photo").cumulativeOffset();
		this._ui._photoContainerCumulativeOffset = $("photoContainer").cumulativeOffset();

		var _photoX = (this._ui._photoCumulativeOffset.left - this._ui._photoContainerCumulativeOffset.left) * -1;
		var _photoY = (this._ui._photoCumulativeOffset.top - this._ui._photoContainerCumulativeOffset.top) * -1;
		var _topLeft = {_x: Math.floor(_photoX / 256),_y: Math.floor(_photoY / 256)};
		var _bottomRight = {_x: Math.ceil((this._ui._photoContainerDimmensions.width + _photoX) / 256),_y: Math.ceil((this._ui._photoContainerDimmensions.height + _photoY) / 256)};

		for (var _y=_topLeft._y;_y<_bottomRight._y;_y++) {
			for (var _x=_topLeft._x;_x<_bottomRight._x;_x++) {
				if ((this._tileImages[this._currentPhotoZoomLevel][_x] !== undefined) && (this._tileImages[this._currentPhotoZoomLevel][_x][_y] !== undefined)) {
					$("pt_" + this._currentPhotoZoomLevel.toString() + "_" + _x.toString() + "_" + _y.toString()).style.backgroundImage = ("url(" + this._tileImages[this._currentPhotoZoomLevel][_x][_y] + ")");
				}
			}
		}
	},

	getImageResizeDimensions: function(_width,_height,_constraintWidth,_constraintHeight) {
		_constraintWidth = (_constraintWidth === undefined) ? _width : _constraintWidth;
		_constraintHeight = (_constraintHeight === undefined) ? _height : _constraintHeight;

		if ((_width > _constraintWidth) || (_height > _constraintHeight)) {
			while ((_constraintWidth < _width) || (_constraintHeight < _height)) {
				if (_constraintWidth < _width) {
					_height = Math.floor(((_constraintWidth * _height) / _width));
					_width = _constraintWidth;
				}
				if (_constraintHeight < _height) {
					_width = Math.floor(((_constraintHeight * _width) / _height));
					_height = _constraintHeight;
				}
			}
		}
		return {_width: _width,_height: _height};
	},

	initOverview: function() {
		var _photoWidth = this._currentPhotocopaImageDimmensions._width;
		var _photoHeight = this._currentPhotocopaImageDimmensions._height;
		var _overviewWidth = this._currentPhotocopaImageOverviewDimmensions._width;
		var _overviewHeight = this._currentPhotocopaImageOverviewDimmensions._height;

		this._ui._photoOverviewImageDimmensions = this.getImageResizeDimensions(_photoWidth,_photoHeight,_overviewWidth,_overviewHeight);

		$("photoOverview").style.width = $("photoOverviewOverlay").style.width = (this._ui._photoOverviewImageDimmensions._width.toString() + "px");
		$("photoOverview").style.height = $("photoOverviewOverlay").style.height = (this._ui._photoOverviewImageDimmensions._height.toString() + "px");
		$("photoContainer").style.marginTop = (((this._ui._photoOverviewImageDimmensions._height + 2) * -1).toString() + "px");
		$("photoOverview").style.background = $("photoOverviewWindow").style.background = ("url(" + this._currentPhotoURIPrefix + "overview.jpg) no-repeat");
		this.setOpacity("photoOverviewOverlay",0.6);

		$("photoOverview").show();

		this._ui._photoOverviewCumulativeOffset = $("photoOverview").cumulativeOffset();
		this.updatePhotoOverviewInterface();
	},

	initPhotoTiles: function() {
		// Find max zoom levels && the image matrices && find photo drag bounds:
		var _photoWidth = this._currentPhotocopaImageDimmensions._width;
		var _photoHeight = this._currentPhotocopaImageDimmensions._height;
		this._maxZoomLevel = -1;
		this._tileImageMatrices = [];
		this._photoDragBounds = [];
		this._ui._photoDimmensions = [];

		// Both of these need to be something bigger than 1 in order for the while loop to be entered, fixes a bug which occurs when one or both sides is exactly 256
		var _numXTiles = 2;
		var _numYTiles = 2;

		while ((_numXTiles > 1) && (_numYTiles > 1)) {
			this._maxZoomLevel++;
			_numXTiles = Math.ceil(_photoWidth / 256);
			_numYTiles = Math.ceil(_photoHeight / 256);

			this._tileImageMatrices[this._maxZoomLevel] = {_x: _numXTiles,_y: _numYTiles};

			var _topLeftX = _bottomRightX = _topLeftY = _bottomRightY = 0;
			if (_photoWidth < this._ui._photoContainerDimmensions.width) {
				_topLeftX = 0;
				_bottomRightX = (this._ui._photoContainerDimmensions.width - _photoWidth);
			} else {
				_topLeftX = ((_photoWidth - this._ui._photoContainerDimmensions.width) * -1);
				_bottomRightX = 0;
			}

			if (_photoHeight < this._ui._photoContainerDimmensions.height) {
				_topLeftY = 0;
				_bottomRightY = (this._ui._photoContainerDimmensions.height - _photoHeight);
			} else {
				_topLeftY = ((_photoHeight - this._ui._photoContainerDimmensions.height) * -1);
				_bottomRightY = 0;
			}

			this._photoDragBounds[this._maxZoomLevel] = {
				_topLeft: {_x: _topLeftX,_y: _topLeftY},
				_bottomRight: {_x: _bottomRightX,_y: _bottomRightY}
			};
			this._ui._photoDimmensions[this._maxZoomLevel] = {_width: _photoWidth,_height: _photoHeight};

			_photoWidth /= 2;
			_photoHeight /= 2;
		}
		this._tileImageMatrices = this._tileImageMatrices.reverse();
		this._photoDragBounds = this._photoDragBounds.reverse();
		this._ui._photoDimmensions = this._ui._photoDimmensions.reverse();

		// Set up tile images:
		this._tileImages = [];
		for (var _i=0;_i<=this._maxZoomLevel;_i++) {
			this._tileImages[_i] = [];

			for (var _x=0;_x<this._tileImageMatrices[_i]._x;_x++) {
				this._tileImages[_i][_x] = [];
				for (var _y=0;_y<this._tileImageMatrices[_i]._y;_y++) {
					this._tileImages[_i][_x][_y] = (this._currentPhotoURIPrefix + _i.toString() + "/" + _x.toString() + "_" + _y.toString() + ".jpg");
				}
			}
		}
	},

	showPhotoAtZoomLevel: function(_zoomLevel,_mouseCoordinates) {
		if (this._currentPhotoZoomLevel !== -1) {
			if (this.isWithinRange(_zoomLevel,0,this._maxZoomLevel) === true) {
				var _zoomDirection = (_zoomLevel < this._currentPhotoZoomLevel) ? "out" : "in";
				$("photo").style.width = ((this._tileImageMatrices[_zoomLevel]._x * 256).toString() + "px");
				$("photo").style.height = ((this._tileImageMatrices[_zoomLevel]._y * 256).toString() + "px");

				// After _zoomDirection has been determined...
				this._currentPhotoZoomLevel = -1;

				if (_mouseCoordinates !== undefined) {
					// These vars SHOULD be the same regardless of scroll
					this._ui._photoCumulativeOffset = $("photo").cumulativeOffset();
					this._ui._photoContainerCumulativeOffset = $("photoContainer").cumulativeOffset();

					// a = this._ui._photoContainerCumulativeOffset
					// b = this._ui._photoCumulativeOffset
					// c = _mouseCoordinates
					if (_zoomDirection === "out") {
						// ((c + b) / 2) - a = _position 

						var _x = (((_mouseCoordinates._x + this._ui._photoCumulativeOffset.left) / 2) - (this._ui._photoContainerCumulativeOffset.left));
						var _y = (((_mouseCoordinates._y + this._ui._photoCumulativeOffset.top) / 2) - (this._ui._photoContainerCumulativeOffset.top));
					} else if (_zoomDirection === "in") {
						// (2b - c - a) = _position 

						var _x = ((this._ui._photoCumulativeOffset.left * 2) - _mouseCoordinates._x - this._ui._photoContainerCumulativeOffset.left);
						var _y = ((this._ui._photoCumulativeOffset.top * 2) - _mouseCoordinates._y - this._ui._photoContainerCumulativeOffset.top);
					}

					if (this._constrainPhoto === true) {
						var _tmp = this.getPhotoContainerContraints(_x,_y,_zoomLevel);

						_x = _tmp[0];
						_y = _tmp[1];

						$("photo").style.left = (_x.toString() + "px");
						$("photo").style.top = (_y.toString() + "px");
					}
				}

				$("photo").innerHTML = "";
				for (var _y=0;_y<this._tileImageMatrices[_zoomLevel]._y;_y++) {
					for (var _x=0;_x<this._tileImageMatrices[_zoomLevel]._x;_x++) {
						$("photo").insert(Builder.node("div",{id: "pt_" + _zoomLevel.toString() + "_" + _x.toString() + "_" + _y.toString(),style: "background: url(" + getStaticURL("/images/v3.5/_/photocopa/loadingTile.png") + ") no-repeat;"}));
					}
				}
				// showPhotoAtZoomLevel() is done doing whatever, so set this to the passed _zoomLevel
				this._currentPhotoZoomLevel = _zoomLevel;

				if (_mouseCoordinates === undefined) {
					this.centerPhoto();
				}

				this.updatePhotoOverviewInterface(true);
				this.updateZoomInterface();
				this.loadPhotoTilesInView();
				this.updateExtractedPhotoColorsDelegator();
			}
		}
	},

	setOnBeforeUnload: function(_state) {
		if (_state === "off") {
			window.onbeforeunload = this._onbeforeunload;
		} else if (_state === "on") {
			window.onbeforeunload = function(_event) {
				return this._confirmOnBeforeUnloadMsg;
			}.bindAsEventListener(this);
		}
	},

	setOpacity: function(_elementID,_opacity) {
		$(_elementID).style.opacity = _opacity;
		$(_elementID).style.MozOpacity = _opacity;
		$(_elementID).style.filter = "alpha(opacity=" + (_opacity * 100).toString() + ")";
	},

	getNumericallyShardedDirectory: function(_path,_id) {
		// primitive sprintf()
		return _path.replace(/%s/,Math.floor(_id / 10000).toString() + "/" + Math.floor(_id / 1000).toString() + "/" + _id.toString());
	},

	isWithinRange: function(_val,_low,_high) {
		return ((_val >= _low) && (_val <= _high));
	},

	isValidHex: function(_hex) {
		return /^[a-fA-F0-9]{6}$/.test(_hex);
	},

	hex2hsv: function(h) {
		return this.rgb2hsv(this.hex2rgb(h));
	},

	hex2cmyk: function(h) {
		return this.rgb2cmyk(this.hex2rgb(h));
	},

	hex2rgb: function(r) {
		return({0:parseInt(r.substr(0,2),16),1:parseInt(r.substr(2,2),16),2:parseInt(r.substr(4,2),16)});
	},

	rgb2hsv: function(r) {
		var max=Math.max(r[0],r[1],r[2]), delta=max-Math.min(r[0],r[1],r[2]), H, S, V;
		if(max!=0) {
			S=Math.round(delta/max*100);
			if(r[0]==max) H=(r[1]-r[2])/delta; else if(r[1]==max) H=2+(r[2]-r[0])/delta; else if(r[2]==max) H=4+(r[0]-r[1])/delta; var H=Math.min(Math.round(H*60),360); if(H<0) H+=360;
		}
		return({0:H?H:0,1:S?S:0,2:Math.round((max/255)*100)});
	},

	rgb2cmyk: function(r) {
		var C=1-(r[0]/255), M=1-(r[1]/255), Y=1-(r[2]/255), K=Math.min(Y,Math.min(M,Math.min(C,1)));
		C=Math.round((C-K)/(1-K)*100);
		C = (isNaN(C)) ? 0 : C;
		M=Math.round((M-K)/(1-K)*100);
		M = (isNaN(M)) ? 0 : M;
		Y=Math.round((Y-K)/(1-K)*100);
		Y = (isNaN(Y)) ? 0 : Y;
		K=Math.round(K*100);
		return([C?C:0,M?M:0,Y?Y:0,K]);
	},

	unload: function(_event) {
	}
});