1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2007, 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2007, 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Creates a slider.
 26  * @constructor
 27  * @class
 28  * This class represents a slider.
 29  *
 30  * @param {DwtControl}	parent    the parent widget
 31  * @param {DwtSlider.HORIZONTAL|DwtSlider.VERTICAL}	orientation		the orientation of the slider
 32  * @param {string}	[className] 	the CSS class. If not provided defaults to "DwtHorizontalSlider" or "DwtVerticalSlider"
 33  * @param {constant}	[posStyle=DwtControl.STATIC_STYLE] the positioning style (see {@link DwtControl})
 34  * 
 35  * @extends		DwtControl
 36  */
 37 DwtSlider = function(parent, orientation, className, posStyle) {
 38     if (arguments.length == 0) return;
 39     this._orientation = orientation || DwtSlider.HORIZONTAL;
 40     className = className || (this._orientation == DwtSlider.HORIZONTAL ? "DwtHorizontalSlider" : "DwtVerticalSlider");
 41     DwtControl.call(this, {parent:parent, className:className, posStyle:posStyle});
 42 
 43     this._size = 0;
 44     this._buttonSize = 0;
 45 
 46     this._value = 0;
 47     this._minimum = 0;
 48     this._maximum = 100;
 49     
 50     this._isDragging = false;
 51 
 52 	DwtDragTracker.init(this, null, 0, 0, this._dragListener, this);
 53 
 54     this._createHtml();
 55 };
 56 
 57 DwtSlider.prototype = new DwtControl;
 58 DwtSlider.prototype.constructor = DwtSlider;
 59 
 60 /**
 61  * Defines the "horizontal" orientation.
 62  */
 63 DwtSlider.HORIZONTAL = 1;
 64 /**
 65  * Defines the "vertical" orientation.
 66  */
 67 DwtSlider.VERTICAL = 2;
 68 
 69 DwtSlider.prototype.toString =
 70 function() {
 71     return "DwtSlider";
 72 };
 73 
 74 /**
 75  * Sets the value of the slider, moving the position button accordingly.
 76  *
 77  * @param {number}		value		the value
 78  * @param {boolean}	notify			if <code>true</code>, to notify change listeners of the new value
 79  */
 80 DwtSlider.prototype.setValue =
 81 function(value, notify) {
 82 	// Adjust the value into the valid range.
 83 	value = Math.max(this._minimum, value);
 84 	value = Math.min(this._maximum, value);
 85 	this._value = value;
 86 
 87 	// Move the button.
 88 	var location = this._valueToLocation(value);
 89     var property = this._orientation == DwtSlider.HORIZONTAL ? "left" : "top";
 90     var element = this._getButtonElement();
 91     element.style[property] = location;
 92     
 93 	// Send notification.
 94 	if (notify) {
 95 		if (!this._changeEvent) {
 96 			this._changeEvent = new DwtEvent(true);
 97 			this._changeEvent.dwtObj = this;
 98 		}
 99 	    this.notifyListeners(DwtEvent.ONCHANGE, this._changeEvent);
100 	}
101 };
102 
103 /**
104  * Gets the slider value.
105  * 
106  * @return	{number}	the value
107  */
108 DwtSlider.prototype.getValue =
109 function() {
110 	return this._value;
111 };
112 
113 /**
114  * Sets the range and value of the slider.
115  *
116  * @param {number}	minimum	the minimum allowed value
117  * @param {number}	maximum	the maximum allowed value
118  * @param {number}	value		the value
119  * @param {boolean}	notify	if <code>true</code>, notify change listeners of the new value
120  */
121 DwtSlider.prototype.setRange =
122 function(minimum, maximum, newValue, notify) {
123 	if (minimum >= maximum) {
124 		throw new DwtException("Invalid slider range: [" + minimum + ", " + maximum + "]");
125 	};
126 
127 	this._minimum = minimum;
128 	this._maximum = maximum;
129 	if (typeof newValue == "undefined") {
130 		newValue = minimum;
131 	}
132 	this.setValue(newValue, notify);
133 };
134 
135 /**
136  * Gets the minimum allowed value.
137  * 
138  * @return	{number}		the minimum value
139  */
140 DwtSlider.prototype.getMinimum =
141 function() {
142 	return this._minimum;
143 };
144 
145 /**
146  * Gets the maximum allowed value
147  * 
148  * @return	{number}		the maximum value
149  */
150 DwtSlider.prototype.getMaximum =
151 function() {
152 	return this._maximum;
153 };
154 
155 /**
156  * Checks if the slider is currently dragging.
157  * 
158  * @return	{Boolean}	<code>true</code> if the slider is dragging
159  */
160 DwtSlider.prototype.isDragging =
161 function() {
162 	return this._isDragging;
163 };
164 
165 /**
166  * Adds a change listener.
167  *
168  * @param {AjxListener} listener	the listener
169  */
170 DwtSlider.prototype.addChangeListener = 
171 function(listener) {
172     this.addListener(DwtEvent.ONCHANGE, listener);
173 };
174 
175 DwtSlider.prototype._setLocation =
176 function(location, notify) {
177 	var value = this._locationToValue(location);
178 	this.setValue(value, notify);
179 };
180 
181 DwtSlider.prototype._getLocation =
182 function() {
183 	return this._valueToLocation(this._value);
184 };
185 
186 DwtSlider.prototype._valueToLocation =
187 function(value) {
188 	if (this._orientation == DwtSlider.HORIZONTAL) {
189 	    return (value - this._minimum) / (this._maximum - this._minimum) * (this._size - this._buttonSize);
190 	} else {
191 	    return this._size - this._buttonSize - (value - this._minimum) / (this._maximum - this._minimum) * (this._size - this._buttonSize);
192 	}
193 };
194 
195 DwtSlider.prototype._locationToValue =
196 function(location) {
197 	if (this._orientation == DwtSlider.HORIZONTAL) {
198 	    return location / (this._size - this._buttonSize) * (this._maximum - this._minimum) + this._minimum;
199 	} else {
200 	    return (this._size - this._buttonSize - location) / (this._size - this._buttonSize) * (this._maximum - this._minimum) + this._minimum;
201 	}
202 };
203 
204 DwtSlider.prototype._calculateSizes =
205 function() {
206 	var property = this._orientation == DwtSlider.HORIZONTAL ? "x" : "y";
207 	this._buttonSize = Dwt.getSize(this._getButtonElement())[property];
208 	this._size = Dwt.getSize(this.getHtmlElement())[property];
209 	if (this._buttonSize >= this._size) {
210 		throw new DwtException("Invalid slider sizes");
211 	}
212 };
213 
214 DwtSlider.prototype._getButtonElement =
215 function() {
216 	return document.getElementById(this._htmlElId + "_button");
217 };
218 
219 DwtSlider.prototype._createHtml =
220 function() {
221     var element = this.getHtmlElement();
222     var args = { id:this._htmlElId };
223     var template = this._orientation == DwtSlider.HORIZONTAL ? 
224     	"dwt.Widgets#DwtHorizontalSlider" : 
225     	"dwt.Widgets#DwtVerticalSlider";
226     element.innerHTML = AjxTemplate.expand(template, args);
227     this._calculateSizes();
228 };
229 
230 DwtSlider.prototype._dragListener =
231 function(obj, a, b) {
232 	var elementProperty = this._orientation == DwtSlider.HORIZONTAL ? "x" : "y";
233 	var eventProperty = this._orientation == DwtSlider.HORIZONTAL ? "docX" : "docY";
234 	if (obj.state == DwtDragTracker.STATE_START) {
235 		// If clicked outside of button, move button immediately.
236 		var windowLocation = Dwt.toWindow(this.getHtmlElement(), 0, 0);
237 		var clickLocation = obj.mouseEv[eventProperty] - windowLocation[elementProperty];
238 		var buttonLocation = this._getLocation();
239 		if (clickLocation < buttonLocation || clickLocation > (buttonLocation + this._buttonSize)) {
240 			this._setLocation(clickLocation - this._buttonSize / 2, true);
241 		}
242 
243 		// Save the original position in the tracker's user data.
244 		obj.userData = { location: this._getLocation(), value: this._value };
245 		this._isDragging = true;
246 		this._moved = false;
247 	} else {
248 		if (obj.state == DwtDragTracker.STATE_END) {
249 			this._isDragging = false;
250 		} else if (obj.state == DwtDragTracker.STATE_DRAGGING) {
251 			this._moved = true;
252 		}
253 		if (this._moved) {
254 			var location = obj.userData.location + obj.delta[elementProperty];
255 			this._setLocation(location, true);
256 		}
257 	}
258 };
259