slider.js 45 KB

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