Multi.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. /**
  2. * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking
  3. * and animation. Can be added as an item to any container.
  4. *
  5. * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
  6. *
  7. * @example
  8. * Ext.create('Ext.slider.Multi', {
  9. * width: 200,
  10. * values: [25, 50, 75],
  11. * increment: 5,
  12. * minValue: 0,
  13. * maxValue: 100,
  14. *
  15. * // this defaults to true, setting to false allows the thumbs to pass each other
  16. * constrainThumbs: false,
  17. * renderTo: Ext.getBody()
  18. * });
  19. */
  20. Ext.define('Ext.slider.Multi', {
  21. extend: 'Ext.form.field.Base',
  22. alias: 'widget.multislider',
  23. alternateClassName: 'Ext.slider.MultiSlider',
  24. requires: [
  25. 'Ext.slider.Thumb',
  26. 'Ext.slider.Tip',
  27. 'Ext.Number',
  28. 'Ext.util.Format',
  29. 'Ext.Template',
  30. 'Ext.layout.component.field.Slider'
  31. ],
  32. childEls: [
  33. 'endEl', 'innerEl'
  34. ],
  35. // note: {id} here is really {inputId}, but {cmpId} is available
  36. fieldSubTpl: [
  37. '<div id="{id}" class="' + Ext.baseCSSPrefix + 'slider {fieldCls} {vertical}" aria-valuemin="{minValue}" aria-valuemax="{maxValue}" aria-valuenow="{value}" aria-valuetext="{value}">',
  38. '<div id="{cmpId}-endEl" class="' + Ext.baseCSSPrefix + 'slider-end" role="presentation">',
  39. '<div id="{cmpId}-innerEl" class="' + Ext.baseCSSPrefix + 'slider-inner" role="presentation">',
  40. '{%this.renderThumbs(out, values)%}',
  41. '</div>',
  42. '</div>',
  43. '</div>',
  44. {
  45. renderThumbs: function(out, values) {
  46. var me = values.$comp,
  47. i = 0,
  48. thumbs = me.thumbs,
  49. len = thumbs.length,
  50. thumb,
  51. thumbConfig;
  52. for (; i < len; i++) {
  53. thumb = thumbs[i];
  54. thumbConfig = thumb.getElConfig();
  55. thumbConfig.id = me.id + '-thumb-' + i;
  56. Ext.DomHelper.generateMarkup(thumbConfig, out);
  57. }
  58. },
  59. disableFormats: true
  60. }
  61. ],
  62. /**
  63. * @cfg {Number} value
  64. * A value with which to initialize the slider. Setting this will only result in the creation
  65. * of a single slider thumb; if you want multiple thumbs then use the {@link #values} config instead.
  66. *
  67. * Defaults to #minValue.
  68. */
  69. /**
  70. * @cfg {Number[]} values
  71. * Array of Number values with which to initalize the slider. A separate slider thumb will be created for each value
  72. * in this array. This will take precedence over the single {@link #value} config.
  73. */
  74. /**
  75. * @cfg {Boolean} vertical
  76. * Orient the Slider vertically rather than horizontally.
  77. */
  78. vertical: false,
  79. /**
  80. * @cfg {Number} minValue
  81. * The minimum value for the Slider.
  82. */
  83. minValue: 0,
  84. /**
  85. * @cfg {Number} maxValue
  86. * The maximum value for the Slider.
  87. */
  88. maxValue: 100,
  89. /**
  90. * @cfg {Number/Boolean} decimalPrecision The number of decimal places to which to round the Slider's value.
  91. *
  92. * To disable rounding, configure as **false**.
  93. */
  94. decimalPrecision: 0,
  95. /**
  96. * @cfg {Number} keyIncrement
  97. * How many units to change the Slider when adjusting with keyboard navigation. If the increment
  98. * config is larger, it will be used instead.
  99. */
  100. keyIncrement: 1,
  101. /**
  102. * @cfg {Number} increment
  103. * How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
  104. */
  105. increment: 0,
  106. /**
  107. * @cfg {Boolean} [zeroBasedSnapping=false]
  108. * Set to `true` to calculate snap points based on {@link #increment}s from zero as opposed to
  109. * from this Slider's {@link #minValue}.
  110. *
  111. * By Default, valid snap points are calculated starting {@link #increment}s from the {@link #minValue}
  112. */
  113. /**
  114. * @private
  115. * @property {Number[]} clickRange
  116. * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom],
  117. * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top'
  118. * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range
  119. */
  120. clickRange: [5,15],
  121. /**
  122. * @cfg {Boolean} clickToChange
  123. * Determines whether or not clicking on the Slider axis will change the slider.
  124. */
  125. clickToChange : true,
  126. /**
  127. * @cfg {Boolean} animate
  128. * Turn on or off animation.
  129. */
  130. animate: true,
  131. /**
  132. * @property {Boolean} dragging
  133. * True while the thumb is in a drag operation
  134. */
  135. dragging: false,
  136. /**
  137. * @cfg {Boolean} constrainThumbs
  138. * True to disallow thumbs from overlapping one another.
  139. */
  140. constrainThumbs: true,
  141. componentLayout: 'sliderfield',
  142. /**
  143. * @cfg {Object/Boolean} useTips
  144. * True to use an {@link Ext.slider.Tip} to display tips for the value. This option may also
  145. * provide a configuration object for an {@link Ext.slider.Tip}.
  146. */
  147. useTips : true,
  148. /**
  149. * @cfg {Function} [tipText=undefined]
  150. * A function used to display custom text for the slider tip.
  151. *
  152. * Defaults to null, which will use the default on the plugin.
  153. *
  154. * @cfg {Ext.slider.Thumb} tipText.thumb The Thumb that the Tip is attached to
  155. * @cfg {String} tipText.return The text to display in the tip
  156. */
  157. tipText : null,
  158. ariaRole: 'slider',
  159. // private override
  160. initValue: function() {
  161. var me = this,
  162. extValue = Ext.value,
  163. // Fallback for initial values: values config -> value config -> minValue config -> 0
  164. values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]),
  165. i = 0,
  166. len = values.length;
  167. // Store for use in dirty check
  168. me.originalValue = values;
  169. // Add a thumb for each value
  170. for (; i < len; i++) {
  171. me.addThumb(values[i]);
  172. }
  173. },
  174. // private override
  175. initComponent : function() {
  176. var me = this,
  177. tipPlug,
  178. hasTip,
  179. p, pLen, plugins;
  180. /**
  181. * @property {Array} thumbs
  182. * Array containing references to each thumb
  183. */
  184. me.thumbs = [];
  185. me.keyIncrement = Math.max(me.increment, me.keyIncrement);
  186. me.addEvents(
  187. /**
  188. * @event beforechange
  189. * Fires before the slider value is changed. By returning false from an event handler, you can cancel the
  190. * event and prevent the slider from changing.
  191. * @param {Ext.slider.Multi} slider The slider
  192. * @param {Number} newValue The new value which the slider is being changed to.
  193. * @param {Number} oldValue The old value which the slider was previously.
  194. */
  195. 'beforechange',
  196. /**
  197. * @event change
  198. * Fires when the slider value is changed.
  199. * @param {Ext.slider.Multi} slider The slider
  200. * @param {Number} newValue The new value which the slider has been changed to.
  201. * @param {Ext.slider.Thumb} thumb The thumb that was changed
  202. */
  203. 'change',
  204. /**
  205. * @event changecomplete
  206. * Fires when the slider value is changed by the user and any drag operations have completed.
  207. * @param {Ext.slider.Multi} slider The slider
  208. * @param {Number} newValue The new value which the slider has been changed to.
  209. * @param {Ext.slider.Thumb} thumb The thumb that was changed
  210. */
  211. 'changecomplete',
  212. /**
  213. * @event dragstart
  214. * Fires after a drag operation has started.
  215. * @param {Ext.slider.Multi} slider The slider
  216. * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
  217. */
  218. 'dragstart',
  219. /**
  220. * @event drag
  221. * Fires continuously during the drag operation while the mouse is moving.
  222. * @param {Ext.slider.Multi} slider The slider
  223. * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
  224. */
  225. 'drag',
  226. /**
  227. * @event dragend
  228. * Fires after the drag operation has completed.
  229. * @param {Ext.slider.Multi} slider The slider
  230. * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
  231. */
  232. 'dragend'
  233. );
  234. // Ensure that the maxValue is a snap point, and that the initial value is snapped.
  235. if (me.increment) {
  236. me.maxValue = Ext.Number.snapInRange(me.maxValue, me.increment, me.minValue);
  237. me.value = me.normalizeValue(me.value);
  238. }
  239. me.callParent();
  240. // only can use it if it exists.
  241. if (me.useTips) {
  242. if (Ext.isObject(me.useTips)) {
  243. tipPlug = Ext.apply({}, me.useTips);
  244. } else {
  245. tipPlug = me.tipText ? {getText: me.tipText} : {};
  246. }
  247. plugins = me.plugins = me.plugins || [];
  248. pLen = plugins.length;
  249. for (p = 0; p < pLen; p++) {
  250. if (plugins[p].isSliderTip) {
  251. hasTip = true;
  252. break;
  253. }
  254. }
  255. if (!hasTip) {
  256. me.plugins.push(new Ext.slider.Tip(tipPlug));
  257. }
  258. }
  259. },
  260. /**
  261. * Creates a new thumb and adds it to the slider
  262. * @param {Number} [value=0] The initial value to set on the thumb.
  263. * @return {Ext.slider.Thumb} The thumb
  264. */
  265. addThumb: function(value) {
  266. var me = this,
  267. thumb = new Ext.slider.Thumb({
  268. ownerCt : me,
  269. ownerLayout : me.getComponentLayout(),
  270. value : value,
  271. slider : me,
  272. index : me.thumbs.length,
  273. constrain : me.constrainThumbs,
  274. disabled : !!me.readOnly
  275. });
  276. me.thumbs.push(thumb);
  277. //render the thumb now if needed
  278. if (me.rendered) {
  279. thumb.render();
  280. }
  281. return thumb;
  282. },
  283. /**
  284. * @private
  285. * Moves the given thumb above all other by increasing its z-index. This is called when as drag
  286. * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
  287. * required when the thumbs are stacked on top of each other at one of the ends of the slider's
  288. * range, which can result in the user not being able to move any of them.
  289. * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
  290. */
  291. promoteThumb: function(topThumb) {
  292. var thumbs = this.thumbs,
  293. ln = thumbs.length,
  294. zIndex, thumb, i;
  295. for (i = 0; i < ln; i++) {
  296. thumb = thumbs[i];
  297. if (thumb == topThumb) {
  298. thumb.bringToFront();
  299. } else {
  300. thumb.sendToBack();
  301. }
  302. }
  303. },
  304. // private override
  305. getSubTplData : function() {
  306. var me = this;
  307. return Ext.apply(me.callParent(), {
  308. $comp: me,
  309. vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz',
  310. minValue: me.minValue,
  311. maxValue: me.maxValue,
  312. value: me.value
  313. });
  314. },
  315. onRender : function() {
  316. var me = this,
  317. thumbs = me.thumbs,
  318. len = thumbs.length,
  319. i = 0,
  320. thumb;
  321. me.callParent(arguments);
  322. for (i = 0; i < len; i++) {
  323. thumb = thumbs[i];
  324. thumb.el = me.el.getById(me.id + '-thumb-' + i);
  325. thumb.onRender();
  326. }
  327. },
  328. /**
  329. * @private
  330. * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
  331. */
  332. initEvents : function() {
  333. var me = this;
  334. me.mon(me.el, {
  335. scope : me,
  336. mousedown: me.onMouseDown,
  337. keydown : me.onKeyDown
  338. });
  339. },
  340. /**
  341. * @private
  342. * Given an `[x, y]` position within the slider's track (Points outside the slider's track are coerced to either the minimum or maximum value),
  343. * calculate how many pixels **from the slider origin** (left for horizontal Sliders and bottom for vertical Sliders) that point is.
  344. *
  345. * If the point is outside the range of the Slider's track, the return value is `undefined`
  346. * @param {Number[]} xy The point to calculate the track point for
  347. */
  348. getTrackpoint : function(xy) {
  349. var me = this,
  350. result,
  351. positionProperty,
  352. sliderTrack = me.innerEl,
  353. trackLength;
  354. if (me.vertical) {
  355. positionProperty = 'top';
  356. trackLength = sliderTrack.getHeight();
  357. } else {
  358. positionProperty = 'left';
  359. trackLength = sliderTrack.getWidth();
  360. }
  361. result = Ext.Number.constrain(sliderTrack.translatePoints(xy)[positionProperty], 0, trackLength);
  362. return me.vertical ? trackLength - result : result;
  363. },
  364. /**
  365. * @private
  366. * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
  367. * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
  368. * @param {Ext.EventObject} e The click event
  369. */
  370. onMouseDown : function(e) {
  371. var me = this,
  372. thumbClicked = false,
  373. i = 0,
  374. thumbs = me.thumbs,
  375. len = thumbs.length,
  376. trackPoint;
  377. if (me.disabled) {
  378. return;
  379. }
  380. //see if the click was on any of the thumbs
  381. for (; i < len; i++) {
  382. thumbClicked = thumbClicked || e.target == thumbs[i].el.dom;
  383. }
  384. if (me.clickToChange && !thumbClicked) {
  385. trackPoint = me.getTrackpoint(e.getXY());
  386. if (trackPoint !== undefined) {
  387. me.onClickChange(trackPoint);
  388. }
  389. }
  390. me.focus();
  391. },
  392. /**
  393. * @private
  394. * Moves the thumb to the indicated position.
  395. * Only changes the value if the click was within this.clickRange.
  396. * @param {Number} trackPoint local pixel offset **from the origin** (left for horizontal and bottom for vertical) along the Slider's axis at which the click event occured.
  397. */
  398. onClickChange : function(trackPoint) {
  399. var me = this,
  400. thumb, index;
  401. // How far along the track *from the origin* was the click.
  402. // If vertical, the origin is the bottom of the slider track.
  403. //find the nearest thumb to the click event
  404. thumb = me.getNearest(trackPoint);
  405. if (!thumb.disabled) {
  406. index = thumb.index;
  407. me.setValue(index, Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision), undefined, true);
  408. }
  409. },
  410. /**
  411. * @private
  412. * Returns the nearest thumb to a click event, along with its distance
  413. * @param {Number} trackPoint local pixel position along the Slider's axis to find the Thumb for
  414. * @return {Object} The closest thumb object and its distance from the click event
  415. */
  416. getNearest: function(trackPoint) {
  417. var me = this,
  418. clickValue = me.reversePixelValue(trackPoint),
  419. nearestDistance = (me.maxValue - me.minValue) + 5, //add a small fudge for the end of the slider
  420. nearest = null,
  421. thumbs = me.thumbs,
  422. i = 0,
  423. len = thumbs.length,
  424. thumb,
  425. value,
  426. dist;
  427. for (; i < len; i++) {
  428. thumb = me.thumbs[i];
  429. value = thumb.value;
  430. dist = Math.abs(value - clickValue);
  431. if (Math.abs(dist <= nearestDistance)) {
  432. nearest = thumb;
  433. nearestDistance = dist;
  434. }
  435. }
  436. return nearest;
  437. },
  438. /**
  439. * @private
  440. * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
  441. * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
  442. * @param {Ext.EventObject} e The Event object
  443. */
  444. onKeyDown : function(e) {
  445. /*
  446. * The behaviour for keyboard handling with multiple thumbs is currently undefined.
  447. * There's no real sane default for it, so leave it like this until we come up
  448. * with a better way of doing it.
  449. */
  450. var me = this,
  451. k,
  452. val;
  453. if(me.disabled || me.thumbs.length !== 1) {
  454. e.preventDefault();
  455. return;
  456. }
  457. k = e.getKey();
  458. switch(k) {
  459. case e.UP:
  460. case e.RIGHT:
  461. e.stopEvent();
  462. val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement;
  463. me.setValue(0, val, undefined, true);
  464. break;
  465. case e.DOWN:
  466. case e.LEFT:
  467. e.stopEvent();
  468. val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement;
  469. me.setValue(0, val, undefined, true);
  470. break;
  471. default:
  472. e.preventDefault();
  473. }
  474. },
  475. /**
  476. * @private
  477. * Returns a snapped, constrained value when given a desired value
  478. * @param {Number} value Raw number value
  479. * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
  480. */
  481. normalizeValue : function(v) {
  482. var me = this,
  483. Num = Ext.Number,
  484. snapFn = Num[me.zeroBasedSnapping ? 'snap' : 'snapInRange'];
  485. v = snapFn.call(Num, v, me.increment, me.minValue, me.maxValue);
  486. v = Ext.util.Format.round(v, me.decimalPrecision);
  487. v = Ext.Number.constrain(v, me.minValue, me.maxValue);
  488. return v;
  489. },
  490. /**
  491. * Sets the minimum value for the slider instance. If the current value is less than the minimum value, the current
  492. * value will be changed.
  493. * @param {Number} val The new minimum value
  494. */
  495. setMinValue : function(val) {
  496. var me = this,
  497. i = 0,
  498. thumbs = me.thumbs,
  499. len = thumbs.length,
  500. t;
  501. me.minValue = val;
  502. if (me.rendered) {
  503. me.inputEl.dom.setAttribute('aria-valuemin', val);
  504. }
  505. for (; i < len; ++i) {
  506. t = thumbs[i];
  507. t.value = t.value < val ? val : t.value;
  508. }
  509. me.syncThumbs();
  510. },
  511. /**
  512. * Sets the maximum value for the slider instance. If the current value is more than the maximum value, the current
  513. * value will be changed.
  514. * @param {Number} val The new maximum value
  515. */
  516. setMaxValue : function(val) {
  517. var me = this,
  518. i = 0,
  519. thumbs = me.thumbs,
  520. len = thumbs.length,
  521. t;
  522. me.maxValue = val;
  523. if (me.rendered) {
  524. me.inputEl.dom.setAttribute('aria-valuemax', val);
  525. }
  526. for (; i < len; ++i) {
  527. t = thumbs[i];
  528. t.value = t.value > val ? val : t.value;
  529. }
  530. me.syncThumbs();
  531. },
  532. /**
  533. * Programmatically sets the value of the Slider. Ensures that the value is constrained within the minValue and
  534. * maxValue.
  535. * @param {Number} index Index of the thumb to move
  536. * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
  537. * @param {Boolean} [animate=true] Turn on or off animation
  538. */
  539. setValue : function(index, value, animate, changeComplete) {
  540. var me = this,
  541. thumb = me.thumbs[index];
  542. // ensures value is contstrained and snapped
  543. value = me.normalizeValue(value);
  544. if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
  545. thumb.value = value;
  546. if (me.rendered) {
  547. // TODO this only handles a single value; need a solution for exposing multiple values to aria.
  548. // Perhaps this should go on each thumb element rather than the outer element.
  549. me.inputEl.set({
  550. 'aria-valuenow': value,
  551. 'aria-valuetext': value
  552. });
  553. thumb.move(me.calculateThumbPosition(value), Ext.isDefined(animate) ? animate !== false : me.animate);
  554. me.fireEvent('change', me, value, thumb);
  555. me.checkDirty();
  556. if (changeComplete) {
  557. me.fireEvent('changecomplete', me, value, thumb);
  558. }
  559. }
  560. }
  561. },
  562. /**
  563. * @private
  564. * Given a value within this Slider's range, calculates a Thumb's percentage CSS position to map that value.
  565. */
  566. calculateThumbPosition : function(v) {
  567. return (v - this.minValue) / (this.maxValue - this.minValue) * 100;
  568. },
  569. /**
  570. * @private
  571. * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
  572. * the ratio is 2
  573. * @return {Number} The ratio of pixels to mapped values
  574. */
  575. getRatio : function() {
  576. var me = this,
  577. trackLength = this.vertical ? this.innerEl.getHeight() : this.innerEl.getWidth(),
  578. valueRange = this.maxValue - this.minValue;
  579. return valueRange === 0 ? trackLength : (trackLength / valueRange);
  580. },
  581. /**
  582. * @private
  583. * Given a pixel location along the slider, returns the mapped slider value for that pixel.
  584. * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reversePixelValue(50)
  585. * returns 200
  586. * @param {Number} pos The position along the slider to return a mapped value for
  587. * @return {Number} The mapped value for the given position
  588. */
  589. reversePixelValue : function(pos) {
  590. return this.minValue + (pos / this.getRatio());
  591. },
  592. /**
  593. * @private
  594. * Given a Thumb's percentage position along the slider, returns the mapped slider value for that pixel.
  595. * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reversePercentageValue(25)
  596. * returns 200
  597. * @param {Number} pos The percentage along the slider track to return a mapped value for
  598. * @return {Number} The mapped value for the given position
  599. */
  600. reversePercentageValue : function(pos) {
  601. return this.minValue + (this.maxValue - this.minValue) * (pos / 100);
  602. },
  603. //private
  604. onDisable: function() {
  605. var me = this,
  606. i = 0,
  607. thumbs = me.thumbs,
  608. len = thumbs.length,
  609. thumb,
  610. el,
  611. xy;
  612. me.callParent();
  613. for (; i < len; i++) {
  614. thumb = thumbs[i];
  615. el = thumb.el;
  616. thumb.disable();
  617. if(Ext.isIE) {
  618. //IE breaks when using overflow visible and opacity other than 1.
  619. //Create a place holder for the thumb and display it.
  620. xy = el.getXY();
  621. el.hide();
  622. me.innerEl.addCls(me.disabledCls).dom.disabled = true;
  623. if (!me.thumbHolder) {
  624. me.thumbHolder = me.endEl.createChild({cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls});
  625. }
  626. me.thumbHolder.show().setXY(xy);
  627. }
  628. }
  629. },
  630. //private
  631. onEnable: function() {
  632. var me = this,
  633. i = 0,
  634. thumbs = me.thumbs,
  635. len = thumbs.length,
  636. thumb,
  637. el;
  638. this.callParent();
  639. for (; i < len; i++) {
  640. thumb = thumbs[i];
  641. el = thumb.el;
  642. thumb.enable();
  643. if (Ext.isIE) {
  644. me.innerEl.removeCls(me.disabledCls).dom.disabled = false;
  645. if (me.thumbHolder) {
  646. me.thumbHolder.hide();
  647. }
  648. el.show();
  649. me.syncThumbs();
  650. }
  651. }
  652. },
  653. /**
  654. * Synchronizes thumbs position to the proper proportion of the total component width based on the current slider
  655. * {@link #value}. This will be called automatically when the Slider is resized by a layout, but if it is rendered
  656. * auto width, this method can be called from another resize handler to sync the Slider if necessary.
  657. */
  658. syncThumbs : function() {
  659. if (this.rendered) {
  660. var thumbs = this.thumbs,
  661. length = thumbs.length,
  662. i = 0;
  663. for (; i < length; i++) {
  664. thumbs[i].move(this.calculateThumbPosition(thumbs[i].value));
  665. }
  666. }
  667. },
  668. /**
  669. * Returns the current value of the slider
  670. * @param {Number} index The index of the thumb to return a value for
  671. * @return {Number/Number[]} The current value of the slider at the given index, or an array of all thumb values if
  672. * no index is given.
  673. */
  674. getValue : function(index) {
  675. return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
  676. },
  677. /**
  678. * Returns an array of values - one for the location of each thumb
  679. * @return {Number[]} The set of thumb values
  680. */
  681. getValues: function() {
  682. var values = [],
  683. i = 0,
  684. thumbs = this.thumbs,
  685. len = thumbs.length;
  686. for (; i < len; i++) {
  687. values.push(thumbs[i].value);
  688. }
  689. return values;
  690. },
  691. getSubmitValue: function() {
  692. var me = this;
  693. return (me.disabled || !me.submitValue) ? null : me.getValue();
  694. },
  695. reset: function() {
  696. var me = this,
  697. arr = [].concat(me.originalValue),
  698. a = 0,
  699. aLen = arr.length,
  700. val;
  701. for (; a < aLen; a++) {
  702. val = arr[a];
  703. me.setValue(a, val);
  704. }
  705. me.clearInvalid();
  706. // delete here so we reset back to the original state
  707. delete me.wasValid;
  708. },
  709. setReadOnly: function(readOnly){
  710. var me = this,
  711. thumbs = me.thumbs,
  712. len = thumbs.length,
  713. i = 0;
  714. me.callParent(arguments);
  715. readOnly = me.readOnly;
  716. for (; i < len; ++i) {
  717. if (readOnly) {
  718. thumbs[i].disable();
  719. } else {
  720. thumbs[i].enable();
  721. }
  722. }
  723. },
  724. // private
  725. beforeDestroy : function() {
  726. var me = this,
  727. thumbs = me.thumbs,
  728. t = 0,
  729. tLen = thumbs.length,
  730. thumb;
  731. Ext.destroy(me.innerEl, me.endEl, me.focusEl);
  732. for (; t < tLen; t++) {
  733. thumb = thumbs[t];
  734. Ext.destroy(thumb);
  735. }
  736. me.callParent();
  737. }
  738. });