vendor: 5,162 bytes, lines 1-211
1/*! noUiSlider - 7.0.10 - 2014-12-27 14:50:46 */ 2 3(function(){ 4 5 'use strict'; 6 7var 8/** @const */ FormatOptions = [ 9 'decimals', 10 'thousand', 11 'mark', 12 'prefix', 13 'postfix', 14 'encoder', 15 'decoder', 16 'negativeBefore', 17 'negative', 18 'edit', 19 'undo' 20]; 21 22// General 23 24 // Reverse a string 25 function strReverse ( a ) { 26 return a.split('').reverse().join(''); 27 } 28 29 // Check if a string starts with a specified prefix. 30 function strStartsWith ( input, match ) { 31 return input.substring(0, match.length) === match; 32 } 33 34 // Check is a string ends in a specified postfix. 35 function strEndsWith ( input, match ) { 36 return input.slice(-1 * match.length) === match; 37 } 38 39 // Throw an error if formatting options are incompatible. 40 function throwEqualError( F, a, b ) { 41 if ( (F[a] || F[b]) && (F[a] === F[b]) ) { 42 throw new Error(a); 43 } 44 } 45 46 // Check if a number is finite and not NaN 47 function isValidNumber ( input ) { 48 return typeof input === 'number' && isFinite( input ); 49 } 50 51 // Provide rounding-accurate toFixed method. 52 function toFixed ( value, decimals ) { 53 var scale = Math.pow(10, decimals); 54 return ( Math.round(value * scale) / scale).toFixed( decimals ); 55 } 56 57 58// Formatting 59 60 // Accept a number as input, output formatted string. 61 function formatTo ( decimals, thousand, mark, prefix, postfix, encoder, decoder, negativeBefore, negative, edit, undo, input ) { 62 63 var originalInput = input, inputIsNegative, inputPieces, inputBase, inputDecimals = '', output = ''; 64 65 // Apply user encoder to the input. 66 // Expected outcome: number. 67 if ( encoder ) { 68 input = encoder(input); 69 } 70 71 // Stop if no valid number was provided, the number is infinite or NaN. 72 if ( !isValidNumber(input) ) { 73 return false; 74 } 75 76 // Rounding away decimals might cause a value of -0 77 // when using very small ranges. Remove those cases. 78 if ( decimals !== false && parseFloat(input.toFixed(decimals)) === 0 ) { 79 input = 0; 80 } 81 82 // Formatting is done on absolute numbers, 83 // decorated by an optional negative symbol. 84 if ( input < 0 ) { 85 inputIsNegative = true; 86 input = Math.abs(input); 87 } 88 89 // Reduce the number of decimals to the specified option. 90 if ( decimals !== false ) { 91 input = toFixed( input, decimals ); 92 } 93 94 // Transform the number into a string, so it can be split. 95 input = input.toString(); 96 97 // Break the number on the decimal separator. 98 if ( input.indexOf('.') !== -1 ) { 99 inputPieces = input.split('.'); 100 101 inputBase = inputPieces[0]; 102 103 if ( mark ) { 104 inputDecimals = mark + inputPieces[1]; 105 } 106 107 } else { 108 109 // If it isn't split, the entire number will do. 110 inputBase = input; 111 } 112 113 // Group numbers in sets of three. 114 if ( thousand ) { 115 inputBase = strReverse(inputBase).match(/.{1,3}/g); 116 inputBase = strReverse(inputBase.join( strReverse( thousand ) )); 117 } 118 119 // If the number is negative, prefix with negation symbol. 120 if ( inputIsNegative && negativeBefore ) { 121 output += negativeBefore; 122 } 123 124 // Prefix the number 125 if ( prefix ) { 126 output += prefix; 127 } 128 129 // Normal negative option comes after the prefix. Defaults to '-'. 130 if ( inputIsNegative && negative ) { 131 output += negative; 132 } 133 134 // Append the actual number. 135 output += inputBase; 136 output += inputDecimals; 137 138 // Apply the postfix. 139 if ( postfix ) { 140 output += postfix; 141 } 142 143 // Run the output through a user-specified post-formatter. 144 if ( edit ) { 145 output = edit ( output, originalInput ); 146 } 147 148 // All done. 149 return output; 150 } 151 152 // Accept a sting as input, output decoded number. 153 function formatFrom ( decimals, thousand, mark, prefix, postfix, encoder, decoder, negativeBefore, negative, edit, undo, input ) { 154 155 var originalInput = input, inputIsNegative, output = ''; 156 157 // User defined pre-decoder. Result must be a non empty string. 158 if ( undo ) { 159 input = undo(input); 160 } 161 162 // Test the input. Can't be empty. 163 if ( !input || typeof input !== 'string' ) { 164 return false; 165 } 166 167 // If the string starts with the negativeBefore value: remove it. 168 // Remember is was there, the number is negative. 169 if ( negativeBefore && strStartsWith(input, negativeBefore) ) { 170 input = input.replace(negativeBefore, ''); 171 inputIsNegative = true; 172 } 173 174 // Repeat the same procedure for the prefix. 175 if ( prefix && strStartsWith(input, prefix) ) { 176 input = input.replace(prefix, ''); 177 } 178 179 // And again for negative. 180 if ( negative && strStartsWith(input, negative) ) { 181 input = input.replace(negative, ''); 182 inputIsNegative = true; 183 } 184 185 // Remove the postfix. 186 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice 187 if ( postfix && strEndsWith(input, postfix) ) { 188 input = input.slice(0, -1 * postfix.length); 189 } 190 191 // Remove the thousand grouping. 192 if ( thousand ) { 193 input = input.split(thousand).join(''); 194 } 195 196 // Set the decimal separator back to period. 197 if ( mark ) { 198 input = input.replace(mark, '.'); 199 } 200 201 // Prepend the negative symbol. 202 if ( inputIsNegative ) { 203 output += '-'; 204 } 205 206 // Add the number 207 output += input; 208 209 // Trim all non-numeric characters (allow '.' and '-'); 210 output = output.replace(/[^0-9\.\-.]/g, ''); 211
212 // The value contains no parse-able number. 213 if ( output === '' ) { 214 return false; 215 } 216 217 // Covert to number. 218 output = Number(output); 219 220 // Run the user-specified post-decoder. 221 if ( decoder ) { 222 output = decoder(output); 223 } 224 225 // Check is the output is valid, otherwise: return false. 226 if ( !isValidNumber(output) ) { 227 return false; 228 } 229 230 return output; 231 } 232 233 234// Framework 235 236 // Validate formatting options 237 function validate ( inputOptions ) { 238 239 var i, optionName, optionValue, 240 filteredOptions = {}; 241 242 for ( i = 0; i < FormatOptions.length; i+=1 ) { 243 244 optionName = FormatOptions[i]; 245 optionValue = inputOptions[optionName]; 246 247 if ( optionValue === undefined ) { 248 249 // Only default if negativeBefore isn't set. 250 if ( optionName === 'negative' && !filteredOptions.negativeBefore ) { 251 filteredOptions[optionName] = '-'; 252 // Don't set a default for mark when 'thousand' is set. 253 } else if ( optionName === 'mark' && filteredOptions.thousand !== '.' ) { 254 filteredOptions[optionName] = '.'; 255 } else { 256 filteredOptions[optionName] = false; 257 } 258 259 // Floating points in JS are stable up to 7 decimals. 260 } else if ( optionName === 'decimals' ) { 261 if ( optionValue >= 0 && optionValue < 8 ) { 262 filteredOptions[optionName] = optionValue; 263 } else { 264 throw new Error(optionName); 265 } 266 267 // These options, when provided, must be functions. 268 } else if ( optionName === 'encoder' || optionName === 'decoder' || optionName === 'edit' || optionName === 'undo' ) { 269 if ( typeof optionValue === 'function' ) { 270 filteredOptions[optionName] = optionValue; 271 } else { 272 throw new Error(optionName); 273 } 274 275 // Other options are strings. 276 } else { 277 278 if ( typeof optionValue === 'string' ) { 279 filteredOptions[optionName] = optionValue; 280 } else { 281 throw new Error(optionName); 282 } 283 } 284 } 285 286 // Some values can't be extracted from a 287 // string if certain combinations are present. 288 throwEqualError(filteredOptions, 'mark', 'thousand'); 289 throwEqualError(filteredOptions, 'prefix', 'negative'); 290 throwEqualError(filteredOptions, 'prefix', 'negativeBefore'); 291 292 return filteredOptions; 293 } 294 295 // Pass all options as function arguments 296 function passAll ( options, method, input ) { 297 var i, args = []; 298 299 // Add all options in order of FormatOptions 300 for ( i = 0; i < FormatOptions.length; i+=1 ) { 301 args.push(options[FormatOptions[i]]); 302 } 303 304 // Append the input, then call the method, presenting all 305 // options as arguments. 306 args.push(input); 307 return method.apply('', args); 308 } 309 310 /** @constructor */ 311 function wNumb ( options ) { 312 313 if ( !(this instanceof wNumb) ) { 314 return new wNumb ( options ); 315 } 316 317 if ( typeof options !== "object" ) { 318 return; 319 } 320 321 options = validate(options); 322 323 // Call 'formatTo' with proper arguments. 324 this.to = function ( input ) { 325 return passAll(options, formatTo, input); 326 }; 327 328 // Call 'formatFrom' with proper arguments. 329 this.from = function ( input ) { 330 return passAll(options, formatFrom, input); 331 }; 332 } 333 334 /** @export */ 335 window.wNumb = wNumb; 336 337}()); 338 339/*jslint browser: true */ 340/*jslint white: true */ 341 342(function( $ ){ 343 344 'use strict'; 345 346// Helpers 347 348 // Test in an object is an instance of jQuery or Zepto. 349 function isInstance ( a ) { 350 return a instanceof $ || ( $.zepto && $.zepto.isZ(a) ); 351 } 352 353 354// Link types 355 356 function fromPrefix ( target, method ) { 357 358 // If target is a string, a new hidden input will be created. 359 if ( typeof target === 'string' && target.indexOf('-inline-') === 0 ) { 360 361 // By default, use the 'html' method. 362 this.method = method || 'html'; 363 364 // Use jQuery to create the element 365 this.target = this.el = $( target.replace('-inline-', '') || '<div/>' ); 366 367 return true; 368 } 369 } 370 371 function fromString ( target ) { 372 373 // If the string doesn't begin with '-', which is reserved, add a new hidden input. 374 if ( typeof target === 'string' && target.indexOf('-') !== 0 ) { 375 376 this.method = 'val'; 377 378 var element = document.createElement('input'); 379 element.name = target; 380 element.type = 'hidden'; 381 this.target = this.el = $(element); 382 383 return true; 384 } 385 } 386 387 function fromFunction ( target ) { 388 389 // The target can also be a function, which will be called. 390 if ( typeof target === 'function' ) { 391 this.target = false;
392 this.method = target; 393 394 return true; 395 } 396 } 397 398 function fromInstance ( target, method ) { 399 400 if ( isInstance( target ) && !method ) { 401 402 // If a jQuery/Zepto input element is provided, but no method is set, 403 // the element can assume it needs to respond to 'change'... 404 if ( target.is('input, select, textarea') ) { 405 406 // Default to .val if this is an input element. 407 this.method = 'val'; 408 409 // Fire the API changehandler when the target changes. 410 this.target = target.on('change.liblink', this.changeHandler); 411 412 } else { 413 414 this.target = target; 415 416 // If no method is set, and we are not auto-binding an input, default to 'html'. 417 this.method = 'html'; 418 } 419 420 return true; 421 } 422 } 423 424 function fromInstanceMethod ( target, method ) { 425 426 // The method must exist on the element. 427 if ( isInstance( target ) && 428 (typeof method === 'function' || 429 (typeof method === 'string' && target[method])) 430 ) { 431 this.method = method; 432 this.target = target; 433 434 return true; 435 } 436 } 437 438var 439/** @const */ 440 creationFunctions = [fromPrefix, fromString, fromFunction, fromInstance, fromInstanceMethod]; 441 442 443// Link Instance 444 445/** @constructor */ 446 function Link ( target, method, format ) { 447 448 var that = this, valid = false; 449 450 // Forward calls within scope. 451 this.changeHandler = function ( changeEvent ) { 452 var decodedValue = that.formatInstance.from( $(this).val() ); 453 454 // If the value is invalid, stop this event, as well as it's propagation. 455 if ( decodedValue === false || isNaN(decodedValue) ) { 456 457 // Reset the value. 458 $(this).val(that.lastSetValue); 459 return false; 460 } 461 462 that.changeHandlerMethod.call( '', changeEvent, decodedValue ); 463 }; 464 465 // See if this Link needs individual targets based on its usage. 466 // If so, return the element that needs to be copied by the 467 // implementing interface. 468 // Default the element to false. 469 this.el = false; 470 471 // Store the formatter, or use the default. 472 this.formatInstance = format; 473 474 // Try all Link types. 475 /*jslint unparam: true*/ 476 $.each(creationFunctions, function(i, fn){ 477 valid = fn.call(that, target, method); 478 return !valid; 479 }); 480 /*jslint unparam: false*/ 481 482 // Nothing matched, throw error. 483 if ( !valid ) { 484 throw new RangeError("(Link) Invalid Link."); 485 } 486 } 487 488 // Provides external items with the object value. 489 Link.prototype.set = function ( value ) { 490 491 // Ignore the value, so only the passed-on arguments remain. 492 var args = Array.prototype.slice.call( arguments ), 493 additionalArgs = args.slice(1); 494 495 // Store some values. The actual, numerical value, 496 // the formatted value and the parameters for use in 'resetValue'. 497 // Slice additionalArgs to break the relation. 498 this.lastSetValue = this.formatInstance.to( value ); 499 500 // Prepend the value to the function arguments. 501 additionalArgs.unshift( 502 this.lastSetValue 503 ); 504 505 // When target is undefined, the target was a function. 506 // In that case, provided the object as the calling scope. 507 // Branch between writing to a function or an object. 508 ( typeof this.method === 'function' ? 509 this.method : 510 this.target[ this.method ] ).apply( this.target, additionalArgs ); 511 }; 512 513 514// Developer API 515 516/** @constructor */ 517 function LinkAPI ( origin ) { 518 this.items = []; 519 this.elements = []; 520 this.origin = origin; 521 } 522 523 LinkAPI.prototype.push = function( item, element ) { 524 this.items.push(item); 525 526 // Prevent 'false' elements 527 if ( element ) { 528 this.elements.push(element); 529 } 530 }; 531 532 LinkAPI.prototype.reconfirm = function ( flag ) { 533 var i; 534 for ( i = 0; i < this.elements.length; i += 1 ) { 535 this.origin.LinkConfirm(flag, this.elements[i]); 536 } 537 }; 538 539 LinkAPI.prototype.remove = function ( flag ) { 540 var i; 541 for ( i = 0; i < this.items.length; i += 1 ) { 542 this.items[i].target.off('.liblink'); 543 } 544 for ( i = 0; i < this.elements.length; i += 1 ) { 545 this.elements[i].remove(); 546 } 547 }; 548 549 LinkAPI.prototype.change = function ( value ) { 550 551 if ( this.origin.LinkIsEmitting ) { 552 return false; 553 } 554 555 this.origin.LinkIsEmitting = true; 556 557 var args = Array.prototype.slice.call( arguments, 1 ), i; 558 args.unshift( value ); 559 560 // Write values to serialization Links. 561 // Convert the value to the correct relative representation. 562 for ( i = 0; i < this.items.length; i += 1 ) { 563 this.items[i].set.apply(this.items[i], args); 564 } 565 566 this.origin.LinkIsEmitting = false;
567 }; 568 569 570// jQuery plugin 571 572 function binder ( flag, target, method, format ){ 573 574 if ( flag === 0 ) { 575 flag = this.LinkDefaultFlag; 576 } 577 578 // Create a list of API's (if it didn't exist yet); 579 if ( !this.linkAPI ) { 580 this.linkAPI = {}; 581 } 582 583 // Add an API point. 584 if ( !this.linkAPI[flag] ) { 585 this.linkAPI[flag] = new LinkAPI(this); 586 } 587 588 var linkInstance = new Link ( target, method, format || this.LinkDefaultFormatter ); 589 590 // Default the calling scope to the linked object. 591 if ( !linkInstance.target ) { 592 linkInstance.target = $(this); 593 } 594 595 // If the Link requires creation of a new element, 596 // Pass the element and request confirmation to get the changehandler. 597 // Set the method to be called when a Link changes. 598 linkInstance.changeHandlerMethod = this.LinkConfirm( flag, linkInstance.el ); 599 600 // Store the linkInstance in the flagged list. 601 this.linkAPI[flag].push( linkInstance, linkInstance.el ); 602 603 // Now that Link have been connected, request an update. 604 this.LinkUpdate( flag ); 605 } 606 607 /** @export */ 608 $.fn.Link = function( flag ){ 609 610 var that = this; 611 612 // Delete all linkAPI 613 if ( flag === false ) { 614 615 return that.each(function(){ 616 617 // .Link(false) can be called on elements without Links. 618 // When that happens, the objects can't be looped. 619 if ( !this.linkAPI ) { 620 return; 621 } 622 623 $.map(this.linkAPI, function(api){ 624 api.remove(); 625 }); 626 627 delete this.linkAPI; 628 }); 629 } 630 631 if ( flag === undefined ) { 632 633 flag = 0; 634 635 } else if ( typeof flag !== 'string') { 636 637 throw new Error("Flag must be string."); 638 } 639 640 return { 641 to: function( a, b, c ){ 642 return that.each(function(){ 643 binder.call(this, flag, a, b, c); 644 }); 645 } 646 }; 647 }; 648 649}( window.jQuery || window.Zepto )); 650 651/*jslint browser: true */ 652/*jslint white: true */ 653 654(function( $ ){ 655 656 'use strict'; 657 658 659 // Removes duplicates from an array. 660 function unique(array) { 661 return $.grep(array, function(el, index) { 662 return index === $.inArray(el, array); 663 }); 664 } 665 666 // Round a value to the closest 'to'. 667 function closest ( value, to ) { 668 return Math.round(value / to) * to; 669 } 670 671 // Checks whether a value is numerical. 672 function isNumeric ( a ) { 673 return typeof a === 'number' && !isNaN( a ) && isFinite( a ); 674 } 675 676 // Rounds a number to 7 supported decimals. 677 function accurateNumber( number ) { 678 var p = Math.pow(10, 7); 679 return Number((Math.round(number*p)/p).toFixed(7)); 680 } 681 682 // Sets a class and removes it after [duration] ms. 683 function addClassFor ( element, className, duration ) { 684 element.addClass(className); 685 setTimeout(function(){ 686 element.removeClass(className); 687 }, duration); 688 } 689 690 // Limits a value to 0 - 100 691 function limit ( a ) { 692 return Math.max(Math.min(a, 100), 0); 693 } 694 695 // Wraps a variable as an array, if it isn't one yet. 696 function asArray ( a ) { 697 return $.isArray(a) ? a : [a]; 698 } 699 700 // Counts decimals 701 function countDecimals ( numStr ) { 702 var pieces = numStr.split("."); 703 return pieces.length > 1 ? pieces[1].length : 0; 704 } 705 706 707 var 708 // Cache the document selector; 709 /** @const */ 710 doc = $(document), 711 // Make a backup of the original jQuery/Zepto .val() method. 712 /** @const */ 713 $val = $.fn.val, 714 // Namespace for binding and unbinding slider events; 715 /** @const */ 716 namespace = '.nui', 717 // Determine the events to bind. IE11 implements pointerEvents without 718 // a prefix, which breaks compatibility with the IE10 implementation. 719 /** @const */ 720 actions = window.navigator.pointerEnabled ? { 721 start: 'pointerdown', 722 move: 'pointermove', 723 end: 'pointerup' 724 } : window.navigator.msPointerEnabled ? { 725 start: 'MSPointerDown', 726 move: 'MSPointerMove', 727 end: 'MSPointerUp' 728 } : { 729 start: 'mousedown touchstart', 730 move: 'mousemove touchmove', 731 end: 'mouseup touchend' 732 }, 733 // Re-usable list of classes; 734 /** @const */ 735 Classes = [ 736/* 0 */ 'noUi-target' 737/* 1 */ ,'noUi-base' 738/* 2 */ ,'noUi-origin' 739/* 3 */ ,'noUi-handle' 740/* 4 */ ,'noUi-horizontal' 741/* 5 */ ,'noUi-vertical' 742/* 6 */ ,'noUi-background' 743/* 7 */ ,'noUi-connect' 744/* 8 */ ,'noUi-ltr' 745/* 9 */ ,'noUi-rtl' 746/* 10 */ ,'noUi-dragable' 747/* 11 */ ,'' 748/* 12 */ ,'noUi-state-drag' 749/* 13 */ ,'' 750/* 14 */ ,'noUi-state-tap' 751/* 15 */ ,'noUi-active' 752/* 16 */ ,'' 753/* 17 */ ,'noUi-stacking' 754 ]; 755 756 757// Value calculation 758 759 // Determine the size of a sub-range in relation to a full range. 760 function subRangeRatio ( pa, pb ) { 761 return (100 / (pb - pa)); 762 } 763 764 // (percentage) How many percent is this value of this range? 765 function fromPercentage ( range, value ) { 766 return (value * 100) / ( range[1] - range[0] ); 767 } 768 769 // (percentage) Where is this value on this range? 770 function toPercentage ( range, value ) { 771 return fromPercentage( range, range[0] < 0 ? 772 value + Math.abs(range[0]) : 773 value - range[0] ); 774 } 775 776 // (value) How much is this percentage on this range? 777 function isPercentage ( range, value ) { 778 return ((value * ( range[1] - range[0] )) / 100) + range[0]; 779 } 780 781 782// Range conversion 783 784 function getJ ( value, arr ) { 785 786 var j = 1; 787 788 while ( value >= arr[j] ){ 789 j += 1; 790 } 791 792 return j; 793 } 794 795 // (percentage) Input a value, find where, on a scale of 0-100, it applies. 796 function toStepping ( xVal, xPct, value ) { 797 798 if ( value >= xVal.slice(-1)[0] ){ 799 return 100; 800 } 801 802 var j = getJ( value, xVal ), va, vb, pa, pb; 803 804 va = xVal[j-1]; 805 vb = xVal[j]; 806 pa = xPct[j-1]; 807 pb = xPct[j]; 808 809 return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); 810 } 811 812 // (value) Input a percentage, find where it is on the specified range. 813 function fromStepping ( xVal, xPct, value ) { 814 815 // There is no range group that fits 100 816 if ( value >= 100 ){ 817 return xVal.slice(-1)[0]; 818 } 819 820 var j = getJ( value, xPct ), va, vb, pa, pb; 821 822 va = xVal[j-1]; 823 vb = xVal[j]; 824 pa = xPct[j-1]; 825 pb = xPct[j]; 826 827 return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); 828 } 829 830 // (percentage) Get the step that applies at a certain value. 831 function getStep ( xPct, xSteps, snap, value ) { 832 833 if ( value === 100 ) { 834 return value; 835 } 836 837 var j = getJ( value, xPct ), a, b; 838 839 // If 'snap' is set, steps are used as fixed points on the slider. 840 if ( snap ) { 841 842 a = xPct[j-1]; 843 b = xPct[j]; 844 845 // Find the closest position, a or b. 846 if ((value - a) > ((b-a)/2)){ 847 return b; 848 } 849 850 return a; 851 } 852 853 if ( !xSteps[j-1] ){ 854 return value; 855 } 856 857 return xPct[j-1] + closest( 858 value - xPct[j-1], 859 xSteps[j-1] 860 ); 861 } 862 863 864// Entry parsing 865 866 function handleEntryPoint ( index, value, that ) { 867 868 var percentage; 869 870 // Wrap numerical input in an array.
871 if ( typeof value === "number" ) { 872 value = [value]; 873 } 874 875 // Reject any invalid input, by testing whether value is an array. 876 if ( Object.prototype.toString.call( value ) !== '[object Array]' ){ 877 throw new Error("noUiSlider: 'range' contains invalid value."); 878 } 879 880 // Covert min/max syntax to 0 and 100. 881 if ( index === 'min' ) { 882 percentage = 0; 883 } else if ( index === 'max' ) { 884 percentage = 100; 885 } else { 886 percentage = parseFloat( index ); 887 } 888 889 // Check for correct input. 890 if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) { 891 throw new Error("noUiSlider: 'range' value isn't numeric."); 892 } 893 894 // Store values. 895 that.xPct.push( percentage ); 896 that.xVal.push( value[0] ); 897 898 // NaN will evaluate to false too, but to keep 899 // logging clear, set step explicitly. Make sure 900 // not to override the 'step' setting with false. 901 if ( !percentage ) { 902 if ( !isNaN( value[1] ) ) { 903 that.xSteps[0] = value[1]; 904 } 905 } else { 906 that.xSteps.push( isNaN(value[1]) ? false : value[1] ); 907 } 908 } 909 910 function handleStepPoint ( i, n, that ) { 911 912 // Ignore 'false' stepping. 913 if ( !n ) { 914 return true; 915 } 916 917 // Factor to range ratio 918 that.xSteps[i] = fromPercentage([ 919 that.xVal[i] 920 ,that.xVal[i+1] 921 ], n) / subRangeRatio ( 922 that.xPct[i], 923 that.xPct[i+1] ); 924 } 925 926 927// Interface 928 929 // The interface to Spectrum handles all direction-based 930 // conversions, so the above values are unaware. 931 932 function Spectrum ( entry, snap, direction, singleStep ) { 933 934 this.xPct = []; 935 this.xVal = []; 936 this.xSteps = [ singleStep || false ]; 937 this.xNumSteps = [ false ]; 938 939 this.snap = snap; 940 this.direction = direction; 941 942 var index, ordered = [ /* [0, 'min'], [1, '50%'], [2, 'max'] */ ]; 943 944 // Map the object keys to an array. 945 for ( index in entry ) { 946 if ( entry.hasOwnProperty(index) ) { 947 ordered.push([entry[index], index]); 948 } 949 } 950 951 // Sort all entries by value (numeric sort). 952 ordered.sort(function(a, b) { return a[0] - b[0]; }); 953 954 // Convert all entries to subranges. 955 for ( index = 0; index < ordered.length; index++ ) { 956 handleEntryPoint(ordered[index][1], ordered[index][0], this); 957 } 958 959 // Store the actual step values. 960 // xSteps is sorted in the same order as xPct and xVal. 961 this.xNumSteps = this.xSteps.slice(0); 962 963 // Convert all numeric steps to the percentage of the subrange they represent. 964 for ( index = 0; index < this.xNumSteps.length; index++ ) { 965 handleStepPoint(index, this.xNumSteps[index], this); 966 } 967 } 968 969 Spectrum.prototype.getMargin = function ( value ) { 970 return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false; 971 }; 972 973 Spectrum.prototype.toStepping = function ( value ) { 974 975 value = toStepping( this.xVal, this.xPct, value ); 976 977 // Invert the value if this is a right-to-left slider. 978 if ( this.direction ) { 979 value = 100 - value; 980 } 981 982 return value; 983 }; 984 985 Spectrum.prototype.fromStepping = function ( value ) { 986 987 // Invert the value if this is a right-to-left slider. 988 if ( this.direction ) { 989 value = 100 - value; 990 } 991 992 return accurateNumber(fromStepping( this.xVal, this.xPct, value )); 993 }; 994 995 Spectrum.prototype.getStep = function ( value ) { 996 997 // Find the proper step for rtl sliders by search in inverse direction. 998 // Fixes issue #262. 999 if ( this.direction ) { 1000 value = 100 - value; 1001 } 1002 1003 value = getStep(this.xPct, this.xSteps, this.snap, value ); 1004 1005 if ( this.direction ) { 1006 value = 100 - value; 1007 } 1008 1009 return value; 1010 }; 1011 1012 Spectrum.prototype.getApplicableStep = function ( value ) { 1013 1014 // If the value is 100%, return the negative step twice. 1015 var j = getJ(value, this.xPct), offset = value === 100 ? 2 : 1; 1016 return [this.xNumSteps[j-2], this.xVal[j-offset], this.xNumSteps[j-offset]]; 1017 }; 1018 1019 // Outside testing 1020 Spectrum.prototype.convert = function ( value ) { 1021 return this.getStep(this.toStepping(value)); 1022 }; 1023 1024/* Every input option is tested and parsed. This'll prevent 1025 endless validation in internal methods. These tests are 1026 structured with an item for every option available. An 1027 option can be marked as required by setting the 'r' flag. 1028 The testing function is provided with three arguments: 1029 - The provided value for the option; 1030 - A reference to the options object; 1031 - The name for the option; 1032 1033 The testing function returns false when an error is detected, 1034 or true when everything is OK. It can also modify the option 1035 object, to make sure all values can be correctly looped elsewhere. */ 1036 1037 /** @const */ 1038 var defaultFormatter = { 'to': function( value ){ 1039 return value.toFixed(2); 1040 }, 'from': Number }; 1041 1042 function testStep ( parsed, entry ) { 1043 1044 if ( !isNumeric( entry ) ) { 1045 throw new Error("noUiSlider: 'step' is not numeric."); 1046 } 1047 1048 // The step option can still be used to set stepping 1049 // for linear sliders. Overwritten if set in 'range'. 1050 parsed.singleStep = entry; 1051 } 1052 1053 function testRange ( parsed, entry ) { 1054 1055 // Filter incorrect input. 1056 if ( typeof entry !== 'object' || $.isArray(entry) ) { 1057 throw new Error("noUiSlider: 'range' is not an object."); 1058 } 1059 1060 // Catch missing start or end. 1061 if ( entry.min === undefined || entry.max === undefined ) { 1062 throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); 1063 } 1064 1065 parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); 1066 } 1067 1068 function testStart ( parsed, entry ) { 1069 1070 entry = asArray(entry); 1071 1072 // Validate input. Values aren't tested, as the public .val method 1073 // will always provide a valid location. 1074 if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) { 1075 throw new Error("noUiSlider: 'start' option is incorrect."); 1076 } 1077 1078 // Store the number of handles. 1079 parsed.handles = entry.length; 1080
1081 // When the slider is initialized, the .val method will 1082 // be called with the start options. 1083 parsed.start = entry; 1084 } 1085 1086 function testSnap ( parsed, entry ) { 1087 1088 // Enforce 100% stepping within subranges. 1089 parsed.snap = entry; 1090 1091 if ( typeof entry !== 'boolean' ){ 1092 throw new Error("noUiSlider: 'snap' option must be a boolean."); 1093 } 1094 } 1095 1096 function testAnimate ( parsed, entry ) { 1097 1098 // Enforce 100% stepping within subranges. 1099 parsed.animate = entry; 1100 1101 if ( typeof entry !== 'boolean' ){ 1102 throw new Error("noUiSlider: 'animate' option must be a boolean."); 1103 } 1104 } 1105 1106 function testConnect ( parsed, entry ) { 1107 1108 if ( entry === 'lower' && parsed.handles === 1 ) { 1109 parsed.connect = 1; 1110 } else if ( entry === 'upper' && parsed.handles === 1 ) { 1111 parsed.connect = 2; 1112 } else if ( entry === true && parsed.handles === 2 ) { 1113 parsed.connect = 3; 1114 } else if ( entry === false ) { 1115 parsed.connect = 0; 1116 } else { 1117 throw new Error("noUiSlider: 'connect' option doesn't match handle count."); 1118 } 1119 } 1120 1121 function testOrientation ( parsed, entry ) { 1122 1123 // Set orientation to an a numerical value for easy 1124 // array selection. 1125 switch ( entry ){ 1126 case 'horizontal': 1127 parsed.ort = 0; 1128 break; 1129 case 'vertical': 1130 parsed.ort = 1; 1131 break; 1132 default: 1133 throw new Error("noUiSlider: 'orientation' option is invalid."); 1134 } 1135 } 1136 1137 function testMargin ( parsed, entry ) { 1138 1139 if ( !isNumeric(entry) ){ 1140 throw new Error("noUiSlider: 'margin' option must be numeric."); 1141 } 1142 1143 parsed.margin = parsed.spectrum.getMargin(entry); 1144 1145 if ( !parsed.margin ) { 1146 throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); 1147 } 1148 } 1149 1150 function testLimit ( parsed, entry ) { 1151 1152 if ( !isNumeric(entry) ){ 1153 throw new Error("noUiSlider: 'limit' option must be numeric."); 1154 } 1155 1156 parsed.limit = parsed.spectrum.getMargin(entry); 1157 1158 if ( !parsed.limit ) { 1159 throw new Error("noUiSlider: 'limit' option is only supported on linear sliders."); 1160 } 1161 } 1162 1163 function testDirection ( parsed, entry ) { 1164 1165 // Set direction as a numerical value for easy parsing. 1166 // Invert connection for RTL sliders, so that the proper 1167 // handles get the connect/background classes. 1168 switch ( entry ) { 1169 case 'ltr': 1170 parsed.dir = 0; 1171 break; 1172 case 'rtl': 1173 parsed.dir = 1; 1174 parsed.connect = [0,2,1,3][parsed.connect]; 1175 break; 1176 default: 1177 throw new Error("noUiSlider: 'direction' option was not recognized."); 1178 } 1179 } 1180 1181 function testBehaviour ( parsed, entry ) { 1182 1183 // Make sure the input is a string. 1184 if ( typeof entry !== 'string' ) { 1185 throw new Error("noUiSlider: 'behaviour' must be a string containing options."); 1186 } 1187 1188 // Check if the string contains any keywords. 1189 // None are required. 1190 var tap = entry.indexOf('tap') >= 0, 1191 drag = entry.indexOf('drag') >= 0, 1192 fixed = entry.indexOf('fixed') >= 0, 1193 snap = entry.indexOf('snap') >= 0; 1194 1195 parsed.events = { 1196 tap: tap || snap, 1197 drag: drag, 1198 fixed: fixed, 1199 snap: snap 1200 }; 1201 } 1202 1203 function testFormat ( parsed, entry ) { 1204 1205 parsed.format = entry; 1206 1207 // Any object with a to and from method is supported. 1208 if ( typeof entry.to === 'function' && typeof entry.from === 'function' ) { 1209 return true; 1210 } 1211 1212 throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods."); 1213 } 1214 1215 // Test all developer settings and parse to assumption-safe values. 1216 function testOptions ( options ) { 1217 1218 var parsed = { 1219 margin: 0, 1220 limit: 0, 1221 animate: true, 1222 format: defaultFormatter 1223 }, tests; 1224 1225 // Tests are executed in the order they are presented here. 1226 tests = { 1227 'step': { r: false, t: testStep }, 1228 'start': { r: true, t: testStart }, 1229 'connect': { r: true, t: testConnect }, 1230 'direction': { r: true, t: testDirection }, 1231 'snap': { r: false, t: testSnap }, 1232 'animate': { r: false, t: testAnimate }, 1233 'range': { r: true, t: testRange }, 1234 'orientation': { r: false, t: testOrientation }, 1235 'margin': { r: false, t: testMargin }, 1236 'limit': { r: false, t: testLimit }, 1237 'behaviour': { r: true, t: testBehaviour }, 1238 'format': { r: false, t: testFormat } 1239 }; 1240 1241 // Set defaults where applicable. 1242 options = $.extend({ 1243 'connect': false, 1244 'direction': 'ltr', 1245 'behaviour': 'tap', 1246 'orientation': 'horizontal' 1247 }, options); 1248 1249 // Run all options through a testing mechanism to ensure correct 1250 // input. It should be noted that options might get modified to 1251 // be handled properly. E.g. wrapping integers in arrays. 1252 $.each( tests, function( name, test ){ 1253 1254 // If the option isn't set, but it is required, throw an error. 1255 if ( options[name] === undefined ) { 1256 1257 if ( test.r ) { 1258 throw new Error("noUiSlider: '" + name + "' is required."); 1259 } 1260 1261 return true; 1262 } 1263 1264 test.t( parsed, options[name] ); 1265 }); 1266 1267 // Pre-define the styles. 1268 parsed.style = parsed.ort ? 'top' : 'left'; 1269 1270 return parsed; 1271 } 1272 1273// Class handling 1274 1275 // Delimit proposed values for handle positions. 1276 function getPositions ( a, b, delimit ) { 1277 1278 // Add movement to current position. 1279 var c = a + b[0], d = a + b[1]; 1280 1281 // Only alter the other position on drag, 1282 // not on standard sliding. 1283 if ( delimit ) { 1284 if ( c < 0 ) { 1285 d += Math.abs(c); 1286 } 1287 if ( d > 100 ) { 1288 c -= ( d - 100 ); 1289 } 1290 1291 // Limit values to 0 and 100. 1292 return [limit(c), limit(d)]; 1293 } 1294 1295 return [c,d]; 1296 } 1297 1298 1299// Event handling 1300 1301 // Provide a clean event with standardized offset values. 1302 function fixEvent ( e ) { 1303 1304 // Prevent scrolling and panning on touch events, while
1305 // attempting to slide. The tap event also depends on this. 1306 e.preventDefault(); 1307 1308 // Filter the event to register the type, which can be 1309 // touch, mouse or pointer. Offset changes need to be 1310 // made on an event specific basis. 1311 var touch = e.type.indexOf('touch') === 0 1312 ,mouse = e.type.indexOf('mouse') === 0 1313 ,pointer = e.type.indexOf('pointer') === 0 1314 ,x,y, event = e; 1315 1316 // IE10 implemented pointer events with a prefix; 1317 if ( e.type.indexOf('MSPointer') === 0 ) { 1318 pointer = true; 1319 } 1320 1321 // Get the originalEvent, if the event has been wrapped 1322 // by jQuery. Zepto doesn't wrap the event. 1323 if ( e.originalEvent ) { 1324 e = e.originalEvent; 1325 } 1326 1327 if ( touch ) { 1328 // noUiSlider supports one movement at a time, 1329 // so we can select the first 'changedTouch'. 1330 x = e.changedTouches[0].pageX; 1331 y = e.changedTouches[0].pageY; 1332 } 1333 1334 if ( mouse || pointer ) { 1335 1336 // Polyfill the pageXOffset and pageYOffset 1337 // variables for IE7 and IE8; 1338 if( !pointer && window.pageXOffset === undefined ){ 1339 window.pageXOffset = document.documentElement.scrollLeft; 1340 window.pageYOffset = document.documentElement.scrollTop; 1341 } 1342 1343 x = e.clientX + window.pageXOffset; 1344 y = e.clientY + window.pageYOffset; 1345 } 1346 1347 event.points = [x, y]; 1348 event.cursor = mouse; 1349 1350 return event; 1351 } 1352 1353 1354// DOM additions 1355 1356 // Append a handle to the base. 1357 function addHandle ( direction, index ) { 1358 1359 var handle = $('<div><div/></div>').addClass( Classes[2] ), 1360 additions = [ '-lower', '-upper' ]; 1361 1362 if ( direction ) { 1363 additions.reverse(); 1364 } 1365 1366 handle.children().addClass( 1367 Classes[3] + " " + Classes[3]+additions[index] 1368 ); 1369 1370 return handle; 1371 } 1372 1373 // Add the proper connection classes. 1374 function addConnection ( connect, target, handles ) { 1375 1376 // Apply the required connection classes to the elements 1377 // that need them. Some classes are made up for several 1378 // segments listed in the class list, to allow easy 1379 // renaming and provide a minor compression benefit. 1380 switch ( connect ) { 1381 case 1: target.addClass( Classes[7] ); 1382 handles[0].addClass( Classes[6] ); 1383 break; 1384 case 3: handles[1].addClass( Classes[6] ); 1385 /* falls through */ 1386 case 2: handles[0].addClass( Classes[7] ); 1387 /* falls through */ 1388 case 0: target.addClass(Classes[6]); 1389 break; 1390 } 1391 } 1392 1393 // Add handles to the slider base. 1394 function addHandles ( nrHandles, direction, base ) { 1395 1396 var index, handles = []; 1397 1398 // Append handles. 1399 for ( index = 0; index < nrHandles; index += 1 ) { 1400 1401 // Keep a list of all added handles. 1402 handles.push( addHandle( direction, index ).appendTo(base) ); 1403 } 1404 1405 return handles; 1406 } 1407 1408 // Initialize a single slider. 1409 function addSlider ( direction, orientation, target ) { 1410 1411 // Apply classes and data to the target. 1412 target.addClass([ 1413 Classes[0], 1414 Classes[8 + direction], 1415 Classes[4 + orientation] 1416 ].join(' ')); 1417 1418 return $('<div/>').appendTo(target).addClass( Classes[1] ); 1419 } 1420 1421function closure ( target, options, originalOptions ){ 1422 1423// Internal variables 1424 1425 // All variables local to 'closure' are marked $. 1426 var $Target = $(target), 1427 $Locations = [-1, -1], 1428 $Base, 1429 $Handles, 1430 $Spectrum = options.spectrum, 1431 $Values = [], 1432 // libLink. For rtl sliders, 'lower' and 'upper' should not be inverted 1433 // for one-handle sliders, so trim 'upper' it that case. 1434 triggerPos = ['lower', 'upper'].slice(0, options.handles); 1435 1436 // Invert the libLink connection for rtl sliders. 1437 if ( options.dir ) { 1438 triggerPos.reverse(); 1439 } 1440 1441// Helpers 1442 1443 // Shorthand for base dimensions. 1444 function baseSize ( ) { 1445 return $Base[['width', 'height'][options.ort]](); 1446 } 1447 1448 // External event handling 1449 function fireEvents ( events ) { 1450 1451 // Use the external api to get the values. 1452 // Wrap the values in an array, as .trigger takes 1453 // only one additional argument. 1454 var index, values = [ $Target.val() ]; 1455 1456 for ( index = 0; index < events.length; index += 1 ){ 1457 $Target.trigger(events[index], values); 1458 } 1459 } 1460 1461 // Returns the input array, respecting the slider direction configuration. 1462 function inSliderOrder ( values ) { 1463 1464 // If only one handle is used, return a single value. 1465 if ( values.length === 1 ){ 1466 return values[0]; 1467 } 1468 1469 if ( options.dir ) { 1470 return values.reverse(); 1471 } 1472 1473 return values; 1474 } 1475 1476// libLink integration 1477 1478 // Create a new function which calls .val on input change. 1479 function createChangeHandler ( trigger ) { 1480 return function ( ignore, value ){ 1481 // Determine which array position to 'null' based on 'trigger'. 1482 $Target.val( [ trigger ? null : value, trigger ? value : null ], true ); 1483 }; 1484 } 1485 1486 // Called by libLink when it wants a set of links updated. 1487 function linkUpdate ( flag ) { 1488 1489 var trigger = $.inArray(flag, triggerPos); 1490 1491 // The API might not have been set yet. 1492 if ( $Target[0].linkAPI && $Target[0].linkAPI[flag] ) { 1493 $Target[0].linkAPI[flag].change( 1494 $Values[trigger], 1495 $Handles[trigger].children(), 1496 $Target 1497 ); 1498 } 1499 } 1500 1501 // Called by libLink to append an element to the slider. 1502 function linkConfirm ( flag, element ) { 1503 1504 // Find the trigger for the passed flag. 1505 var trigger = $.inArray(flag, triggerPos); 1506 1507 // If set, append the element to the handle it belongs to. 1508 if ( element ) { 1509 element.appendTo( $Handles[trigger].children() ); 1510 } 1511 1512 // The public API is reversed for rtl sliders, so the changeHandler 1513 // should not be aware of the inverted trigger positions. 1514 // On rtl slider with one handle, 'lower' should be used. 1515 if ( options.dir && options.handles > 1 ) { 1516 trigger = trigger === 1 ? 0 : 1; 1517 } 1518 1519 return createChangeHandler( trigger ); 1520 } 1521 1522 // Place elements back on the slider. 1523 function reAppendLink ( ) { 1524 1525 var i, flag; 1526 1527 // The API keeps a list of elements: we can re-append them on rebuild. 1528 for ( i = 0; i < triggerPos.length; i += 1 ) { 1529 if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { 1530 this.linkAPI[flag].reconfirm(flag); 1531 } 1532 } 1533 } 1534 1535 target.LinkUpdate = linkUpdate; 1536 target.LinkConfirm = linkConfirm; 1537 target.LinkDefaultFormatter = options.format; 1538 target.LinkDefaultFlag = 'lower'; 1539
1540 target.reappend = reAppendLink; 1541 1542 1543 // Handler for attaching events trough a proxy. 1544 function attach ( events, element, callback, data ) { 1545 1546 // This function can be used to 'filter' events to the slider. 1547 1548 // Add the noUiSlider namespace to all events. 1549 events = events.replace( /\s/g, namespace + ' ' ) + namespace; 1550 1551 // Bind a closure on the target. 1552 return element.on( events, function( e ){ 1553 1554 // jQuery and Zepto (1) handle unset attributes differently, 1555 // but always falsy; #208 1556 if ( !!$Target.attr('disabled') ) { 1557 return false; 1558 } 1559 1560 // Stop if an active 'tap' transition is taking place. 1561 if ( $Target.hasClass( Classes[14] ) ) { 1562 return false; 1563 } 1564 1565 e = fixEvent(e); 1566 e.calcPoint = e.points[ options.ort ]; 1567 1568 // Call the event handler with the event [ and additional data ]. 1569 callback ( e, data ); 1570 }); 1571 } 1572 1573 // Handle movement on document for handle and range drag. 1574 function move ( event, data ) { 1575 1576 var handles = data.handles || $Handles, positions, state = false, 1577 proposal = ((event.calcPoint - data.start) * 100) / baseSize(), 1578 h = handles[0][0] !== $Handles[0][0] ? 1 : 0; 1579 1580 // Calculate relative positions for the handles. 1581 positions = getPositions( proposal, data.positions, handles.length > 1); 1582 1583 state = setHandle ( handles[0], positions[h], handles.length === 1 ); 1584 1585 if ( handles.length > 1 ) { 1586 state = setHandle ( handles[1], positions[h?0:1], false ) || state; 1587 } 1588 1589 // Fire the 'slide' event if any handle moved. 1590 if ( state ) { 1591 fireEvents(['slide']); 1592 } 1593 } 1594 1595 // Unbind move events on document, call callbacks. 1596 function end ( event ) { 1597 1598 // The handle is no longer active, so remove the class. 1599 $('.' + Classes[15]).removeClass(Classes[15]); 1600 1601 // Remove cursor styles and text-selection events bound to the body. 1602 if ( event.cursor ) { 1603 $('body').css('cursor', '').off( namespace ); 1604 } 1605 1606 // Unbind the move and end events, which are added on 'start'. 1607 doc.off( namespace ); 1608 1609 // Remove dragging class. 1610 $Target.removeClass(Classes[12]); 1611 1612 // Fire the change and set events. 1613 fireEvents(['set', 'change']); 1614 } 1615 1616 // Bind move events on document. 1617 function start ( event, data ) { 1618 1619 // Mark the handle as 'active' so it can be styled. 1620 if( data.handles.length === 1 ) { 1621 data.handles[0].children().addClass(Classes[15]); 1622 } 1623 1624 // A drag should never propagate up to the 'tap' event. 1625 event.stopPropagation(); 1626 1627 // Attach the move event. 1628 attach ( actions.move, doc, move, { 1629 start: event.calcPoint, 1630 handles: data.handles, 1631 positions: [ 1632 $Locations[0], 1633 $Locations[$Handles.length - 1] 1634 ] 1635 }); 1636 1637 // Unbind all movement when the drag ends. 1638 attach ( actions.end, doc, end, null ); 1639 1640 // Text selection isn't an issue on touch devices, 1641 // so adding cursor styles can be skipped. 1642 if ( event.cursor ) { 1643 1644 // Prevent the 'I' cursor and extend the range-drag cursor. 1645 $('body').css('cursor', $(event.target).css('cursor')); 1646 1647 // Mark the target with a dragging state. 1648 if ( $Handles.length > 1 ) { 1649 $Target.addClass(Classes[12]); 1650 } 1651 1652 // Prevent text selection when dragging the handles. 1653 $('body').on('selectstart' + namespace, false); 1654 } 1655 } 1656 1657 // Move closest handle to tapped location. 1658 function tap ( event ) { 1659 1660 var location = event.calcPoint, total = 0, to; 1661 1662 // The tap event shouldn't propagate up and cause 'edge' to run. 1663 event.stopPropagation(); 1664 1665 // Add up the handle offsets. 1666 $.each( $Handles, function(){ 1667 total += this.offset()[ options.style ]; 1668 }); 1669 1670 // Find the handle closest to the tapped position. 1671 total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1; 1672 1673 location -= $Base.offset()[ options.style ]; 1674 1675 // Calculate the new position. 1676 to = ( location * 100 ) / baseSize(); 1677 1678 if ( !options.events.snap ) { 1679 // Flag the slider as it is now in a transitional state. 1680 // Transition takes 300 ms, so re-enable the slider afterwards. 1681 addClassFor( $Target, Classes[14], 300 ); 1682 } 1683 1684 // Find the closest handle and calculate the tapped point. 1685 // The set handle to the new position. 1686 setHandle( $Handles[total], to ); 1687 1688 fireEvents(['slide', 'set', 'change']); 1689 1690 if ( options.events.snap ) { 1691 start(event, { handles: [$Handles[total]] }); 1692 } 1693 } 1694 1695 // Attach events to several slider parts. 1696 function events ( behaviour ) { 1697 1698 var i, drag; 1699 1700 // Attach the standard drag event to the handles. 1701 if ( !behaviour.fixed ) { 1702 1703 for ( i = 0; i < $Handles.length; i += 1 ) { 1704 1705 // These events are only bound to the visual handle 1706 // element, not the 'real' origin element. 1707 attach ( actions.start, $Handles[i].children(), start, { 1708 handles: [ $Handles[i] ] 1709 }); 1710 } 1711 } 1712 1713 // Attach the tap event to the slider base. 1714 if ( behaviour.tap ) { 1715 1716 attach ( actions.start, $Base, tap, { 1717 handles: $Handles 1718 }); 1719 } 1720 1721 // Make the range dragable. 1722 if ( behaviour.drag ){ 1723 1724 drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] ); 1725 1726 // When the range is fixed, the entire range can 1727 // be dragged by the handles. The handle in the first 1728 // origin will propagate the start event upward, 1729 // but it needs to be bound manually on the other. 1730 if ( behaviour.fixed ) { 1731 drag = drag.add($Base.children().not( drag ).children()); 1732 } 1733 1734 attach ( actions.start, drag, start, { 1735 handles: $Handles 1736 }); 1737 } 1738 } 1739 1740 1741 // Test suggested values and apply margin, step. 1742 function setHandle ( handle, to, noLimitOption ) { 1743 1744 var trigger = handle[0] !== $Handles[0][0] ? 1 : 0, 1745 lowerMargin = $Locations[0] + options.margin, 1746 upperMargin = $Locations[1] - options.margin, 1747 lowerLimit = $Locations[0] + options.limit, 1748 upperLimit = $Locations[1] - options.limit; 1749 1750 // For sliders with multiple handles, 1751 // limit movement to the other handle. 1752 // Apply the margin option by adding it to the handle positions. 1753 if ( $Handles.length > 1 ) { 1754 to = trigger ? Math.max( to, lowerMargin ) : Math.min( to, upperMargin ); 1755 } 1756 1757 // The limit option has the opposite effect, limiting handles to a 1758 // maximum distance from another. Limit must be > 0, as otherwise 1759 // handles would be unmoveable. 'noLimitOption' is set to 'false' 1760 // for the .val() method, except for pass 4/4. 1761 if ( noLimitOption !== false && options.limit && $Handles.length > 1 ) { 1762 to = trigger ? Math.min ( to, lowerLimit ) : Math.max( to, upperLimit ); 1763 } 1764 1765 // Handle the step option. 1766 to = $Spectrum.getStep( to ); 1767 1768 // Limit to 0/100 for .val input, trim anything beyond 7 digits, as 1769 // JavaScript has some issues in its floating point implementation. 1770 to = limit(parseFloat(to.toFixed(7))); 1771 1772 // Return false if handle can't move. 1773 if ( to === $Locations[trigger] ) { 1774 return false;
1775 } 1776 1777 // Set the handle to the new position. 1778 handle.css( options.style, to + '%' ); 1779 1780 // Force proper handle stacking 1781 if ( handle.is(':first-child') ) { 1782 handle.toggleClass(Classes[17], to > 50 ); 1783 } 1784 1785 // Update locations. 1786 $Locations[trigger] = to; 1787 1788 // Convert the value to the slider stepping/range. 1789 $Values[trigger] = $Spectrum.fromStepping( to ); 1790 1791 linkUpdate(triggerPos[trigger]); 1792 1793 return true; 1794 } 1795 1796 // Loop values from value method and apply them. 1797 function setValues ( count, values ) { 1798 1799 var i, trigger, to; 1800 1801 // With the limit option, we'll need another limiting pass. 1802 if ( options.limit ) { 1803 count += 1; 1804 } 1805 1806 // If there are multiple handles to be set run the setting 1807 // mechanism twice for the first handle, to make sure it 1808 // can be bounced of the second one properly. 1809 for ( i = 0; i < count; i += 1 ) { 1810 1811 trigger = i%2; 1812 1813 // Get the current argument from the array. 1814 to = values[trigger]; 1815 1816 // Setting with null indicates an 'ignore'. 1817 // Inputting 'false' is invalid. 1818 if ( to !== null && to !== false ) { 1819 1820 // If a formatted number was passed, attemt to decode it. 1821 if ( typeof to === 'number' ) { 1822 to = String(to); 1823 } 1824 1825 to = options.format.from( to ); 1826
1827 // Request an update for all links if the value was invalid. 1828 // Do so too if setting the handle fails. 1829 if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ), i === (3 - options.dir) ) === false ) { 1830 1831 linkUpdate(triggerPos[trigger]); 1832 } 1833 } 1834 } 1835 } 1836 1837 // Set the slider value. 1838 function valueSet ( input ) { 1839 1840 // LibLink: don't accept new values when currently emitting changes. 1841 if ( $Target[0].LinkIsEmitting ) { 1842 return this; 1843 } 1844 1845 var count, values = asArray( input ); 1846 1847 // The RTL settings is implemented by reversing the front-end, 1848 // internal mechanisms are the same. 1849 if ( options.dir && options.handles > 1 ) { 1850 values.reverse(); 1851 } 1852 1853 // Animation is optional. 1854 // Make sure the initial values where set before using animated 1855 // placement. (no report, unit testing); 1856 if ( options.animate && $Locations[0] !== -1 ) { 1857 addClassFor( $Target, Classes[14], 300 ); 1858 } 1859 1860 // Determine how often to set the handles. 1861 count = $Handles.length > 1 ? 3 : 1; 1862 1863 if ( values.length === 1 ) { 1864 count = 1; 1865 } 1866 1867 setValues ( count, values ); 1868 1869 // Fire the 'set' event. As of noUiSlider 7, 1870 // this is no longer optional. 1871 fireEvents(['set']); 1872 1873 return this; 1874 } 1875 1876 // Get the slider value. 1877 function valueGet ( ) { 1878 1879 var i, retour = []; 1880 1881 // Get the value from all handles. 1882 for ( i = 0; i < options.handles; i += 1 ){ 1883 retour[i] = options.format.to( $Values[i] ); 1884 } 1885 1886 return inSliderOrder( retour ); 1887 } 1888 1889 // Destroy the slider and unbind all events. 1890 function destroyTarget ( ) { 1891 1892 // Unbind events on the slider, remove all classes and child elements. 1893 $(this).off(namespace) 1894 .removeClass(Classes.join(' ')) 1895 .empty(); 1896 1897 delete this.LinkUpdate; 1898 delete this.LinkConfirm; 1899 delete this.LinkDefaultFormatter; 1900 delete this.LinkDefaultFlag; 1901 delete this.reappend; 1902 delete this.vGet; 1903 delete this.vSet; 1904 delete this.getCurrentStep; 1905 delete this.getInfo; 1906 delete this.destroy; 1907 1908 // Return the original options from the closure. 1909 return originalOptions; 1910 } 1911 1912 // Get the current step size for the slider. 1913 function getCurrentStep ( ) { 1914 1915 // Check all locations, map them to their stepping point. 1916 // Get the step point, then find it in the input list. 1917 var retour = $.map($Locations, function( location, index ){ 1918 1919 var step = $Spectrum.getApplicableStep( location ), 1920 1921 // As per #391, the comparison for the decrement step can have some rounding issues. 1922 // Round the value to the precision used in the step. 1923 stepDecimals = countDecimals(String(step[2])), 1924 1925 // Get the current numeric value 1926 value = $Values[index], 1927 1928 // To move the slider 'one step up', the current step value needs to be added. 1929 // Use null if we are at the maximum slider value. 1930 increment = location === 100 ? null : step[2], 1931 1932 // Going 'one step down' might put the slider in a different sub-range, so we 1933 // need to switch between the current or the previous step. 1934 prev = Number((value - step[2]).toFixed(stepDecimals)), 1935 1936 // If the value fits the step, return the current step value. Otherwise, use the 1937 // previous step. Return null if the slider is at its minimum value. 1938 decrement = location === 0 ? null : (prev >= step[1]) ? step[2] : (step[0] || false); 1939 1940 return [[decrement, increment]]; 1941 }); 1942 1943 // Return values in the proper order. 1944 return inSliderOrder( retour ); 1945 } 1946 1947 // Get the original set of options. 1948 function getOriginalOptions ( ) { 1949 return originalOptions; 1950 } 1951 1952 1953// Initialize slider 1954 1955 // Throw an error if the slider was already initialized. 1956 if ( $Target.hasClass(Classes[0]) ) { 1957 throw new Error('Slider was already initialized.'); 1958 } 1959 1960 // Create the base element, initialise HTML and set classes. 1961 // Add handles and links. 1962 $Base = addSlider( options.dir, options.ort, $Target ); 1963 $Handles = addHandles( options.handles, options.dir, $Base ); 1964 1965 // Set the connect classes. 1966 addConnection ( options.connect, $Target, $Handles ); 1967 1968 // Attach user events. 1969 events( options.events ); 1970 1971// Methods 1972 1973 target.vSet = valueSet; 1974 target.vGet = valueGet; 1975 target.destroy = destroyTarget; 1976 1977 target.getCurrentStep = getCurrentStep; 1978 target.getOriginalOptions = getOriginalOptions; 1979 1980 target.getInfo = function(){ 1981 return [ 1982 $Spectrum, 1983 options.style, 1984 options.ort 1985 ]; 1986 }; 1987 1988 // Use the public value method to set the start values. 1989 $Target.val( options.start ); 1990 1991} 1992 1993 1994 // Run the standard initializer 1995 function initialize ( originalOptions ) { 1996 1997 // Test the options once, not for every slider. 1998 var options = testOptions( originalOptions, this ); 1999 2000 // Loop all items, and provide a new closed-scope environment. 2001 return this.each(function(){ 2002 closure(this, options, originalOptions); 2003 }); 2004 } 2005 2006 // Destroy the slider, then re-enter initialization. 2007 function rebuild ( options ) { 2008 2009 return this.each(function(){ 2010 2011 // The rebuild flag can be used if the slider wasn't initialized yet. 2012 if ( !this.destroy ) { 2013 $(this).noUiSlider( options ); 2014 return; 2015 } 2016 2017 // Get the current values from the slider, 2018 // including the initialization options. 2019 var values = $(this).val(), originalOptions = this.destroy(), 2020 2021 // Extend the previous options with the newly provided ones. 2022 newOptions = $.extend( {}, originalOptions, options ); 2023 2024 // Run the standard initializer. 2025 $(this).noUiSlider( newOptions ); 2026 2027 // Place Link elements back. 2028 this.reappend(); 2029 2030 // If the start option hasn't changed, 2031 // reset the previous values. 2032 if ( originalOptions.start === newOptions.start ) { 2033 $(this).val(values); 2034 } 2035 }); 2036 } 2037 2038 // Access the internal getting and setting methods based on argument count. 2039 function value ( ) { 2040 return this[0][ !arguments.length ? 'vGet' : 'vSet' ].apply(this[0], arguments); 2041 } 2042 2043 // Override the .val() method. Test every element. Is it a slider? Go to 2044 // the slider value handling. No? Use the standard method. 2045 // Note how $.fn.val expects 'this' to be an instance of $. For convenience, 2046 // the above 'value' function does too. 2047 $.fn.val = function ( arg ) { 2048
2049 // this === instanceof $ 2050 2051 function valMethod( a ){ 2052 return a.hasClass(Classes[0]) ? value : $val; 2053 } 2054 2055 // If no value is passed, this is 'get'. 2056 if ( !arguments.length ) { 2057 var first = $(this[0]); 2058 return valMethod(first).call(first); 2059 } 2060 2061 var isFunction = $.isFunction(arg); 2062 2063 // Return the set so it remains chainable. Make sure not to break 2064 // jQuery's .val(function( index, value ){}) signature. 2065 return this.each(function( i ){ 2066 2067 var val = arg, $t = $(this); 2068 2069 if ( isFunction ) { 2070 val = arg.call(this, i, $t.val()); 2071 } 2072 2073 valMethod($t).call($t, val); 2074 }); 2075 }; 2076 2077// Extend jQuery/Zepto with the noUiSlider method. 2078 $.fn.noUiSlider = function ( options, rebuildFlag ) { 2079 2080 switch ( options ) { 2081 case 'step': return this[0].getCurrentStep(); 2082 case 'options': return this[0].getOriginalOptions(); 2083 } 2084 2085 return ( rebuildFlag ? rebuild : initialize ).call(this, options); 2086 }; 2087 2088 function getGroup ( $Spectrum, mode, values, stepped ) { 2089 2090 // Use the range. 2091 if ( mode === 'range' || mode === 'steps' ) { 2092 return $Spectrum.xVal; 2093 } 2094 2095 if ( mode === 'count' ) { 2096 2097 // Divide 0 - 100 in 'count' parts. 2098 var spread = ( 100 / (values-1) ), v, i = 0; 2099 values = []; 2100 2101 // List these parts and have them handled as 'positions'. 2102 while ((v=i++*spread) <= 100 ) { 2103 values.push(v); 2104 } 2105 2106 mode = 'positions'; 2107 } 2108 2109 if ( mode === 'positions' ) { 2110 2111 // Map all percentages to on-range values. 2112 return $.map(values, function( value ){ 2113 return $Spectrum.fromStepping( stepped ? $Spectrum.getStep( value ) : value ); 2114 }); 2115 } 2116 2117 if ( mode === 'values' ) { 2118 2119 // If the value must be stepped, it needs to be converted to a percentage first. 2120 if ( stepped ) { 2121 2122 return $.map(values, function( value ){ 2123 2124 // Convert to percentage, apply step, return to value. 2125 return $Spectrum.fromStepping( $Spectrum.getStep( $Spectrum.toStepping( value ) ) ); 2126 }); 2127 2128 } 2129 2130 // Otherwise, we can simply use the values. 2131 return values; 2132 } 2133 } 2134 2135 function generateSpread ( $Spectrum, density, mode, group ) { 2136 2137 var originalSpectrumDirection = $Spectrum.direction, 2138 indexes = {}, 2139 firstInRange = $Spectrum.xVal[0], 2140 lastInRange = $Spectrum.xVal[$Spectrum.xVal.length-1], 2141 ignoreFirst = false, 2142 ignoreLast = false, 2143 prevPct = 0; 2144 2145 // This function loops the spectrum in an ltr linear fashion, 2146 // while the toStepping method is direction aware. Trick it into 2147 // believing it is ltr. 2148 $Spectrum.direction = 0; 2149 2150 // Create a copy of the group, sort it and filter away all duplicates. 2151 group = unique(group.slice().sort(function(a, b){ return a - b; })); 2152 2153 // Make sure the range starts with the first element. 2154 if ( group[0] !== firstInRange ) { 2155 group.unshift(firstInRange); 2156 ignoreFirst = true; 2157 } 2158 2159 // Likewise for the last one. 2160 if ( group[group.length - 1] !== lastInRange ) { 2161 group.push(lastInRange); 2162 ignoreLast = true; 2163 } 2164 2165 $.each(group, function ( index ) { 2166 2167 // Get the current step and the lower + upper positions. 2168 var step, i, q, 2169 low = group[index], 2170 high = group[index+1], 2171 newPct, pctDifference, pctPos, type, 2172 steps, realSteps, stepsize; 2173 2174 // When using 'steps' mode, use the provided steps. 2175 // Otherwise, we'll step on to the next subrange. 2176 if ( mode === 'steps' ) { 2177 step = $Spectrum.xNumSteps[ index ]; 2178 } 2179 2180 // Default to a 'full' step. 2181 if ( !step ) { 2182 step = high-low; 2183 } 2184 2185 // Low can be 0, so test for false. If high is undefined, 2186 // we are at the last subrange. Index 0 is already handled. 2187 if ( low === false || high === undefined ) { 2188 return; 2189 } 2190 2191 // Find all steps in the subrange. 2192 for ( i = low; i <= high; i += step ) { 2193 2194 // Get the percentage value for the current step, 2195 // calculate the size for the subrange. 2196 newPct = $Spectrum.toStepping( i ); 2197 pctDifference = newPct - prevPct; 2198 2199 steps = pctDifference / density; 2200 realSteps = Math.round(steps); 2201 2202 // This ratio represents the ammount of percentage-space a point indicates. 2203 // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided. 2204 // Round the percentage offset to an even number, then divide by two 2205 // to spread the offset on both sides of the range. 2206 stepsize = pctDifference/realSteps; 2207 2208 // Divide all points evenly, adding the correct number to this subrange. 2209 // Run up to <= so that 100% gets a point, event if ignoreLast is set. 2210 for ( q = 1; q <= realSteps; q += 1 ) { 2211 2212 // The ratio between the rounded value and the actual size might be ~1% off. 2213 // Correct the percentage offset by the number of points 2214 // per subrange. density = 1 will result in 100 points on the 2215 // full range, 2 for 50, 4 for 25, etc. 2216 pctPos = prevPct + ( q * stepsize ); 2217 indexes[pctPos.toFixed(5)] = ['x', 0]; 2218 } 2219 2220 // Determine the point type. 2221 type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 ); 2222 2223 // Enforce the 'ignoreFirst' option by overwriting the type for 0. 2224 if ( !index && ignoreFirst ) { 2225 type = 0; 2226 } 2227 2228 if ( !(i === high && ignoreLast)) { 2229 // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. 2230 indexes[newPct.toFixed(5)] = [i, type]; 2231 } 2232 2233 // Update the percentage count. 2234 prevPct = newPct; 2235 } 2236 }); 2237 2238 // Reset the spectrum. 2239 $Spectrum.direction = originalSpectrumDirection; 2240 2241 return indexes; 2242 } 2243 2244 function addMarking ( CSSstyle, orientation, direction, spread, filterFunc, formatter ) { 2245 2246 var style = ['horizontal', 'vertical'][orientation], 2247 element = $('<div/>'); 2248 2249 element.addClass('noUi-pips noUi-pips-'+style); 2250 2251 function getSize( type, value ){ 2252 return [ '-normal', '-large', '-sub' ][type]; 2253 } 2254 2255 function getTags( offset, source, values ) { 2256 return 'class="' + source + ' ' + 2257 source + '-' + style + ' ' + 2258 source + getSize(values[1], values[0]) + 2259 '" style="' + CSSstyle + ': ' + offset + '%"'; 2260 } 2261 2262 function addSpread ( offset, values ){ 2263 2264 if ( direction ) { 2265 offset = 100 - offset; 2266 } 2267 2268 // Apply the filter function, if it is set. 2269 values[1] = (values[1] && filterFunc) ? filterFunc(values[0], values[1]) : values[1]; 2270 2271 // Add a marker for every point 2272 element.append('<div ' + getTags(offset, 'noUi-marker', values) + '></div>'); 2273 2274 // Values are only appended for points marked '1' or '2'. 2275 if ( values[1] ) { 2276 element.append('<div '+getTags(offset, 'noUi-value', values)+'>' + formatter.to(values[0]) + '</div>'); 2277 } 2278 } 2279 2280 // Append all points. 2281 $.each(spread, addSpread); 2282 2283 return element; 2284 } 2285 2286 $.fn.noUiSlider_pips = function ( grid ) { 2287 2288 var mode = grid.mode, 2289 density = grid.density || 1, 2290 filter = grid.filter || false, 2291 values = grid.values || false, 2292 format = grid.format || { 2293 to: Math.round 2294 }, 2295 stepped = grid.stepped || false; 2296 2297 return this.each(function(){ 2298 2299 var info = this.getInfo(), 2300 group = getGroup( info[0], mode, values, stepped ), 2301 spread = generateSpread( info[0], density, mode, group ); 2302 2303 return $(this).append(addMarking( 2304 info[1], 2305 info[2], 2306 info[0].direction, 2307 spread, 2308 filter, 2309 format 2310 )); 2311 }); 2312 }; 2313 2314}( window.jQuery || window.Zepto ));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.