rangemodel.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Implementation of a range model. This is an implementation of
  16. * the BoundedRangeModel as described by Java at
  17. * http://java.sun.com/javase/6/docs/api/javax/swing/BoundedRangeModel.html.
  18. *
  19. * One good way to understand the range model is to think of a scroll bar for
  20. * a scrollable element. In that case minimum is 0, maximum is scrollHeight,
  21. * value is scrollTop and extent is clientHeight.
  22. *
  23. * Based on http://webfx.eae.net/dhtml/slider/js/range.js
  24. *
  25. * @author arv@google.com (Erik Arvidsson)
  26. */
  27. goog.provide('goog.ui.RangeModel');
  28. goog.require('goog.events.EventTarget');
  29. goog.require('goog.ui.Component');
  30. /**
  31. * Creates a range model
  32. * @extends {goog.events.EventTarget}
  33. * @constructor
  34. */
  35. goog.ui.RangeModel = function() {
  36. goog.events.EventTarget.call(this);
  37. };
  38. goog.inherits(goog.ui.RangeModel, goog.events.EventTarget);
  39. goog.tagUnsealableClass(goog.ui.RangeModel);
  40. /**
  41. * @type {number}
  42. * @private
  43. */
  44. goog.ui.RangeModel.prototype.value_ = 0;
  45. /**
  46. * @type {number}
  47. * @private
  48. */
  49. goog.ui.RangeModel.prototype.minimum_ = 0;
  50. /**
  51. * @type {number}
  52. * @private
  53. */
  54. goog.ui.RangeModel.prototype.maximum_ = 100;
  55. /**
  56. * @type {number}
  57. * @private
  58. */
  59. goog.ui.RangeModel.prototype.extent_ = 0;
  60. /**
  61. * @type {?number}
  62. * @private
  63. */
  64. goog.ui.RangeModel.prototype.step_ = 1;
  65. /**
  66. * This is true if something is changed as a side effect. This happens when for
  67. * example we set the maximum below the current value.
  68. * @type {boolean}
  69. * @private
  70. */
  71. goog.ui.RangeModel.prototype.isChanging_ = false;
  72. /**
  73. * If set to true, we do not fire any change events.
  74. * @type {boolean}
  75. * @private
  76. */
  77. goog.ui.RangeModel.prototype.mute_ = false;
  78. /**
  79. * Sets the model to mute / unmute.
  80. * @param {boolean} muteValue Whether or not to mute the range, i.e.,
  81. * suppress any CHANGE events.
  82. */
  83. goog.ui.RangeModel.prototype.setMute = function(muteValue) {
  84. this.mute_ = muteValue;
  85. };
  86. /**
  87. * Sets the value.
  88. * @param {number} value The new value.
  89. */
  90. goog.ui.RangeModel.prototype.setValue = function(value) {
  91. value = this.roundToStepWithMin(value);
  92. if (this.value_ != value) {
  93. if (value + this.extent_ > this.maximum_) {
  94. this.value_ = this.maximum_ - this.extent_;
  95. } else if (value < this.minimum_) {
  96. this.value_ = this.minimum_;
  97. } else {
  98. this.value_ = value;
  99. }
  100. if (!this.isChanging_ && !this.mute_) {
  101. this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
  102. }
  103. }
  104. };
  105. /**
  106. * @return {number} the current value.
  107. */
  108. goog.ui.RangeModel.prototype.getValue = function() {
  109. return this.roundToStepWithMin(this.value_);
  110. };
  111. /**
  112. * Sets the extent. The extent is the 'size' of the value.
  113. * @param {number} extent The new extent.
  114. */
  115. goog.ui.RangeModel.prototype.setExtent = function(extent) {
  116. extent = this.roundToStepWithMin(extent);
  117. if (this.extent_ != extent) {
  118. if (extent < 0) {
  119. this.extent_ = 0;
  120. } else if (this.value_ + extent > this.maximum_) {
  121. this.extent_ = this.maximum_ - this.value_;
  122. } else {
  123. this.extent_ = extent;
  124. }
  125. if (!this.isChanging_ && !this.mute_) {
  126. this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
  127. }
  128. }
  129. };
  130. /**
  131. * @return {number} The extent for the range model.
  132. */
  133. goog.ui.RangeModel.prototype.getExtent = function() {
  134. return this.roundToStep(this.extent_);
  135. };
  136. /**
  137. * Sets the minimum
  138. * @param {number} minimum The new minimum.
  139. */
  140. goog.ui.RangeModel.prototype.setMinimum = function(minimum) {
  141. // Don't round minimum because it is the base
  142. if (this.minimum_ != minimum) {
  143. var oldIsChanging = this.isChanging_;
  144. this.isChanging_ = true;
  145. this.minimum_ = minimum;
  146. if (minimum + this.extent_ > this.maximum_) {
  147. this.extent_ = this.maximum_ - this.minimum_;
  148. }
  149. if (minimum > this.value_) {
  150. this.setValue(minimum);
  151. }
  152. if (minimum > this.maximum_) {
  153. this.extent_ = 0;
  154. this.setMaximum(minimum);
  155. this.setValue(minimum);
  156. }
  157. this.isChanging_ = oldIsChanging;
  158. if (!this.isChanging_ && !this.mute_) {
  159. this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
  160. }
  161. }
  162. };
  163. /**
  164. * @return {number} The minimum value for the range model.
  165. */
  166. goog.ui.RangeModel.prototype.getMinimum = function() {
  167. return this.roundToStepWithMin(this.minimum_);
  168. };
  169. /**
  170. * Sets the maximum
  171. * @param {number} maximum The new maximum.
  172. */
  173. goog.ui.RangeModel.prototype.setMaximum = function(maximum) {
  174. maximum = this.roundToStepWithMin(maximum);
  175. if (this.maximum_ != maximum) {
  176. var oldIsChanging = this.isChanging_;
  177. this.isChanging_ = true;
  178. this.maximum_ = maximum;
  179. if (maximum < this.value_ + this.extent_) {
  180. this.setValue(maximum - this.extent_);
  181. }
  182. if (maximum < this.minimum_) {
  183. this.extent_ = 0;
  184. this.setMinimum(maximum);
  185. this.setValue(this.maximum_);
  186. }
  187. if (maximum < this.minimum_ + this.extent_) {
  188. this.extent_ = this.maximum_ - this.minimum_;
  189. }
  190. this.isChanging_ = oldIsChanging;
  191. if (!this.isChanging_ && !this.mute_) {
  192. this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
  193. }
  194. }
  195. };
  196. /**
  197. * @return {number} The maximimum value for the range model.
  198. */
  199. goog.ui.RangeModel.prototype.getMaximum = function() {
  200. return this.roundToStepWithMin(this.maximum_);
  201. };
  202. /**
  203. * Returns the step value. The step value is used to determine how to round the
  204. * value.
  205. * @return {?number} The maximimum value for the range model.
  206. */
  207. goog.ui.RangeModel.prototype.getStep = function() {
  208. return this.step_;
  209. };
  210. /**
  211. * Sets the step. The step value is used to determine how to round the value.
  212. * @param {?number} step The step size.
  213. */
  214. goog.ui.RangeModel.prototype.setStep = function(step) {
  215. if (this.step_ != step) {
  216. this.step_ = step;
  217. // adjust value, extent and maximum
  218. var oldIsChanging = this.isChanging_;
  219. this.isChanging_ = true;
  220. this.setMaximum(this.getMaximum());
  221. this.setExtent(this.getExtent());
  222. this.setValue(this.getValue());
  223. this.isChanging_ = oldIsChanging;
  224. if (!this.isChanging_ && !this.mute_) {
  225. this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
  226. }
  227. }
  228. };
  229. /**
  230. * Rounds to the closest step using the minimum value as the base.
  231. * @param {number} value The number to round.
  232. * @return {number} The number rounded to the closest step.
  233. */
  234. goog.ui.RangeModel.prototype.roundToStepWithMin = function(value) {
  235. if (this.step_ == null) return value;
  236. return this.minimum_ +
  237. Math.round((value - this.minimum_) / this.step_) * this.step_;
  238. };
  239. /**
  240. * Rounds to the closest step.
  241. * @param {number} value The number to round.
  242. * @return {number} The number rounded to the closest step.
  243. */
  244. goog.ui.RangeModel.prototype.roundToStep = function(value) {
  245. if (this.step_ == null) return value;
  246. return Math.round(value / this.step_) * this.step_;
  247. };