1 /** 
  2  * @fileoverview
  3  * 128 x 128 HSV Picker. 
  4  * @requires APE.dom, APE.EventPublisher, APE.color.ColorRGB
  5  * @author Garrett Smith
  6 
  7  * @namespace APE.widget
  8  */
  9 APE.namespace("APE.widget");
 10 
 11 /** 
 12  * @constructor
 13  */
 14 APE.widget.HsvPicker = function(id) {
 15 	
 16     var Draggable = APE.drag.Draggable;
 17 	this.id = id;
 18 	
 19 	var hueSelectorEl = document.getElementById(this.id + "-hue-slider");
 20 	var bgSelectorEl = document.getElementById(this.id + "-saturation-value-selector");
 21 
 22 	this.hueSlider = Draggable.getByNode(hueSelectorEl, APE.drag.Draggable.constraints.VERT);
 23 
 24 	this.hueSlider.keepInContainer = true;
 25 
 26 	this.bgSelector = Draggable.getByNode(bgSelectorEl);
 27 	
 28 	var bg = bgSelectorEl.parentNode.parentNode;
 29 	bg.onselectstart=function(){return false;};
 30 	this.bg = bg;
 31 	this.bgSelector.container = bg;
 32 	
 33 	this.bgSelector.keepInContainer = true;
 34 	
 35 	this.textInput = document.getElementById(this.id + "-color-input");
 36 	this.displayStyle = document.getElementById(this.id + "-color-preview").style;
 37 	
 38 	// TODO: bgSelectorEl.parentNode.offsetTop/2
 39 	this.bgClipTop = 12/2;
 40 	this.bgClipLeft = 12/2;
 41 	
 42 	this.enabled = true;
 43 	
 44 	this.prevValue = this.textInput.value;
 45 };
 46 
 47 APE.widget.HsvPicker.getById = APE.getById;
 48 
 49 (function(){
 50 
 51 	var HsvPicker = APE.widget.HsvPicker;
 52 
 53 	APE.mixin(HsvPicker, {
 54 		hueSlid : hueSlid,
 55 		grabHueSlider : grabHueSlider,
 56 		bgSelectorDrag : bgSelectorDrag,
 57 		textInputBlur : textInputBlur,
 58 		checkEnabled : checkEnabled,
 59 		textInputKeyDown : textInputKeyDown,
 60 		backgroundMousedown : backgroundMousedown,
 61 		saveValue : saveValue,
 62 		transparentClicked : transparentClicked
 63 	});
 64 
 65 	function hueSlid(e) {
 66 		var hsvPicker = HsvPicker.getById(this.id.split("-")[0]);
 67 		if(!hsvPicker.enabled) return;
 68 		hsvPicker.onbeforechange();
 69 		hsvPicker.h = 360 * hsvPicker.hueSlider.el.offsetTop/128;
 70 		
 71 		hsvPicker.bg.style.background = hsvPicker.rgbForHue(hsvPicker.h);
 72 		hsvPicker.updateDisplay();
 73 		hsvPicker.onchange(e);
 74 	}
 75 
 76 	function grabHueSlider(e) {
 77 		var hsvPicker = HsvPicker.getById(this.id.split("-")[0]);
 78 		if(!hsvPicker.enabled) return;
 79 		hsvPicker.hueSlider.grab(e);
 80 		hsvPicker.updateDisplay();
 81 		hsvPicker.onchange(e);
 82 		hsvPicker.onchangecomplete(e);
 83 	}
 84 
 85 	function textInputBlur(e) {
 86 		var hsvPicker = HsvPicker.getById(this.id.split("-")[0]);
 87 		if(!hsvPicker.enabled) return;
 88 		return hsvPicker.trySetValue(this.value, e||event);
 89 	}
 90 
 91 	function checkEnabled() {
 92 		return HsvPicker.getById(this.id.split("-")[0]).enabled;
 93 	}
 94 
 95 	// Must use keyDown for IE.
 96 	function textInputKeyDown(e) {
 97 		e = e||window.event;
 98 		var isTabKey = e.keyCode == 9;
 99 		var isEnterKey = e.keyCode == 13;
100 		if(isTabKey || isEnterKey) {
101 			HsvPicker.getById(this.id.split("-")[0]).trySetValue(this.value, e||event);
102 		}
103 		if(isEnterKey)
104 			this.focus();
105 	}
106 
107 	function backgroundMousedown(e) {
108 		var hsvPicker = HsvPicker.getById(this.id.split("-")[0]);
109 		if(!hsvPicker.enabled) return;
110 		hsvPicker.onbeforechange();
111 		hsvPicker.bgSelector.grab(e);
112 		hsvPicker.bg.style.background = hsvPicker.rgbForHue(hsvPicker.h);
113 		hsvPicker.updateDisplay();
114 		hsvPicker.onchange(e);
115 		hsvPicker.onchangecomplete(e);
116 	}
117 
118 	function bgSelectorDrag(e) {
119 		
120 		var hsvPicker = HsvPicker.getById(this.id.split("-")[0]);
121 
122 		if(!hsvPicker.enabled) return;
123 		
124 		hsvPicker.v = (127 - (this.el.offsetTop + hsvPicker.bgClipTop))/127;
125 		hsvPicker.s = (this.el.offsetLeft + hsvPicker.bgClipLeft)/127;
126 		//document.title = 'v=' + hsvPicker.v + ", s=" + hsvPicker.s;
127 		hsvPicker.updateDisplay();
128 		hsvPicker.onchange(e);
129 	}
130 
131 	function saveValue() {
132 		if(this.textInput.value)
133 			this.prevValue = this.textInput.value;
134 	}
135 
136 	function transparentClicked(e) {
137 		var hsvPicker = HsvPicker.getById(this.id.split("-")[0]);
138 		hsvPicker.onbeforechange();
139 		if(this.checked) {
140 			hsvPicker.prevValue = hsvPicker.textInput.value;
141 			hsvPicker.setEnabled(false);
142 			hsvPicker.setValue("transparent");
143 		}
144 		else {
145 			hsvPicker.setValue(hsvPicker.prevValue||new APE.color.ColorRGB(255,255,255).toString());
146 			hsvPicker.prevValue = "";
147 			hsvPicker.setEnabled(true);
148 			
149 			// We have to update the position of the hueSlider and 
150 			// bgSelector, so we just feign ondrag by direct invocation.
151 			// This seems potentially dangerous.
152 			hsvPicker.hueSlider.ondrag(e);
153 			hsvPicker.bgSelector.ondrag(e);
154 		}
155 		hsvPicker.onchange(e);
156 		hsvPicker.onchangecomplete(e);
157 	}
158 
159 })();
160 
161 APE.widget.HsvPicker.prototype = {
162 	
163 	rgbForHue : APE.color.ColorHSV.rgbForHue,
164 	rgbFromString : APE.color.ColorRGB.fromString,
165 
166 	init : function() {
167 		if(this.textInput.value)
168 			this.setValue(this.textInput.value);
169         else
170             this.setValue("#ff0000");
171 		
172 		var HsvPicker = APE.widget.HsvPicker;
173 
174 		this.bgSelector.onbeforedragstart = HsvPicker.bgSelectorDrag;
175 		this.bgSelector.ondrag = HsvPicker.bgSelectorDrag;
176 		this.bgSelector.onglide = HsvPicker.bgSelectorDrag;
177 		this.hueSlider.onbeforedragstart = HsvPicker.hueSlid;
178 		this.hueSlider.ondrag = HsvPicker.hueSlid;
179 		this.hueSlider.onglide = HsvPicker.hueSlid;
180 		this.hueSlider.container.onmousedown = HsvPicker.grabHueSlider;
181 		this.bgSelector.onglide = HsvPicker.bgSelectorDrag;
182 		
183 		this.bgSelector.onfocus = HsvPicker.checkEnabled;
184 		this.hueSlider.onfocus = HsvPicker.checkEnabled;
185 
186 		var EventPublisher = APE.EventPublisher;
187 
188 		EventPublisher.add(this.textInput, "onblur", HsvPicker.textInputBlur);
189 		EventPublisher.add(this, "onbeforechange", HsvPicker.saveValue);
190 		EventPublisher.add(this.bgSelector.el.parentNode, "onmousedown", HsvPicker.backgroundMousedown);
191 		EventPublisher.add(this.bgSelector, "ondragend", function(e) { this.onchangecomplete(e); }, this);
192 		EventPublisher.add(this.hueSlider, "ondragend", function(e) { this.onchangecomplete(e); }, this);
193 
194 		var tpCheckbox = document.getElementById(this.id + "-transparent-checkbox");
195 		EventPublisher.add(tpCheckbox, "onclick", HsvPicker.transparentClicked);
196 
197 		// This handles initialization when transparent checkbox is checked at load time.
198 		if(tpCheckbox.checked)
199 			HsvPicker.transparentClicked.call(document.getElementById(this.id + "-transparent-checkbox"));
200 	},
201 	
202 	getHexValue : function() {
203 		if(this.textInput.value == "") return "";
204 		return new APE.color.ColorHSV(this.h, this.s, this.v).toRGB().toHexString();
205 	},
206 	
207     /**@event*/
208 	onbeforechange : function() { },
209     /**@event*/
210 	onchange : function(e) { },
211     /**@event*/
212 	onchangecomplete : function(e) { },
213 	
214 	setEnabled : function(bEnable) {
215 		this.enabled = bEnable;
216 	},
217 	
218 	setValue : function(hexOrRGB) {
219 		
220 		if(hexOrRGB == "transparent") {
221 			this.displayStyle.background = "transparent";
222 			this.hueSlider.el.parentNode.style.background = "transparent";
223 			this.bgSelector.el.parentNode.style.visibility = "hidden";
224 			this.bgSelector.el.parentNode.previousSibling.style.visibility = "inherit";
225 			this.setEnabled(false);
226 			this.bg.style.backgroundColor = "transparent";
227 			this.textInput.value = "";
228 			document.getElementById(this.id + "-transparent-checkbox").checked = true;
229 			return;
230 		}
231 		else {
232 			this.displayStyle.background = "transparent";
233 			// this kills the background image in IE7.
234             // this.hueSlider.el.parentNode.style.background = "";
235 			this.bgSelector.el.parentNode.style.visibility = "inherit";
236 			this.bgSelector.el.parentNode.previousSibling.style.visibility = "hidden";
237 			this.setEnabled(true);
238 			document.getElementById(this.id + "-transparent-checkbox").checked = false;
239 		}
240 		
241 		var rgb = this.rgbFromString(hexOrRGB);
242 		var isInputValid = rgb.isValid();
243 		if(!isInputValid) {
244 			rgb = this.rgbForHue(this.h = 0, this.s = 1, this.v = 1);
245 		}
246 
247 		var hsv = rgb.toHSV();
248 		this.h = hsv.h;
249 		this.s = hsv.s;
250 		this.v = hsv.v;
251 		
252 		this.hueSlider.el.style.top = (hsv.h/360*128) + "px";
253 		this.bgSelector.moveToX( (hsv.s * 127) - this.bgClipLeft);
254 		this.bgSelector.moveToY( 127 - (hsv.v * 127)  - this.bgClipTop );
255 		this.bg.style.backgroundColor = this.rgbForHue(hsv.h, 1, 1).toHexString();
256 		this.updateDisplay(rgb, isInputValid);
257 	},
258 	
259 	updateDisplay : function(rgb, isInputValid) {
260 		var color = rgb||new APE.color.ColorHSV(this.h, this.s, this.v).toRGB();
261 		if(color.isValid() && !(isInputValid == false))
262 			this.displayStyle.backgroundColor = this.textInput.value = color.toHexString();
263 		else
264 			this.textInput.value = "";
265 	},
266 	
267 	trySetValue : function( value, e) {
268 		if(this.rgbFromString(value).isValid()) {
269 		 	this.setValue(value);
270 			this.onchange(e);
271 			this.onchangecomplete(e);
272 			return true;
273 		}
274 		else {
275 			alert("Please enter a valid color value.");
276 			return false;
277 		}	
278 	}
279 };