slider.js 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. /*!
  2. * # Fomantic-UI - Slider
  3. * http://github.com/fomantic/Fomantic-UI/
  4. *
  5. *
  6. * Released under the MIT license
  7. * http://opensource.org/licenses/MIT
  8. *
  9. */
  10. ;(function ( $, window, document, undefined ) {
  11. "use strict";
  12. window = (typeof window != 'undefined' && window.Math == Math)
  13. ? window
  14. : (typeof self != 'undefined' && self.Math == Math)
  15. ? self
  16. : Function('return this')()
  17. ;
  18. $.fn.slider = function(parameters) {
  19. var
  20. $allModules = $(this),
  21. $window = $(window),
  22. moduleSelector = $allModules.selector || '',
  23. time = new Date().getTime(),
  24. performance = [],
  25. query = arguments[0],
  26. methodInvoked = (typeof query == 'string'),
  27. queryArguments = [].slice.call(arguments, 1),
  28. alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
  29. SINGLE_STEP = 1,
  30. BIG_STEP = 2,
  31. NO_STEP = 0,
  32. SINGLE_BACKSTEP = -1,
  33. BIG_BACKSTEP = -2,
  34. // Used to manage document bound events.
  35. // Use this so that we can distinguish between which document events are bound to which range.
  36. currentRange = 0,
  37. returnedValue
  38. ;
  39. $allModules
  40. .each(function() {
  41. var
  42. settings = ( $.isPlainObject(parameters) )
  43. ? $.extend(true, {}, $.fn.slider.settings, parameters)
  44. : $.extend({}, $.fn.slider.settings),
  45. className = settings.className,
  46. metadata = settings.metadata,
  47. namespace = settings.namespace,
  48. error = settings.error,
  49. keys = settings.keys,
  50. interpretLabel = settings.interpretLabel,
  51. isHover = false,
  52. eventNamespace = '.' + namespace,
  53. moduleNamespace = 'module-' + namespace,
  54. $module = $(this),
  55. $currThumb,
  56. $thumb,
  57. $secondThumb,
  58. $track,
  59. $trackFill,
  60. $labels,
  61. element = this,
  62. instance = $module.data(moduleNamespace),
  63. documentEventID,
  64. value,
  65. position,
  66. secondPos,
  67. offset,
  68. precision,
  69. isTouch,
  70. gapRatio = 1,
  71. module
  72. ;
  73. module = {
  74. initialize: function() {
  75. module.debug('Initializing slider', settings);
  76. currentRange += 1;
  77. documentEventID = currentRange;
  78. isTouch = module.setup.testOutTouch();
  79. module.setup.layout();
  80. module.setup.labels();
  81. if(!module.is.disabled()) {
  82. module.bind.events();
  83. }
  84. module.read.metadata();
  85. module.read.settings();
  86. module.instantiate();
  87. },
  88. instantiate: function() {
  89. module.verbose('Storing instance of slider', module);
  90. instance = module;
  91. $module
  92. .data(moduleNamespace, module)
  93. ;
  94. },
  95. destroy: function() {
  96. module.verbose('Destroying previous slider for', $module);
  97. clearInterval(instance.interval);
  98. module.unbind.events();
  99. module.unbind.slidingEvents();
  100. $module.removeData(moduleNamespace);
  101. instance = undefined;
  102. },
  103. setup: {
  104. layout: function() {
  105. if( $module.attr('tabindex') === undefined) {
  106. $module.attr('tabindex', 0);
  107. }
  108. if($module.find('.inner').length == 0) {
  109. $module.append("<div class='inner'>"
  110. + "<div class='track'></div>"
  111. + "<div class='track-fill'></div>"
  112. + "<div class='thumb'></div>"
  113. + "</div>");
  114. }
  115. precision = module.get.precision();
  116. $thumb = $module.find('.thumb:not(.second)');
  117. $currThumb = $thumb;
  118. if(module.is.range()) {
  119. if($module.find('.thumb.second').length == 0) {
  120. $module.find('.inner').append("<div class='thumb second'></div>");
  121. }
  122. $secondThumb = $module.find('.thumb.second');
  123. }
  124. $track = $module.find('.track');
  125. $trackFill = $module.find('.track-fill');
  126. offset = $thumb.width() / 2;
  127. },
  128. labels: function() {
  129. if(module.is.labeled()) {
  130. $labels = $module.find('.labels:not(.auto)');
  131. if($labels.length != 0) {
  132. module.setup.customLabel();
  133. } else {
  134. module.setup.autoLabel();
  135. }
  136. if (settings.showLabelTicks) {
  137. $module.addClass(className.ticked)
  138. }
  139. }
  140. },
  141. testOutTouch: function() {
  142. try {
  143. document.createEvent('TouchEvent');
  144. return true;
  145. } catch (e) {
  146. return false;
  147. }
  148. },
  149. customLabel: function() {
  150. var
  151. $children = $labels.find('.label'),
  152. numChildren = $children.length,
  153. min = module.get.min(),
  154. max = module.get.max(),
  155. ratio
  156. ;
  157. $children.each(function(index) {
  158. var
  159. $child = $(this),
  160. attrValue = $child.attr('data-value')
  161. ;
  162. if(attrValue) {
  163. attrValue = attrValue > max ? max : attrValue < min ? min : attrValue;
  164. ratio = (attrValue - min) / (max - min);
  165. } else {
  166. ratio = (index + 1) / (numChildren + 1);
  167. }
  168. module.update.labelPosition(ratio, $(this));
  169. });
  170. },
  171. autoLabel: function() {
  172. if(module.get.step() != 0) {
  173. $labels = $module.find('.labels');
  174. if($labels.length != 0) {
  175. $labels.empty();
  176. }
  177. else {
  178. $labels = $module.append('<ul class="auto labels"></ul>').find('.labels');
  179. }
  180. for(var i = 0, len = module.get.numLabels(); i <= len; i++) {
  181. var
  182. labelText = module.get.label(i),
  183. $label = (labelText !== "")
  184. ? !(i % module.get.gapRatio())
  185. ? $('<li class="label">' + labelText + '</li>')
  186. : $('<li class="halftick label"></li>')
  187. : null,
  188. ratio = i / len
  189. ;
  190. if($label) {
  191. module.update.labelPosition(ratio, $label);
  192. $labels.append($label);
  193. }
  194. }
  195. }
  196. }
  197. },
  198. bind: {
  199. events: function() {
  200. module.bind.globalKeyboardEvents();
  201. module.bind.keyboardEvents();
  202. module.bind.mouseEvents();
  203. if(module.is.touch()) {
  204. module.bind.touchEvents();
  205. }
  206. if (settings.autoAdjustLabels) {
  207. module.bind.windowEvents();
  208. }
  209. },
  210. keyboardEvents: function() {
  211. module.verbose('Binding keyboard events');
  212. $module.on('keydown' + eventNamespace, module.event.keydown);
  213. },
  214. globalKeyboardEvents: function() {
  215. $(document).on('keydown' + eventNamespace + documentEventID, module.event.activateFocus);
  216. },
  217. mouseEvents: function() {
  218. module.verbose('Binding mouse events');
  219. $module.find('.track, .thumb, .inner').on('mousedown' + eventNamespace, function(event) {
  220. event.stopImmediatePropagation();
  221. event.preventDefault();
  222. module.event.down(event);
  223. });
  224. $module.on('mousedown' + eventNamespace, module.event.down);
  225. $module.on('mouseenter' + eventNamespace, function(event) {
  226. isHover = true;
  227. });
  228. $module.on('mouseleave' + eventNamespace, function(event) {
  229. isHover = false;
  230. });
  231. },
  232. touchEvents: function() {
  233. module.verbose('Binding touch events');
  234. $module.find('.track, .thumb, .inner').on('touchstart' + eventNamespace, function(event) {
  235. event.stopImmediatePropagation();
  236. event.preventDefault();
  237. module.event.down(event);
  238. });
  239. $module.on('touchstart' + eventNamespace, module.event.down);
  240. },
  241. slidingEvents: function() {
  242. // these don't need the identifier because we only ever want one of them to be registered with document
  243. module.verbose('Binding page wide events while handle is being draged');
  244. if(module.is.touch()) {
  245. $(document).on('touchmove' + eventNamespace, module.event.move);
  246. $(document).on('touchend' + eventNamespace, module.event.up);
  247. }
  248. else {
  249. $(document).on('mousemove' + eventNamespace, module.event.move);
  250. $(document).on('mouseup' + eventNamespace, module.event.up);
  251. }
  252. },
  253. windowEvents: function() {
  254. $window.on('resize' + eventNamespace, module.event.resize);
  255. }
  256. },
  257. unbind: {
  258. events: function() {
  259. $module.find('.track, .thumb, .inner').off('mousedown' + eventNamespace);
  260. $module.find('.track, .thumb, .inner').off('touchstart' + eventNamespace);
  261. $module.off('mousedown' + eventNamespace);
  262. $module.off('mouseenter' + eventNamespace);
  263. $module.off('mouseleave' + eventNamespace);
  264. $module.off('touchstart' + eventNamespace);
  265. $module.off('keydown' + eventNamespace);
  266. $module.off('focusout' + eventNamespace);
  267. $(document).off('keydown' + eventNamespace + documentEventID, module.event.activateFocus);
  268. $window.off('resize' + eventNamespace);
  269. },
  270. slidingEvents: function() {
  271. if(module.is.touch()) {
  272. $(document).off('touchmove' + eventNamespace);
  273. $(document).off('touchend' + eventNamespace);
  274. } else {
  275. $(document).off('mousemove' + eventNamespace);
  276. $(document).off('mouseup' + eventNamespace);
  277. }
  278. },
  279. },
  280. event: {
  281. down: function(event, originalEvent) {
  282. event.preventDefault();
  283. if(module.is.range()) {
  284. var
  285. eventPos = module.determine.eventPos(event, originalEvent),
  286. newPos = module.determine.pos(eventPos)
  287. ;
  288. $currThumb = module.determine.closestThumb(newPos);
  289. }
  290. if(!module.is.disabled()) {
  291. module.bind.slidingEvents();
  292. }
  293. },
  294. move: function(event, originalEvent) {
  295. event.preventDefault();
  296. var value = module.determine.valueFromEvent(event, originalEvent);
  297. if(module.get.step() == 0 || module.is.smooth()) {
  298. var
  299. thumbVal = module.thumbVal,
  300. secondThumbVal = module.secondThumbVal,
  301. thumbSmoothVal = module.determine.smoothValueFromEvent(event, originalEvent)
  302. ;
  303. if(!$currThumb.hasClass('second')) {
  304. thumbVal = value;
  305. } else {
  306. secondThumbVal = value;
  307. }
  308. value = Math.abs(thumbVal - (secondThumbVal || 0));
  309. module.update.position(thumbSmoothVal);
  310. settings.onMove.call(element, value, thumbVal, secondThumbVal);
  311. } else {
  312. module.update.value(value, function(value, thumbVal, secondThumbVal) {
  313. settings.onMove.call(element, value, thumbVal, secondThumbVal);
  314. });
  315. }
  316. },
  317. up: function(event, originalEvent) {
  318. event.preventDefault();
  319. var value = module.determine.valueFromEvent(event, originalEvent);
  320. module.set.value(value);
  321. module.unbind.slidingEvents();
  322. },
  323. keydown: function(event, first) {
  324. if(module.is.focused()) {
  325. $(document).trigger(event);
  326. }
  327. if(first || module.is.focused()) {
  328. var step = module.determine.keyMovement(event);
  329. if(step != NO_STEP) {
  330. event.preventDefault();
  331. switch(step) {
  332. case SINGLE_STEP:
  333. module.takeStep();
  334. break;
  335. case BIG_STEP:
  336. module.takeStep(module.get.multiplier());
  337. break;
  338. case SINGLE_BACKSTEP:
  339. module.backStep();
  340. break;
  341. case BIG_BACKSTEP:
  342. module.backStep(module.get.multiplier());
  343. break;
  344. }
  345. }
  346. }
  347. },
  348. activateFocus: function(event) {
  349. if(!module.is.focused() && module.is.hover() && module.determine.keyMovement(event) != NO_STEP) {
  350. event.preventDefault();
  351. module.event.keydown(event, true);
  352. $module.focus();
  353. }
  354. },
  355. resize: function(_event) {
  356. // To avoid a useless performance cost, we only call the label refresh when its necessary
  357. if (gapRatio != module.get.gapRatio()) {
  358. module.setup.labels();
  359. gapRatio = module.get.gapRatio();
  360. }
  361. }
  362. },
  363. resync: function() {
  364. module.verbose('Resyncing thumb position based on value');
  365. if(module.is.range()) {
  366. module.update.position(module.secondThumbVal, $secondThumb);
  367. }
  368. module.update.position(module.thumbVal, $thumb);
  369. module.setup.labels();
  370. },
  371. takeStep: function(multiplier) {
  372. var
  373. multiplier = multiplier != undefined ? multiplier : 1,
  374. step = module.get.step(),
  375. currValue = module.get.currentThumbValue()
  376. ;
  377. module.verbose('Taking a step');
  378. if(step > 0) {
  379. module.set.value(currValue + step * multiplier);
  380. } else if (step == 0){
  381. var
  382. precision = module.get.precision(),
  383. newValue = currValue + (multiplier/precision)
  384. ;
  385. module.set.value(Math.round(newValue * precision) / precision);
  386. }
  387. },
  388. backStep: function(multiplier) {
  389. var
  390. multiplier = multiplier != undefined ? multiplier : 1,
  391. step = module.get.step(),
  392. currValue = module.get.currentThumbValue()
  393. ;
  394. module.verbose('Going back a step');
  395. if(step > 0) {
  396. module.set.value(currValue - step * multiplier);
  397. } else if (step == 0) {
  398. var
  399. precision = module.get.precision(),
  400. newValue = currValue - (multiplier/precision)
  401. ;
  402. module.set.value(Math.round(newValue * precision) / precision);
  403. }
  404. },
  405. is: {
  406. range: function() {
  407. return $module.hasClass(settings.className.range);
  408. },
  409. hover: function() {
  410. return isHover;
  411. },
  412. focused: function() {
  413. return $module.is(':focus');
  414. },
  415. disabled: function() {
  416. return $module.hasClass(settings.className.disabled);
  417. },
  418. labeled: function() {
  419. return $module.hasClass(settings.className.labeled);
  420. },
  421. reversed: function() {
  422. return $module.hasClass(settings.className.reversed);
  423. },
  424. vertical: function() {
  425. return $module.hasClass(settings.className.vertical);
  426. },
  427. smooth: function() {
  428. return settings.smooth || $module.hasClass(settings.className.smooth);
  429. },
  430. touch: function() {
  431. return isTouch;
  432. }
  433. },
  434. get: {
  435. trackOffset: function() {
  436. if (module.is.vertical()) {
  437. return $track.offset().top;
  438. } else {
  439. return $track.offset().left;
  440. }
  441. },
  442. trackLength: function() {
  443. if (module.is.vertical()) {
  444. return $track.height();
  445. } else {
  446. return $track.width();
  447. }
  448. },
  449. trackLeft: function() {
  450. if (module.is.vertical()) {
  451. return $track.position().top;
  452. } else {
  453. return $track.position().left;
  454. }
  455. },
  456. trackStartPos: function() {
  457. return module.is.reversed() ? module.get.trackLeft() + module.get.trackLength() : module.get.trackLeft();
  458. },
  459. trackEndPos: function() {
  460. return module.is.reversed() ? module.get.trackLeft() : module.get.trackLeft() + module.get.trackLength();
  461. },
  462. trackStartMargin: function () {
  463. var margin;
  464. if (module.is.vertical()) {
  465. margin = module.is.reversed() ? $module.css('padding-bottom') : $module.css('padding-top');
  466. } else {
  467. margin = module.is.reversed() ? $module.css('padding-right') : $module.css('padding-left');
  468. }
  469. return margin || '0px';
  470. },
  471. trackEndMargin: function () {
  472. var margin;
  473. if (module.is.vertical()) {
  474. margin = module.is.reversed() ? $module.css('padding-top') : $module.css('padding-bottom');
  475. } else {
  476. margin = module.is.reversed() ? $module.css('padding-left') : $module.css('padding-right');
  477. }
  478. return margin || '0px';
  479. },
  480. precision: function() {
  481. var
  482. decimalPlaces,
  483. step = module.get.step()
  484. ;
  485. if(step != 0) {
  486. var split = String(step).split('.');
  487. if(split.length == 2) {
  488. decimalPlaces = split[1].length;
  489. } else {
  490. decimalPlaces = 0;
  491. }
  492. } else {
  493. decimalPlaces = settings.decimalPlaces;
  494. }
  495. var precision = Math.pow(10, decimalPlaces);
  496. module.debug('Precision determined', precision);
  497. return precision;
  498. },
  499. min: function() {
  500. return settings.min;
  501. },
  502. max: function() {
  503. var step = module.get.step(),
  504. min = module.get.min(),
  505. quotient = step === 0 ? 0 : Math.floor((settings.max - min) / step),
  506. remainder = step === 0 ? 0 : (settings.max - min) % step;
  507. return remainder === 0 ? settings.max : min + quotient * step;
  508. },
  509. step: function() {
  510. return settings.step;
  511. },
  512. numLabels: function() {
  513. var value = Math.round((module.get.max() - module.get.min()) / module.get.step());
  514. module.debug('Determined that there should be ' + value + ' labels');
  515. return value;
  516. },
  517. labelType: function() {
  518. return settings.labelType;
  519. },
  520. label: function(value) {
  521. if(interpretLabel) {
  522. return interpretLabel(value);
  523. }
  524. switch (settings.labelType) {
  525. case settings.labelTypes.number:
  526. return Math.round(((value * module.get.step()) + module.get.min()) * precision ) / precision;
  527. case settings.labelTypes.letter:
  528. return alphabet[(value) % 26];
  529. default:
  530. return value;
  531. }
  532. },
  533. value: function() {
  534. return value;
  535. },
  536. currentThumbValue: function() {
  537. return $currThumb.hasClass('second') ? module.secondThumbVal : module.thumbVal;
  538. },
  539. thumbValue: function(which) {
  540. switch(which) {
  541. case 'second':
  542. if(module.is.range()) {
  543. return module.secondThumbVal;
  544. }
  545. else {
  546. module.error(error.notrange);
  547. break;
  548. }
  549. case 'first':
  550. default:
  551. return module.thumbVal;
  552. }
  553. },
  554. multiplier: function() {
  555. return settings.pageMultiplier;
  556. },
  557. thumbPosition: function(which) {
  558. switch(which) {
  559. case 'second':
  560. if(module.is.range()) {
  561. return secondPos;
  562. }
  563. else {
  564. module.error(error.notrange);
  565. break;
  566. }
  567. case 'first':
  568. default:
  569. return position;
  570. }
  571. },
  572. gapRatio: function() {
  573. var gapRatio = 1;
  574. if( settings.autoAdjustLabels ) {
  575. var
  576. numLabels = module.get.numLabels(),
  577. gapCounter = 1
  578. ;
  579. // While the distance between two labels is too short,
  580. // we divide the number of labels at each iteration
  581. // and apply only if the modulo of the operation is an odd number.
  582. while ((module.get.trackLength() / numLabels) * gapCounter < settings.labelDistance) {
  583. if( !(numLabels % gapCounter) ) {
  584. gapRatio = gapCounter;
  585. }
  586. gapCounter += 1;
  587. }
  588. return gapRatio;
  589. } else {
  590. return 1;
  591. }
  592. }
  593. },
  594. determine: {
  595. pos: function(pagePos) {
  596. return module.is.reversed()
  597. ?
  598. module.get.trackStartPos() - pagePos + module.get.trackOffset()
  599. :
  600. pagePos - module.get.trackOffset() - module.get.trackStartPos()
  601. ;
  602. },
  603. closestThumb: function(eventPos) {
  604. var
  605. thumbPos = parseFloat(module.determine.thumbPos($thumb)),
  606. thumbDelta = Math.abs(eventPos - thumbPos),
  607. secondThumbPos = parseFloat(module.determine.thumbPos($secondThumb)),
  608. secondThumbDelta = Math.abs(eventPos - secondThumbPos)
  609. ;
  610. return thumbDelta <= secondThumbDelta ? $thumb : $secondThumb;
  611. },
  612. closestThumbPos: function(eventPos) {
  613. var
  614. thumbPos = parseFloat(module.determine.thumbPos($thumb)),
  615. thumbDelta = Math.abs(eventPos - thumbPos),
  616. secondThumbPos = parseFloat(module.determine.thumbPos($secondThumb)),
  617. secondThumbDelta = Math.abs(eventPos - secondThumbPos)
  618. ;
  619. return thumbDelta <= secondThumbDelta ? thumbPos : secondThumbPos;
  620. },
  621. thumbPos: function($element) {
  622. var pos =
  623. module.is.vertical()
  624. ?
  625. module.is.reversed() ? $element.css('bottom') : $element.css('top')
  626. :
  627. module.is.reversed() ? $element.css('right') : $element.css('left')
  628. ;
  629. return pos;
  630. },
  631. positionFromValue: function(value) {
  632. var
  633. min = module.get.min(),
  634. max = module.get.max(),
  635. value = value > max ? max : value < min ? min : value,
  636. trackLength = module.get.trackLength(),
  637. ratio = (value - min) / (max - min),
  638. position = Math.round(ratio * trackLength)
  639. ;
  640. module.verbose('Determined position: ' + position + ' from value: ' + value);
  641. return position;
  642. },
  643. positionFromRatio: function(ratio) {
  644. var
  645. trackLength = module.get.trackLength(),
  646. step = module.get.step(),
  647. position = Math.round(ratio * trackLength),
  648. adjustedPos = (step == 0) ? position : Math.round(position / step) * step
  649. ;
  650. return adjustedPos;
  651. },
  652. valueFromEvent: function(event, originalEvent) {
  653. var
  654. eventPos = module.determine.eventPos(event, originalEvent),
  655. newPos = module.determine.pos(eventPos),
  656. value
  657. ;
  658. if(eventPos < module.get.trackOffset()) {
  659. value = module.is.reversed() ? module.get.max() : module.get.min();
  660. } else if(eventPos > module.get.trackOffset() + module.get.trackLength()) {
  661. value = module.is.reversed() ? module.get.min() : module.get.max();
  662. } else {
  663. value = module.determine.value(newPos);
  664. }
  665. return value;
  666. },
  667. smoothValueFromEvent: function(event, originalEvent) {
  668. var
  669. min = module.get.min(),
  670. max = module.get.max(),
  671. trackLength = module.get.trackLength(),
  672. eventPos = module.determine.eventPos(event, originalEvent),
  673. newPos = eventPos - module.get.trackOffset(),
  674. ratio,
  675. value
  676. ;
  677. newPos = newPos < 0 ? 0 : newPos > trackLength ? trackLength : newPos;
  678. ratio = newPos / trackLength;
  679. if (module.is.reversed()) {
  680. ratio = 1 - ratio;
  681. }
  682. value = ratio * (max - min) + min;
  683. return value;
  684. },
  685. eventPos: function(event, originalEvent) {
  686. if(module.is.touch()) {
  687. var
  688. touchY = event.changedTouches[0].pageY || event.touches[0].pageY,
  689. touchX = event.changedTouches[0].pageX || event.touches[0].pageX
  690. ;
  691. return module.is.vertical() ? touchY : touchX;
  692. }
  693. var
  694. clickY = event.pageY || originalEvent.pageY,
  695. clickX = event.pageX || originalEvent.pageX
  696. ;
  697. return module.is.vertical() ? clickY : clickX;
  698. },
  699. value: function(position) {
  700. var
  701. startPos = module.is.reversed() ? module.get.trackEndPos() : module.get.trackStartPos(),
  702. endPos = module.is.reversed() ? module.get.trackStartPos() : module.get.trackEndPos(),
  703. ratio = (position - startPos) / (endPos - startPos),
  704. range = module.get.max() - module.get.min(),
  705. step = module.get.step(),
  706. value = (ratio * range),
  707. difference = (step == 0) ? value : Math.round(value / step) * step
  708. ;
  709. module.verbose('Determined value based upon position: ' + position + ' as: ' + value);
  710. if(value != difference) {
  711. module.verbose('Rounding value to closest step: ' + difference);
  712. }
  713. // Use precision to avoid ugly Javascript floating point rounding issues
  714. // (like 35 * .01 = 0.35000000000000003)
  715. difference = Math.round(difference * precision) / precision;
  716. module.verbose('Cutting off additional decimal places');
  717. return difference + module.get.min();
  718. },
  719. keyMovement: function(event) {
  720. var
  721. key = event.which,
  722. downArrow =
  723. module.is.vertical()
  724. ?
  725. module.is.reversed() ? keys.downArrow : keys.upArrow
  726. :
  727. keys.downArrow
  728. ,
  729. upArrow =
  730. module.is.vertical()
  731. ?
  732. module.is.reversed() ? keys.upArrow : keys.downArrow
  733. :
  734. keys.upArrow
  735. ,
  736. leftArrow =
  737. !module.is.vertical()
  738. ?
  739. module.is.reversed() ? keys.rightArrow : keys.leftArrow
  740. :
  741. keys.leftArrow
  742. ,
  743. rightArrow =
  744. !module.is.vertical()
  745. ?
  746. module.is.reversed() ? keys.leftArrow : keys.rightArrow
  747. :
  748. keys.rightArrow
  749. ;
  750. if(key == downArrow || key == leftArrow) {
  751. return SINGLE_BACKSTEP;
  752. } else if(key == upArrow || key == rightArrow) {
  753. return SINGLE_STEP;
  754. } else if (key == keys.pageDown) {
  755. return BIG_BACKSTEP;
  756. } else if (key == keys.pageUp) {
  757. return BIG_STEP;
  758. } else {
  759. return NO_STEP;
  760. }
  761. }
  762. },
  763. handleNewValuePosition: function(val) {
  764. var
  765. min = module.get.min(),
  766. max = module.get.max(),
  767. newPos
  768. ;
  769. if (val <= min) {
  770. val = min;
  771. } else if (val >= max) {
  772. val = max;
  773. }
  774. newPos = module.determine.positionFromValue(val);
  775. return newPos;
  776. },
  777. set: {
  778. value: function(newValue) {
  779. module.update.value(newValue, function(value, thumbVal, secondThumbVal) {
  780. settings.onChange.call(element, value, thumbVal, secondThumbVal);
  781. settings.onMove.call(element, value, thumbVal, secondThumbVal);
  782. });
  783. },
  784. rangeValue: function(first, second) {
  785. if(module.is.range()) {
  786. var
  787. min = module.get.min(),
  788. max = module.get.max()
  789. ;
  790. if (first <= min) {
  791. first = min;
  792. } else if(first >= max){
  793. first = max;
  794. }
  795. if (second <= min) {
  796. second = min;
  797. } else if(second >= max){
  798. second = max;
  799. }
  800. module.thumbVal = first;
  801. module.secondThumbVal = second;
  802. value = Math.abs(module.thumbVal - module.secondThumbVal);
  803. module.update.position(module.thumbVal, $thumb);
  804. module.update.position(module.secondThumbVal, $secondThumb);
  805. settings.onChange.call(element, value, module.thumbVal, module.secondThumbVal);
  806. settings.onMove.call(element, value, module.thumbVal, module.secondThumbVal);
  807. } else {
  808. module.error(error.notrange);
  809. }
  810. },
  811. position: function(position, which) {
  812. var thumbVal = module.determine.value(position);
  813. switch (which) {
  814. case 'second':
  815. module.secondThumbVal = thumbVal;
  816. module.update.position(thumbVal, $secondThumb);
  817. break;
  818. default:
  819. module.thumbVal = thumbVal;
  820. module.update.position(thumbVal, $thumb);
  821. }
  822. value = Math.abs(module.thumbVal - (module.secondThumbVal || 0));
  823. module.set.value(value);
  824. }
  825. },
  826. update: {
  827. value: function(newValue, callback) {
  828. var
  829. min = module.get.min(),
  830. max = module.get.max()
  831. ;
  832. if (newValue <= min) {
  833. newValue = min;
  834. } else if(newValue >= max){
  835. newValue = max;
  836. }
  837. if(!module.is.range()) {
  838. value = newValue;
  839. module.thumbVal = value;
  840. } else {
  841. if(!$currThumb.hasClass('second')) {
  842. module.thumbVal = newValue;
  843. } else {
  844. module.secondThumbVal = newValue;
  845. }
  846. value = Math.abs(module.thumbVal - module.secondThumbVal);
  847. }
  848. module.update.position(newValue);
  849. module.debug('Setting slider value to ' + value);
  850. if(typeof callback === 'function') {
  851. callback(value, module.thumbVal, module.secondThumbVal);
  852. }
  853. },
  854. position: function(newValue, $element) {
  855. var
  856. newPos = module.handleNewValuePosition(newValue),
  857. $targetThumb = $element != undefined ? $element : $currThumb,
  858. thumbVal = module.thumbVal || module.get.min(),
  859. secondThumbVal = module.secondThumbVal || module.get.min()
  860. ;
  861. if(module.is.range()) {
  862. if(!$targetThumb.hasClass('second')) {
  863. position = newPos;
  864. thumbVal = newValue;
  865. } else {
  866. secondPos = newPos;
  867. secondThumbVal = newValue;
  868. }
  869. } else {
  870. position = newPos;
  871. thumbVal = newValue;
  872. }
  873. var
  874. trackPosValue,
  875. thumbPosValue,
  876. min = module.get.min(),
  877. max = module.get.max(),
  878. thumbPosPercent = 100 * (newValue - min) / (max - min),
  879. trackStartPosPercent = 100 * (Math.min(thumbVal, secondThumbVal) - min) / (max - min),
  880. trackEndPosPercent = 100 * (1 - (Math.max(thumbVal, secondThumbVal) - min) / (max - min))
  881. ;
  882. if (module.is.vertical()) {
  883. if (module.is.reversed()) {
  884. thumbPosValue = {bottom: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', top: 'auto'};
  885. trackPosValue = {bottom: trackStartPosPercent + '%', top: trackEndPosPercent + '%'};
  886. }
  887. else {
  888. thumbPosValue = {top: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', bottom: 'auto'};
  889. trackPosValue = {top: trackStartPosPercent + '%', bottom: trackEndPosPercent + '%'};
  890. }
  891. } else {
  892. if (module.is.reversed()) {
  893. thumbPosValue = {right: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', left: 'auto'};
  894. trackPosValue = {right: trackStartPosPercent + '%', left: trackEndPosPercent + '%'};
  895. }
  896. else {
  897. thumbPosValue = {left: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', right: 'auto'};
  898. trackPosValue = {left: trackStartPosPercent + '%', right: trackEndPosPercent + '%'};
  899. }
  900. }
  901. $targetThumb.css(thumbPosValue);
  902. $trackFill.css(trackPosValue);
  903. module.debug('Setting slider position to ' + newPos);
  904. },
  905. labelPosition: function (ratio, $label) {
  906. var
  907. startMargin = module.get.trackStartMargin(),
  908. endMargin = module.get.trackEndMargin(),
  909. posDir =
  910. module.is.vertical()
  911. ?
  912. module.is.reversed() ? 'bottom' : 'top'
  913. :
  914. module.is.reversed() ? 'right' : 'left',
  915. startMarginMod = module.is.reversed() && !module.is.vertical() ? ' - ' : ' + '
  916. ;
  917. var position = '(100% - ' + startMargin + ' - ' + endMargin + ') * ' + ratio;
  918. $label.css(posDir, 'calc(' + position + startMarginMod + startMargin + ')');
  919. }
  920. },
  921. goto: {
  922. max: function() {
  923. module.set.value(module.get.max());
  924. },
  925. min: function() {
  926. module.set.value(module.get.min());
  927. },
  928. },
  929. read: {
  930. metadata: function() {
  931. var
  932. data = {
  933. thumbVal : $module.data(metadata.thumbVal),
  934. secondThumbVal : $module.data(metadata.secondThumbVal)
  935. }
  936. ;
  937. if(data.thumbVal) {
  938. if(module.is.range() && data.secondThumbVal) {
  939. module.debug('Current value set from metadata', data.thumbVal, data.secondThumbVal);
  940. module.set.rangeValue(data.thumbVal, data.secondThumbVal);
  941. } else {
  942. module.debug('Current value set from metadata', data.thumbVal);
  943. module.set.value(data.thumbVal);
  944. }
  945. }
  946. },
  947. settings: function() {
  948. if(settings.start !== false) {
  949. if(module.is.range()) {
  950. module.debug('Start position set from settings', settings.start, settings.end);
  951. module.set.rangeValue(settings.start, settings.end);
  952. } else {
  953. module.debug('Start position set from settings', settings.start);
  954. module.set.value(settings.start);
  955. }
  956. }
  957. }
  958. },
  959. setting: function(name, value) {
  960. module.debug('Changing setting', name, value);
  961. if( $.isPlainObject(name) ) {
  962. $.extend(true, settings, name);
  963. }
  964. else if(value !== undefined) {
  965. if($.isPlainObject(settings[name])) {
  966. $.extend(true, settings[name], value);
  967. }
  968. else {
  969. settings[name] = value;
  970. }
  971. }
  972. else {
  973. return settings[name];
  974. }
  975. },
  976. internal: function(name, value) {
  977. if( $.isPlainObject(name) ) {
  978. $.extend(true, module, name);
  979. }
  980. else if(value !== undefined) {
  981. module[name] = value;
  982. }
  983. else {
  984. return module[name];
  985. }
  986. },
  987. debug: function() {
  988. if(!settings.silent && settings.debug) {
  989. if(settings.performance) {
  990. module.performance.log(arguments);
  991. }
  992. else {
  993. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  994. module.debug.apply(console, arguments);
  995. }
  996. }
  997. },
  998. verbose: function() {
  999. if(!settings.silent && settings.verbose && settings.debug) {
  1000. if(settings.performance) {
  1001. module.performance.log(arguments);
  1002. }
  1003. else {
  1004. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1005. module.verbose.apply(console, arguments);
  1006. }
  1007. }
  1008. },
  1009. error: function() {
  1010. if(!settings.silent) {
  1011. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1012. module.error.apply(console, arguments);
  1013. }
  1014. },
  1015. performance: {
  1016. log: function(message) {
  1017. var
  1018. currentTime,
  1019. executionTime,
  1020. previousTime
  1021. ;
  1022. if(settings.performance) {
  1023. currentTime = new Date().getTime();
  1024. previousTime = time || currentTime;
  1025. executionTime = currentTime - previousTime;
  1026. time = currentTime;
  1027. performance.push({
  1028. 'Name' : message[0],
  1029. 'Arguments' : [].slice.call(message, 1) || '',
  1030. 'Element' : element,
  1031. 'Execution Time' : executionTime
  1032. });
  1033. }
  1034. clearTimeout(module.performance.timer);
  1035. module.performance.timer = setTimeout(module.performance.display, 500);
  1036. },
  1037. display: function() {
  1038. var
  1039. title = settings.name + ':',
  1040. totalTime = 0
  1041. ;
  1042. time = false;
  1043. clearTimeout(module.performance.timer);
  1044. $.each(performance, function(index, data) {
  1045. totalTime += data['Execution Time'];
  1046. });
  1047. title += ' ' + totalTime + 'ms';
  1048. if(moduleSelector) {
  1049. title += ' \'' + moduleSelector + '\'';
  1050. }
  1051. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  1052. console.groupCollapsed(title);
  1053. if(console.table) {
  1054. console.table(performance);
  1055. }
  1056. else {
  1057. $.each(performance, function(index, data) {
  1058. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  1059. });
  1060. }
  1061. console.groupEnd();
  1062. }
  1063. performance = [];
  1064. }
  1065. },
  1066. invoke: function(query, passedArguments, context) {
  1067. var
  1068. object = instance,
  1069. maxDepth,
  1070. found,
  1071. response
  1072. ;
  1073. passedArguments = passedArguments || queryArguments;
  1074. context = element || context;
  1075. if(typeof query == 'string' && object !== undefined) {
  1076. query = query.split(/[\. ]/);
  1077. maxDepth = query.length - 1;
  1078. $.each(query, function(depth, value) {
  1079. var camelCaseValue = (depth != maxDepth)
  1080. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1081. : query
  1082. ;
  1083. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  1084. object = object[camelCaseValue];
  1085. }
  1086. else if( object[camelCaseValue] !== undefined ) {
  1087. found = object[camelCaseValue];
  1088. return false;
  1089. }
  1090. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  1091. object = object[value];
  1092. }
  1093. else if( object[value] !== undefined ) {
  1094. found = object[value];
  1095. return false;
  1096. }
  1097. else {
  1098. module.error(error.method, query);
  1099. return false;
  1100. }
  1101. });
  1102. }
  1103. if ( $.isFunction( found ) ) {
  1104. response = found.apply(context, passedArguments);
  1105. }
  1106. else if(found !== undefined) {
  1107. response = found;
  1108. }
  1109. if($.isArray(returnedValue)) {
  1110. returnedValue.push(response);
  1111. }
  1112. else if(returnedValue !== undefined) {
  1113. returnedValue = [returnedValue, response];
  1114. }
  1115. else if(response !== undefined) {
  1116. returnedValue = response;
  1117. }
  1118. return found;
  1119. }
  1120. };
  1121. if(methodInvoked) {
  1122. if(instance === undefined) {
  1123. module.initialize();
  1124. }
  1125. module.invoke(query);
  1126. }
  1127. else {
  1128. if(instance !== undefined) {
  1129. instance.invoke('destroy');
  1130. }
  1131. module.initialize();
  1132. }
  1133. })
  1134. ;
  1135. return (returnedValue !== undefined)
  1136. ? returnedValue
  1137. : this
  1138. ;
  1139. };
  1140. $.fn.slider.settings = {
  1141. silent : false,
  1142. debug : false,
  1143. verbose : false,
  1144. performance : true,
  1145. name : 'Slider',
  1146. namespace : 'slider',
  1147. error : {
  1148. method : 'The method you called is not defined.',
  1149. notrange : 'This slider is not a range slider'
  1150. },
  1151. metadata: {
  1152. thumbVal : 'thumbVal',
  1153. secondThumbVal : 'secondThumbVal'
  1154. },
  1155. min : 0,
  1156. max : 20,
  1157. step : 1,
  1158. start : 0,
  1159. end : 20,
  1160. labelType : 'number',
  1161. showLabelTicks : false,
  1162. smooth : false,
  1163. autoAdjustLabels : true,
  1164. labelDistance : 100,
  1165. //the decimal place to round to if step is undefined
  1166. decimalPlaces : 2,
  1167. // page up/down multiplier. How many more times the steps to take on page up/down press
  1168. pageMultiplier : 2,
  1169. selector: {
  1170. },
  1171. className : {
  1172. reversed : 'reversed',
  1173. disabled : 'disabled',
  1174. labeled : 'labeled',
  1175. ticked : 'ticked',
  1176. vertical : 'vertical',
  1177. range : 'range',
  1178. smooth : 'smooth'
  1179. },
  1180. keys : {
  1181. pageUp : 33,
  1182. pageDown : 34,
  1183. leftArrow : 37,
  1184. upArrow : 38,
  1185. rightArrow : 39,
  1186. downArrow : 40
  1187. },
  1188. labelTypes : {
  1189. number : 'number',
  1190. letter : 'letter'
  1191. },
  1192. onChange : function(value, thumbVal, secondThumbVal){},
  1193. onMove : function(value, thumbVal, secondThumbVal){},
  1194. };
  1195. })( jQuery, window, document );