Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | espaco | 1 | /**@preserve |
| 2 | $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ |
||
| 3 | |||
| 4 | /*jslint browser: true */ |
||
| 5 | /*jslint sub: true */ |
||
| 6 | /*jslint white: true */ |
||
| 7 | /*jslint continue: true */ |
||
| 8 | /*jslint plusplus: true */ |
||
| 9 | |||
| 10 | (function( $ ){ |
||
| 11 | |||
| 12 | 'use strict'; |
||
| 13 | |||
| 14 | var |
||
| 15 | // Cache the document selector; |
||
| 16 | /** @const */ |
||
| 17 | doc = $(document), |
||
| 18 | // Namespace for binding and unbinding slider events; |
||
| 19 | /** @const */ |
||
| 20 | namespace = '.nui', |
||
| 21 | // Determine the events to bind. IE11 implements pointerEvents without |
||
| 22 | // a prefix, which breaks compatibility with the IE10 implementation. |
||
| 23 | /** @const */ |
||
| 24 | actions = window.navigator['pointerEnabled'] ? { |
||
| 25 | start: 'pointerdown', |
||
| 26 | move: 'pointermove', |
||
| 27 | end: 'pointerup' |
||
| 28 | } : window.navigator['msPointerEnabled'] ? { |
||
| 29 | start: 'MSPointerDown', |
||
| 30 | move: 'MSPointerMove', |
||
| 31 | end: 'MSPointerUp' |
||
| 32 | } : { |
||
| 33 | start: 'mousedown touchstart', |
||
| 34 | move: 'mousemove touchmove', |
||
| 35 | end: 'mouseup touchend' |
||
| 36 | }, |
||
| 37 | // Re-usable list of classes; |
||
| 38 | /** @const */ |
||
| 39 | Classes = [ |
||
| 40 | /* 0 */ 'noUi-target' |
||
| 41 | /* 1 */ ,'noUi-base' |
||
| 42 | /* 2 */ ,'noUi-origin' |
||
| 43 | /* 3 */ ,'noUi-handle' |
||
| 44 | /* 4 */ ,'noUi-horizontal' |
||
| 45 | /* 5 */ ,'noUi-vertical' |
||
| 46 | /* 6 */ ,'noUi-background' |
||
| 47 | /* 7 */ ,'noUi-connect' |
||
| 48 | /* 8 */ ,'noUi-ltr' |
||
| 49 | /* 9 */ ,'noUi-rtl' |
||
| 50 | /* 10 */ ,'noUi-dragable' |
||
| 51 | /* 11 */ ,'' |
||
| 52 | /* 12 */ ,'noUi-state-drag' |
||
| 53 | /* 13 */ ,'' |
||
| 54 | /* 14 */ ,'noUi-state-tap' |
||
| 55 | /* 15 */ ,'noUi-active' |
||
| 56 | /* 16 */ ,'noUi-extended' |
||
| 57 | /* 17 */ ,'noUi-stacking' |
||
| 58 | ]; |
||
| 59 | |||
| 60 | |||
| 61 | // General helpers |
||
| 62 | |||
| 63 | // Limits a value to 0 - 100 |
||
| 64 | function limit ( a ) { |
||
| 65 | return Math.max(Math.min(a, 100), 0); |
||
| 66 | } |
||
| 67 | |||
| 68 | // Round a value to the closest 'to'. |
||
| 69 | function closest ( value, to ) { |
||
| 70 | return Math.round(value / to) * to; |
||
| 71 | } |
||
| 72 | |||
| 73 | // Determine the size of a sub-range in relation to a full range. |
||
| 74 | function subRangeRatio ( pa, pb ) { |
||
| 75 | return (100 / (pb - pa)); |
||
| 76 | } |
||
| 77 | |||
| 78 | |||
| 79 | // Type validation |
||
| 80 | |||
| 81 | // Checks whether a value is numerical. |
||
| 82 | function isNumeric ( a ) { |
||
| 83 | return typeof a === 'number' && !isNaN( a ) && isFinite( a ); |
||
| 84 | } |
||
| 85 | |||
| 86 | // Wraps a variable as an array, if it isn't one yet. |
||
| 87 | function asArray ( a ) { |
||
| 88 | return $.isArray(a) ? a : [a]; |
||
| 89 | } |
||
| 90 | |||
| 91 | |||
| 92 | // Class handling |
||
| 93 | |||
| 94 | // Sets a class and removes it after [duration] ms. |
||
| 95 | function addClassFor ( element, className, duration ) { |
||
| 96 | element.addClass(className); |
||
| 97 | setTimeout(function(){ |
||
| 98 | element.removeClass(className); |
||
| 99 | }, duration); |
||
| 100 | } |
||
| 101 | |||
| 102 | |||
| 103 | // Value calculation |
||
| 104 | |||
| 105 | // (percentage) How many percent is this value of this range? |
||
| 106 | function fromPercentage ( range, value ) { |
||
| 107 | return (value * 100) / ( range[1] - range[0] ); |
||
| 108 | } |
||
| 109 | |||
| 110 | // (percentage) Where is this value on this range? |
||
| 111 | function toPercentage ( range, value ) { |
||
| 112 | return fromPercentage( range, range[0] < 0 ? |
||
| 113 | value + Math.abs(range[0]) : |
||
| 114 | value - range[0] ); |
||
| 115 | } |
||
| 116 | |||
| 117 | // (value) How much is this percentage on this range? |
||
| 118 | function isPercentage ( range, value ) { |
||
| 119 | return ((value * ( range[1] - range[0] )) / 100) + range[0]; |
||
| 120 | } |
||
| 121 | |||
| 122 | // (percentage) |
||
| 123 | function toStepping ( options, value ) { |
||
| 124 | |||
| 125 | if ( value >= options.xVal.slice(-1)[0] ){ |
||
| 126 | return 100; |
||
| 127 | } |
||
| 128 | |||
| 129 | var j = 1, va, vb, pa, pb; |
||
| 130 | while ( value >= options.xVal[j] ){ |
||
| 131 | j++; |
||
| 132 | } |
||
| 133 | |||
| 134 | va = options.xVal[j-1]; |
||
| 135 | vb = options.xVal[j]; |
||
| 136 | pa = options.xPct[j-1]; |
||
| 137 | pb = options.xPct[j]; |
||
| 138 | |||
| 139 | return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); |
||
| 140 | } |
||
| 141 | |||
| 142 | // (value) |
||
| 143 | function fromStepping ( options, value ) { |
||
| 144 | |||
| 145 | // There is no range group that fits 100 |
||
| 146 | if ( value >= 100 ){ |
||
| 147 | return options.xVal.slice(-1)[0]; |
||
| 148 | } |
||
| 149 | |||
| 150 | var j = 1, va, vb, pa, pb; |
||
| 151 | while ( value >= options.xPct[j] ){ |
||
| 152 | j++; |
||
| 153 | } |
||
| 154 | |||
| 155 | va = options.xVal[j-1]; |
||
| 156 | vb = options.xVal[j]; |
||
| 157 | pa = options.xPct[j-1]; |
||
| 158 | pb = options.xPct[j]; |
||
| 159 | |||
| 160 | return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); |
||
| 161 | } |
||
| 162 | |||
| 163 | // (percentage) Get the step that applies at a certain value. |
||
| 164 | function getStep ( options, value ){ |
||
| 165 | |||
| 166 | var j = 1, a, b; |
||
| 167 | while ( value >= options.xPct[j] ){ |
||
| 168 | j++; |
||
| 169 | } |
||
| 170 | |||
| 171 | if ( options.snap ) { |
||
| 172 | |||
| 173 | a = options.xPct[j-1]; |
||
| 174 | b = options.xPct[j]; |
||
| 175 | |||
| 176 | if ((value - a) > ((b-a)/2)){ |
||
| 177 | return b; |
||
| 178 | } |
||
| 179 | |||
| 180 | return a; |
||
| 181 | } |
||
| 182 | |||
| 183 | if ( !options.xSteps[j-1] ){ |
||
| 184 | return value; |
||
| 185 | } |
||
| 186 | |||
| 187 | return options.xPct[j-1] + closest( |
||
| 188 | value - options.xPct[j-1], |
||
| 189 | options.xSteps[j-1] |
||
| 190 | ); |
||
| 191 | } |
||
| 192 | |||
| 193 | |||
| 194 | // Event handling |
||
| 195 | |||
| 196 | // Provide a clean event with standardized offset values. |
||
| 197 | function fixEvent ( e ) { |
||
| 198 | |||
| 199 | // Prevent scrolling and panning on touch events, while |
||
| 200 | // attempting to slide. The tap event also depends on this. |
||
| 201 | e.preventDefault(); |
||
| 202 | |||
| 203 | // Filter the event to register the type, which can be |
||
| 204 | // touch, mouse or pointer. Offset changes need to be |
||
| 205 | // made on an event specific basis. |
||
| 206 | var touch = e.type.indexOf('touch') === 0 |
||
| 207 | ,mouse = e.type.indexOf('mouse') === 0 |
||
| 208 | ,pointer = e.type.indexOf('pointer') === 0 |
||
| 209 | ,x,y, event = e; |
||
| 210 | |||
| 211 | // IE10 implemented pointer events with a prefix; |
||
| 212 | if ( e.type.indexOf('MSPointer') === 0 ) { |
||
| 213 | pointer = true; |
||
| 214 | } |
||
| 215 | |||
| 216 | // Get the originalEvent, if the event has been wrapped |
||
| 217 | // by jQuery. Zepto doesn't wrap the event. |
||
| 218 | if ( e.originalEvent ) { |
||
| 219 | e = e.originalEvent; |
||
| 220 | } |
||
| 221 | |||
| 222 | if ( touch ) { |
||
| 223 | // noUiSlider supports one movement at a time, |
||
| 224 | // so we can select the first 'changedTouch'. |
||
| 225 | x = e.changedTouches[0].pageX; |
||
| 226 | y = e.changedTouches[0].pageY; |
||
| 227 | } |
||
| 228 | |||
| 229 | if ( mouse || pointer ) { |
||
| 230 | |||
| 231 | // Polyfill the pageXOffset and pageYOffset |
||
| 232 | // variables for IE7 and IE8; |
||
| 233 | if( !pointer && window.pageXOffset === undefined ){ |
||
| 234 | window.pageXOffset = document.documentElement.scrollLeft; |
||
| 235 | window.pageYOffset = document.documentElement.scrollTop; |
||
| 236 | } |
||
| 237 | |||
| 238 | x = e.clientX + window.pageXOffset; |
||
| 239 | y = e.clientY + window.pageYOffset; |
||
| 240 | } |
||
| 241 | |||
| 242 | event.points = [x, y]; |
||
| 243 | event.cursor = mouse; |
||
| 244 | |||
| 245 | return event; |
||
| 246 | } |
||
| 247 | |||
| 248 | |||
| 249 | // Input validation |
||
| 250 | |||
| 251 | function testStep ( parsed, entry ) { |
||
| 252 | |||
| 253 | if ( !isNumeric( entry ) ) { |
||
| 254 | throw new Error("noUiSlider: 'step' is not numeric."); |
||
| 255 | } |
||
| 256 | |||
| 257 | // The step option can still be used to set stepping |
||
| 258 | // for linear sliders. Overwritten if set in 'range'. |
||
| 259 | parsed.xSteps[0] = entry; |
||
| 260 | } |
||
| 261 | |||
| 262 | function testRange ( parsed, entry ) { |
||
| 263 | |||
| 264 | // Filter incorrect input. |
||
| 265 | if ( typeof entry !== 'object' || $.isArray(entry) ) { |
||
| 266 | throw new Error("noUiSlider: 'range' is not an object."); |
||
| 267 | } |
||
| 268 | |||
| 269 | // Loop all entries. |
||
| 270 | $.each( entry, function ( index, value ) { |
||
| 271 | |||
| 272 | var percentage; |
||
| 273 | |||
| 274 | // Wrap numerical input in an array. |
||
| 275 | if ( typeof value === "number" ) { |
||
| 276 | value = [value]; |
||
| 277 | } |
||
| 278 | |||
| 279 | // Reject any invalid input. |
||
| 280 | if ( !$.isArray( value ) ){ |
||
| 281 | throw new Error("noUiSlider: 'range' contains invalid value."); |
||
| 282 | } |
||
| 283 | |||
| 284 | // Covert min/max syntax to 0 and 100. |
||
| 285 | if ( index === 'min' ) { |
||
| 286 | percentage = 0; |
||
| 287 | } else if ( index === 'max' ) { |
||
| 288 | percentage = 100; |
||
| 289 | } else { |
||
| 290 | percentage = parseFloat( index ); |
||
| 291 | } |
||
| 292 | |||
| 293 | // Check for correct input. |
||
| 294 | if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) { |
||
| 295 | throw new Error("noUiSlider: 'range' value isn't numeric."); |
||
| 296 | } |
||
| 297 | |||
| 298 | // Store values. |
||
| 299 | parsed.xPct.push( percentage ); |
||
| 300 | parsed.xVal.push( value[0] ); |
||
| 301 | |||
| 302 | // NaN will evaluate to false too, but to keep |
||
| 303 | // logging clear, set step explicitly. Make sure |
||
| 304 | // not to override the 'step' setting with false. |
||
| 305 | if ( !percentage ) { |
||
| 306 | if ( !isNaN( value[1] ) ) { |
||
| 307 | parsed.xSteps[0] = value[1]; |
||
| 308 | } |
||
| 309 | } else { |
||
| 310 | parsed.xSteps.push( isNaN(value[1]) ? false : value[1] ); |
||
| 311 | } |
||
| 312 | }); |
||
| 313 | |||
| 314 | $.each(parsed.xSteps, function(i,n){ |
||
| 315 | |||
| 316 | // Ignore 'false' stepping. |
||
| 317 | if ( !n ) { |
||
| 318 | return true; |
||
| 319 | } |
||
| 320 | |||
| 321 | // Check if step fits. Not required, but this might serve some goal. |
||
| 322 | // !((parsed.xVal[i+1] - parsed.xVal[i]) % n); |
||
| 323 | |||
| 324 | // Factor to range ratio |
||
| 325 | parsed.xSteps[i] = fromPercentage([ |
||
| 326 | parsed.xVal[i] |
||
| 327 | ,parsed.xVal[i+1] |
||
| 328 | ], n) / subRangeRatio ( |
||
| 329 | parsed.xPct[i], |
||
| 330 | parsed.xPct[i+1] ); |
||
| 331 | }); |
||
| 332 | } |
||
| 333 | |||
| 334 | function testStart ( parsed, entry ) { |
||
| 335 | |||
| 336 | if ( typeof entry === "number" ) { |
||
| 337 | entry = [entry]; |
||
| 338 | } |
||
| 339 | |||
| 340 | // Validate input. Values aren't tested, the internal Link will do |
||
| 341 | // that and provide a valid location. |
||
| 342 | if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) { |
||
| 343 | throw new Error("noUiSlider: 'start' option is incorrect."); |
||
| 344 | } |
||
| 345 | |||
| 346 | // Store the number of handles. |
||
| 347 | parsed.handles = entry.length; |
||
| 348 | |||
| 349 | // When the slider is initialized, the .val method will |
||
| 350 | // be called with the start options. |
||
| 351 | parsed.start = entry; |
||
| 352 | } |
||
| 353 | |||
| 354 | function testSnap ( parsed, entry ) { |
||
| 355 | |||
| 356 | // Enforce 100% stepping within subranges. |
||
| 357 | parsed.snap = entry; |
||
| 358 | |||
| 359 | if ( typeof entry !== 'boolean' ){ |
||
| 360 | throw new Error("noUiSlider: 'snap' option must be a boolean."); |
||
| 361 | } |
||
| 362 | } |
||
| 363 | |||
| 364 | function testConnect ( parsed, entry ) { |
||
| 365 | |||
| 366 | if ( entry === 'lower' && parsed.handles === 1 ) { |
||
| 367 | parsed.connect = 1; |
||
| 368 | } else if ( entry === 'upper' && parsed.handles === 1 ) { |
||
| 369 | parsed.connect = 2; |
||
| 370 | } else if ( entry === true && parsed.handles === 2 ) { |
||
| 371 | parsed.connect = 3; |
||
| 372 | } else if ( entry === false ) { |
||
| 373 | parsed.connect = 0; |
||
| 374 | } else { |
||
| 375 | throw new Error("noUiSlider: 'connect' option doesn't match handle count."); |
||
| 376 | } |
||
| 377 | } |
||
| 378 | |||
| 379 | function testOrientation ( parsed, entry ) { |
||
| 380 | |||
| 381 | // Set orientation to an a numerical value for easy |
||
| 382 | // array selection. |
||
| 383 | switch ( entry ){ |
||
| 384 | case 'horizontal': |
||
| 385 | parsed.ort = 0; |
||
| 386 | break; |
||
| 387 | case 'vertical': |
||
| 388 | parsed.ort = 1; |
||
| 389 | break; |
||
| 390 | default: |
||
| 391 | throw new Error("noUiSlider: 'orientation' option is invalid."); |
||
| 392 | } |
||
| 393 | } |
||
| 394 | |||
| 395 | function testMargin ( parsed, entry ) { |
||
| 396 | |||
| 397 | if ( parsed.xPct.length > 2 ) { |
||
| 398 | throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); |
||
| 399 | } |
||
| 400 | |||
| 401 | // Parse value to range and store. As xVal is checked |
||
| 402 | // to be no bigger than 2, use it as range. |
||
| 403 | parsed.margin = fromPercentage(parsed.xVal, entry); |
||
| 404 | |||
| 405 | if ( !isNumeric(entry) ){ |
||
| 406 | throw new Error("noUiSlider: 'margin' option must be numeric."); |
||
| 407 | } |
||
| 408 | } |
||
| 409 | |||
| 410 | function testDirection ( parsed, entry ) { |
||
| 411 | |||
| 412 | // Set direction as a numerical value for easy parsing. |
||
| 413 | // Invert connection for RTL sliders, so that the proper |
||
| 414 | // handles get the connect/background classes. |
||
| 415 | switch ( entry ) { |
||
| 416 | case 'ltr': |
||
| 417 | parsed.dir = 0; |
||
| 418 | break; |
||
| 419 | case 'rtl': |
||
| 420 | parsed.dir = 1; |
||
| 421 | parsed.connect = [0,2,1,3][parsed.connect]; |
||
| 422 | break; |
||
| 423 | default: |
||
| 424 | throw new Error("noUiSlider: 'direction' option was not recognized."); |
||
| 425 | } |
||
| 426 | } |
||
| 427 | |||
| 428 | function testBehaviour ( parsed, entry ) { |
||
| 429 | |||
| 430 | // Make sure the input is a string. |
||
| 431 | if ( typeof entry !== 'string' ) { |
||
| 432 | throw new Error("noUiSlider: 'behaviour' must be a string containing options."); |
||
| 433 | } |
||
| 434 | |||
| 435 | // Check if the string contains any keywords. |
||
| 436 | // None are required. |
||
| 437 | var tap = entry.indexOf('tap') >= 0, |
||
| 438 | extend = entry.indexOf('extend') >= 0, |
||
| 439 | drag = entry.indexOf('drag') >= 0, |
||
| 440 | fixed = entry.indexOf('fixed') >= 0, |
||
| 441 | snap = entry.indexOf('snap') >= 0; |
||
| 442 | |||
| 443 | parsed.events = { |
||
| 444 | tap: tap || snap, |
||
| 445 | extend: extend, |
||
| 446 | drag: drag, |
||
| 447 | fixed: fixed, |
||
| 448 | snap: snap |
||
| 449 | }; |
||
| 450 | } |
||
| 451 | |||
| 452 | function testSerialization ( parsed, entry, sliders ) { |
||
| 453 | |||
| 454 | parsed.ser = [ entry['lower'], entry['upper'] ]; |
||
| 455 | parsed.formatting = entry['format']; |
||
| 456 | |||
| 457 | $.each( parsed.ser, function( index, linkInstances ){ |
||
| 458 | |||
| 459 | // Check if the provided option is an array. |
||
| 460 | if ( !$.isArray(linkInstances) ) { |
||
| 461 | throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' must be an array."); |
||
| 462 | } |
||
| 463 | |||
| 464 | $.each(linkInstances, function(){ |
||
| 465 | |||
| 466 | // Check if entry is a Link. |
||
| 467 | if ( !(this instanceof $.Link) ) { |
||
| 468 | throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' can only contain Link instances."); |
||
| 469 | } |
||
| 470 | |||
| 471 | // Assign properties. |
||
| 472 | this.setIndex ( index ); |
||
| 473 | this.setObject( sliders ); |
||
| 474 | this.setFormatting( entry['format'] ); |
||
| 475 | }); |
||
| 476 | }); |
||
| 477 | |||
| 478 | // If the slider has two handles and is RTL, |
||
| 479 | // reverse the serialization input. For one handle, |
||
| 480 | // lower is still lower. |
||
| 481 | if ( parsed.dir && parsed.handles > 1 ) { |
||
| 482 | parsed.ser.reverse(); |
||
| 483 | } |
||
| 484 | } |
||
| 485 | |||
| 486 | // Test all developer settings and parse to assumption-safe values. |
||
| 487 | function test ( options, sliders ){ |
||
| 488 | |||
| 489 | /* Every input option is tested and parsed. This'll prevent |
||
| 490 | endless validation in internal methods. These tests are |
||
| 491 | structured with an item for every option available. An |
||
| 492 | option can be marked as required by setting the 'r' flag. |
||
| 493 | The testing function is provided with three arguments: |
||
| 494 | - The provided value for the option; |
||
| 495 | - A reference to the options object; |
||
| 496 | - The name for the option; |
||
| 497 | |||
| 498 | The testing function returns false when an error is detected, |
||
| 499 | or true when everything is OK. It can also modify the option |
||
| 500 | object, to make sure all values can be correctly looped elsewhere. */ |
||
| 501 | |||
| 502 | var parsed = { |
||
| 503 | xPct: [] |
||
| 504 | ,xVal: [] |
||
| 505 | ,xSteps: [ false ] |
||
| 506 | ,margin: 0 |
||
| 507 | }, tests; |
||
| 508 | |||
| 509 | tests = { |
||
| 510 | 'step': { r: false, t: testStep }, |
||
| 511 | 'range': { r: true, t: testRange }, |
||
| 512 | 'start': { r: true, t: testStart }, |
||
| 513 | 'snap': { r: false, t: testSnap }, |
||
| 514 | 'connect': { r: true, t: testConnect }, |
||
| 515 | 'orientation': { r: false, t: testOrientation }, |
||
| 516 | 'margin': { r: false, t: testMargin }, |
||
| 517 | 'direction': { r: true, t: testDirection }, |
||
| 518 | 'behaviour': { r: true, t: testBehaviour }, |
||
| 519 | 'serialization': { r: true, t: testSerialization } |
||
| 520 | }; |
||
| 521 | |||
| 522 | // Set defaults where applicable. |
||
| 523 | options = $.extend({ |
||
| 524 | 'connect': false, |
||
| 525 | 'direction': 'ltr', |
||
| 526 | 'behaviour': 'tap', |
||
| 527 | 'orientation': 'horizontal' |
||
| 528 | }, options); |
||
| 529 | |||
| 530 | // Make sure the test for serialization runs. |
||
| 531 | options['serialization'] = $.extend({ |
||
| 532 | 'lower': [] |
||
| 533 | ,'upper': [] |
||
| 534 | ,'format': {} |
||
| 535 | }, options['serialization']); |
||
| 536 | |||
| 537 | // Run all options through a testing mechanism to ensure correct |
||
| 538 | // input. It should be noted that options might get modified to |
||
| 539 | // be handled properly. E.g. wrapping integers in arrays. |
||
| 540 | $.each( tests, function( name, test ){ |
||
| 541 | |||
| 542 | if ( options[name] === undefined ) { |
||
| 543 | |||
| 544 | if ( test.r ) { |
||
| 545 | throw new Error("noUiSlider: '" + name + "' is required."); |
||
| 546 | } |
||
| 547 | |||
| 548 | return true; |
||
| 549 | } |
||
| 550 | |||
| 551 | test.t( parsed, options[name], sliders ); |
||
| 552 | }); |
||
| 553 | |||
| 554 | // Pre-define the styles. |
||
| 555 | parsed.style = parsed.ort ? 'top' : 'left'; |
||
| 556 | |||
| 557 | return parsed; |
||
| 558 | } |
||
| 559 | |||
| 560 | |||
| 561 | // DOM additions |
||
| 562 | |||
| 563 | // Append a handle to the base. |
||
| 564 | function addHandle ( options, index ) { |
||
| 565 | |||
| 566 | var handle = $('<div><div/></div>').addClass( Classes[2] ), |
||
| 567 | additions = [ '-lower', '-upper' ]; |
||
| 568 | |||
| 569 | if ( options.dir ) { |
||
| 570 | additions.reverse(); |
||
| 571 | } |
||
| 572 | |||
| 573 | handle.children().addClass( |
||
| 574 | Classes[3] + " " + Classes[3]+additions[index] |
||
| 575 | ); |
||
| 576 | |||
| 577 | return handle; |
||
| 578 | } |
||
| 579 | |||
| 580 | // Create a copy of an element-creating Link. |
||
| 581 | function addElement ( handle, link ) { |
||
| 582 | |||
| 583 | // If the Link requires creation of a new element, |
||
| 584 | // create this element and return a new Link instance. |
||
| 585 | if ( link.el ) { |
||
| 586 | |||
| 587 | link = new $.Link({ |
||
| 588 | 'target': $(link.el).clone().appendTo( handle ), |
||
| 589 | 'method': link.method, |
||
| 590 | 'format': link.formatting |
||
| 591 | }, true); |
||
| 592 | } |
||
| 593 | |||
| 594 | // Otherwise, return the reference. |
||
| 595 | return link; |
||
| 596 | } |
||
| 597 | |||
| 598 | // Loop all links for a handle. |
||
| 599 | function addElements ( elements, handle, formatting ) { |
||
| 600 | |||
| 601 | var index, list = [], standard = new $.Link({}, true); |
||
| 602 | |||
| 603 | // Use the Link interface to provide unified |
||
| 604 | // formatting for the .val() method. |
||
| 605 | standard.setFormatting(formatting); |
||
| 606 | |||
| 607 | // The list now contains at least one element. |
||
| 608 | list.push( standard ); |
||
| 609 | |||
| 610 | // Loop all links in either 'lower' or 'upper'. |
||
| 611 | for ( index = 0; index < elements.length; index++ ) { |
||
| 612 | list.push(addElement(handle, elements[index])); |
||
| 613 | } |
||
| 614 | |||
| 615 | return list; |
||
| 616 | } |
||
| 617 | |||
| 618 | // Go over all Links and assign them to a handle. |
||
| 619 | function addLinks ( options, handles ) { |
||
| 620 | |||
| 621 | var index, links = []; |
||
| 622 | |||
| 623 | // Copy the links into a new array, instead of modifying |
||
| 624 | // the 'options.ser' list. This allows replacement of the invalid |
||
| 625 | // '.el' Links, while the others are still passed by reference. |
||
| 626 | for ( index = 0; index < options.handles; index++ ) { |
||
| 627 | |||
| 628 | // Append a new array. |
||
| 629 | links[index] = addElements( |
||
| 630 | options.ser[index], |
||
| 631 | handles[index].children(), |
||
| 632 | options.formatting |
||
| 633 | ); |
||
| 634 | } |
||
| 635 | |||
| 636 | return links; |
||
| 637 | } |
||
| 638 | |||
| 639 | // Add the proper connection classes. |
||
| 640 | function addConnection ( connect, target, handles ) { |
||
| 641 | |||
| 642 | // Apply the required connection classes to the elements |
||
| 643 | // that need them. Some classes are made up for several |
||
| 644 | // segments listed in the class list, to allow easy |
||
| 645 | // renaming and provide a minor compression benefit. |
||
| 646 | switch ( connect ) { |
||
| 647 | case 1: target.addClass( Classes[7] ); |
||
| 648 | handles[0].addClass( Classes[6] ); |
||
| 649 | break; |
||
| 650 | case 3: handles[1].addClass( Classes[6] ); |
||
| 651 | /* falls through */ |
||
| 652 | case 2: handles[0].addClass( Classes[7] ); |
||
| 653 | /* falls through */ |
||
| 654 | case 0: target.addClass(Classes[6]); |
||
| 655 | break; |
||
| 656 | } |
||
| 657 | } |
||
| 658 | |||
| 659 | // Add handles and loop Link elements. |
||
| 660 | function addHandles ( options, base ) { |
||
| 661 | |||
| 662 | var index, handles = []; |
||
| 663 | |||
| 664 | // Append handles. |
||
| 665 | for ( index = 0; index < options.handles; index++ ) { |
||
| 666 | |||
| 667 | // Keep a list of all added handles. |
||
| 668 | handles.push( addHandle( options, index ).appendTo(base) ); |
||
| 669 | } |
||
| 670 | |||
| 671 | return handles; |
||
| 672 | } |
||
| 673 | |||
| 674 | // Initialize a single slider. |
||
| 675 | function addSlider ( options, target ) { |
||
| 676 | |||
| 677 | // Apply classes and data to the target. |
||
| 678 | target.addClass([ |
||
| 679 | Classes[0], |
||
| 680 | Classes[8 + options.dir], |
||
| 681 | Classes[4 + options.ort] |
||
| 682 | ].join(' ')); |
||
| 683 | |||
| 684 | return $('<div/>').appendTo(target).addClass( Classes[1] ); |
||
| 685 | } |
||
| 686 | |||
| 687 | |||
| 688 | // Slider scope |
||
| 689 | |||
| 690 | function closure ( target, options, originalOptions ){ |
||
| 691 | |||
| 692 | // Internal variables |
||
| 693 | |||
| 694 | // All variables local to 'closure' are marked $. |
||
| 695 | var $Target = $(target), |
||
| 696 | $Locations = [-1, -1], |
||
| 697 | $Base, |
||
| 698 | $Serialization, |
||
| 699 | $Handles; |
||
| 700 | |||
| 701 | // Shorthand for base dimensions. |
||
| 702 | function baseSize ( ) { |
||
| 703 | return $Base[['width', 'height'][options.ort]](); |
||
| 704 | } |
||
| 705 | |||
| 706 | |||
| 707 | // External event handling |
||
| 708 | |||
| 709 | function fireEvents ( events ) { |
||
| 710 | |||
| 711 | // Use the external api to get the values. |
||
| 712 | // Wrap the values in an array, as .trigger takes |
||
| 713 | // only one additional argument. |
||
| 714 | var index, values = [ $Target.val() ]; |
||
| 715 | |||
| 716 | for ( index = 0; index < events.length; index++ ){ |
||
| 717 | $Target.trigger(events[index], values); |
||
| 718 | } |
||
| 719 | } |
||
| 720 | |||
| 721 | |||
| 722 | // Handle placement |
||
| 723 | |||
| 724 | // Test suggested values and apply margin, step. |
||
| 725 | function setHandle ( handle, to, delimit ) { |
||
| 726 | |||
| 727 | var n = handle[0] !== $Handles[0][0] ? 1 : 0, |
||
| 728 | lower = $Locations[0] + options.margin, |
||
| 729 | upper = $Locations[1] - options.margin; |
||
| 730 | |||
| 731 | // Don't delimit range dragging. |
||
| 732 | if ( delimit && $Handles.length > 1 ) { |
||
| 733 | to = n ? Math.max( to, lower ) : Math.min( to, upper ); |
||
| 734 | } |
||
| 735 | |||
| 736 | // Handle the step option. |
||
| 737 | if ( to < 100 ){ |
||
| 738 | to = getStep(options, to); |
||
| 739 | } |
||
| 740 | |||
| 741 | // Limit to 0/100 for .val input, trim anything beyond 7 digits, as |
||
| 742 | // JavaScript has some issues in its floating point implementation. |
||
| 743 | to = limit(parseFloat(to.toFixed(7))); |
||
| 744 | |||
| 745 | // Return falsy if handle can't move. False for 0 or 100 limit, |
||
| 746 | // '0' for limiting by another handle. |
||
| 747 | if ( to === $Locations[n] ) { |
||
| 748 | if ( $Handles.length === 1 ) { |
||
| 749 | return false; |
||
| 750 | } |
||
| 751 | return ( to === lower || to === upper ) ? 0 : false; |
||
| 752 | } |
||
| 753 | |||
| 754 | // Set the handle to the new position. |
||
| 755 | handle.css( options.style, to + '%' ); |
||
| 756 | |||
| 757 | // Force proper handle stacking |
||
| 758 | if ( handle.is(':first-child') ) { |
||
| 759 | handle.toggleClass(Classes[17], to > 50 ); |
||
| 760 | } |
||
| 761 | |||
| 762 | // Update locations. |
||
| 763 | $Locations[n] = to; |
||
| 764 | |||
| 765 | // Invert the value if this is a right-to-left slider. |
||
| 766 | if ( options.dir ) { |
||
| 767 | to = 100 - to; |
||
| 768 | } |
||
| 769 | |||
| 770 | // Write values to serialization Links. |
||
| 771 | // Convert the value to the correct relative representation. |
||
| 772 | // Convert the value to the slider stepping/range. |
||
| 773 | $($Serialization[n]).each(function(){ |
||
| 774 | this.write( fromStepping( options, to ), handle.children(), $Target ); |
||
| 775 | }); |
||
| 776 | |||
| 777 | return true; |
||
| 778 | } |
||
| 779 | |||
| 780 | // Delimit proposed values for handle positions. |
||
| 781 | function getPositions ( a, b, delimit ) { |
||
| 782 | |||
| 783 | // Add movement to current position. |
||
| 784 | var c = a + b[0], d = a + b[1]; |
||
| 785 | |||
| 786 | // Only alter the other position on drag, |
||
| 787 | // not on standard sliding. |
||
| 788 | if ( delimit ) { |
||
| 789 | if ( c < 0 ) { |
||
| 790 | d += Math.abs(c); |
||
| 791 | } |
||
| 792 | if ( d > 100 ) { |
||
| 793 | c -= ( d - 100 ); |
||
| 794 | } |
||
| 795 | |||
| 796 | // Limit values to 0 and 100. |
||
| 797 | return [limit(c), limit(d)]; |
||
| 798 | } |
||
| 799 | |||
| 800 | return [c,d]; |
||
| 801 | } |
||
| 802 | |||
| 803 | // Handles movement by tapping. |
||
| 804 | function jump ( handle, to, instant ) { |
||
| 805 | |||
| 806 | if ( !instant ) { |
||
| 807 | // Flag the slider as it is now in a transitional state. |
||
| 808 | // Transition takes 300 ms, so re-enable the slider afterwards. |
||
| 809 | addClassFor( $Target, Classes[14], 300 ); |
||
| 810 | } |
||
| 811 | |||
| 812 | // Move the handle to the new position. |
||
| 813 | setHandle( handle, to, false ); |
||
| 814 | |||
| 815 | fireEvents(['slide', 'set', 'change']); |
||
| 816 | } |
||
| 817 | |||
| 818 | |||
| 819 | // Events |
||
| 820 | |||
| 821 | // Handler for attaching events trough a proxy. |
||
| 822 | function attach ( events, element, callback, data ) { |
||
| 823 | |||
| 824 | // Add the noUiSlider namespace to all events. |
||
| 825 | events = events.replace( /\s/g, namespace + ' ' ) + namespace; |
||
| 826 | |||
| 827 | // Bind a closure on the target. |
||
| 828 | return element.on( events, function( e ){ |
||
| 829 | |||
| 830 | // jQuery and Zepto handle unset attributes differently. |
||
| 831 | var disabled = $Target.attr('disabled'); |
||
| 832 | disabled = !( disabled === undefined || disabled === null ); |
||
| 833 | |||
| 834 | // Test if there is anything that should prevent an event |
||
| 835 | // from being handled, such as a disabled state or an active |
||
| 836 | // 'tap' transition. |
||
| 837 | if( $Target.hasClass( Classes[14] ) || disabled ) { |
||
| 838 | return false; |
||
| 839 | } |
||
| 840 | |||
| 841 | e = fixEvent(e); |
||
| 842 | e.calcPoint = e.points[ options.ort ]; |
||
| 843 | |||
| 844 | // Call the event handler with the event [ and additional data ]. |
||
| 845 | callback ( e, data ); |
||
| 846 | }); |
||
| 847 | } |
||
| 848 | |||
| 849 | // Handle movement on document for handle and range drag. |
||
| 850 | function move ( event, data ) { |
||
| 851 | |||
| 852 | var handles = data.handles || $Handles, positions, state = false, |
||
| 853 | proposal = ((event.calcPoint - data.start) * 100) / baseSize(), |
||
| 854 | h = handles[0][0] !== $Handles[0][0] ? 1 : 0; |
||
| 855 | |||
| 856 | // Calculate relative positions for the handles. |
||
| 857 | positions = getPositions( proposal, data.positions, handles.length > 1); |
||
| 858 | |||
| 859 | state = setHandle ( handles[0], positions[h], handles.length === 1 ); |
||
| 860 | |||
| 861 | if ( handles.length > 1 ) { |
||
| 862 | state = setHandle ( handles[1], positions[h?0:1], false ) || state; |
||
| 863 | } |
||
| 864 | |||
| 865 | // Fire the 'slide' event if any handle moved. |
||
| 866 | if ( state ) { |
||
| 867 | fireEvents(['slide']); |
||
| 868 | } |
||
| 869 | } |
||
| 870 | |||
| 871 | // Unbind move events on document, call callbacks. |
||
| 872 | function end ( event ) { |
||
| 873 | |||
| 874 | // The handle is no longer active, so remove the class. |
||
| 875 | $('.' + Classes[15]).removeClass(Classes[15]); |
||
| 876 | |||
| 877 | // Remove cursor styles and text-selection events bound to the body. |
||
| 878 | if ( event.cursor ) { |
||
| 879 | $('body').css('cursor', '').off( namespace ); |
||
| 880 | } |
||
| 881 | |||
| 882 | // Unbind the move and end events, which are added on 'start'. |
||
| 883 | doc.off( namespace ); |
||
| 884 | |||
| 885 | // Remove dragging class. |
||
| 886 | $Target.removeClass(Classes[12]); |
||
| 887 | |||
| 888 | // Fire the change and set events. |
||
| 889 | fireEvents(['set', 'change']); |
||
| 890 | } |
||
| 891 | |||
| 892 | // Bind move events on document. |
||
| 893 | function start ( event, data ) { |
||
| 894 | |||
| 895 | // Mark the handle as 'active' so it can be styled. |
||
| 896 | if( data.handles.length === 1 ) { |
||
| 897 | data.handles[0].children().addClass(Classes[15]); |
||
| 898 | } |
||
| 899 | |||
| 900 | // A drag should never propagate up to the 'tap' event. |
||
| 901 | event.stopPropagation(); |
||
| 902 | |||
| 903 | // Attach the move event. |
||
| 904 | attach ( actions.move, doc, move, { |
||
| 905 | start: event.calcPoint, |
||
| 906 | handles: data.handles, |
||
| 907 | positions: [ |
||
| 908 | $Locations[0], |
||
| 909 | $Locations[$Handles.length - 1] |
||
| 910 | ] |
||
| 911 | }); |
||
| 912 | |||
| 913 | // Unbind all movement when the drag ends. |
||
| 914 | attach ( actions.end, doc, end, null ); |
||
| 915 | |||
| 916 | // Text selection isn't an issue on touch devices, |
||
| 917 | // so adding cursor styles can be skipped. |
||
| 918 | if ( event.cursor ) { |
||
| 919 | |||
| 920 | // Prevent the 'I' cursor and extend the range-drag cursor. |
||
| 921 | $('body').css('cursor', $(event.target).css('cursor')); |
||
| 922 | |||
| 923 | // Mark the target with a dragging state. |
||
| 924 | if ( $Handles.length > 1 ) { |
||
| 925 | $Target.addClass(Classes[12]); |
||
| 926 | } |
||
| 927 | |||
| 928 | // Prevent text selection when dragging the handles. |
||
| 929 | $('body').on('selectstart' + namespace, false); |
||
| 930 | } |
||
| 931 | } |
||
| 932 | |||
| 933 | // Move closest handle to tapped location. |
||
| 934 | function tap ( event ) { |
||
| 935 | |||
| 936 | var location = event.calcPoint, total = 0, to; |
||
| 937 | |||
| 938 | // The tap event shouldn't propagate up and cause 'edge' to run. |
||
| 939 | event.stopPropagation(); |
||
| 940 | |||
| 941 | // Add up the handle offsets. |
||
| 942 | $.each( $Handles, function(){ |
||
| 943 | total += this.offset()[ options.style ]; |
||
| 944 | }); |
||
| 945 | |||
| 946 | // Find the handle closest to the tapped position. |
||
| 947 | total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1; |
||
| 948 | |||
| 949 | location -= $Base.offset()[ options.style ]; |
||
| 950 | |||
| 951 | // Calculate the new position. |
||
| 952 | to = ( location * 100 ) / baseSize(); |
||
| 953 | |||
| 954 | // Find the closest handle and calculate the tapped point. |
||
| 955 | // The set handle to the new position. |
||
| 956 | jump( $Handles[total], to, options.events.snap ); |
||
| 957 | |||
| 958 | if ( options.events.snap ) { |
||
| 959 | start(event, { handles: [$Handles[total]] }); |
||
| 960 | } |
||
| 961 | } |
||
| 962 | |||
| 963 | // Move handle to edges when target gets tapped. |
||
| 964 | function edge ( event ) { |
||
| 965 | |||
| 966 | var i = event.calcPoint < $Base.offset()[ options.style ], |
||
| 967 | to = i ? 0 : 100; |
||
| 968 | |||
| 969 | i = i ? 0 : $Handles.length - 1; |
||
| 970 | |||
| 971 | jump( $Handles[i], to, false ); |
||
| 972 | } |
||
| 973 | |||
| 974 | // Attach events to several slider parts. |
||
| 975 | function events ( behaviour ) { |
||
| 976 | |||
| 977 | var i, drag; |
||
| 978 | |||
| 979 | // Attach the standard drag event to the handles. |
||
| 980 | if ( !behaviour.fixed ) { |
||
| 981 | |||
| 982 | for ( i = 0; i < $Handles.length; i++ ) { |
||
| 983 | |||
| 984 | // These events are only bound to the visual handle |
||
| 985 | // element, not the 'real' origin element. |
||
| 986 | attach ( actions.start, $Handles[i].children(), start, { |
||
| 987 | handles: [ $Handles[i] ] |
||
| 988 | }); |
||
| 989 | } |
||
| 990 | } |
||
| 991 | |||
| 992 | // Attach the tap event to the slider base. |
||
| 993 | if ( behaviour.tap ) { |
||
| 994 | attach ( actions.start, $Base, tap, { |
||
| 995 | handles: $Handles |
||
| 996 | }); |
||
| 997 | } |
||
| 998 | |||
| 999 | // Extend tapping behaviour to target |
||
| 1000 | if ( behaviour.extend ) { |
||
| 1001 | |||
| 1002 | $Target.addClass( Classes[16] ); |
||
| 1003 | |||
| 1004 | if ( behaviour.tap ) { |
||
| 1005 | attach ( actions.start, $Target, edge, { |
||
| 1006 | handles: $Handles |
||
| 1007 | }); |
||
| 1008 | } |
||
| 1009 | } |
||
| 1010 | |||
| 1011 | // Make the range dragable. |
||
| 1012 | if ( behaviour.drag ){ |
||
| 1013 | |||
| 1014 | drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] ); |
||
| 1015 | |||
| 1016 | // When the range is fixed, the entire range can |
||
| 1017 | // be dragged by the handles. The handle in the first |
||
| 1018 | // origin will propagate the start event upward, |
||
| 1019 | // but it needs to be bound manually on the other. |
||
| 1020 | if ( behaviour.fixed ) { |
||
| 1021 | drag = drag.add($Base.children().not( drag ).children()); |
||
| 1022 | } |
||
| 1023 | |||
| 1024 | attach ( actions.start, drag, start, { |
||
| 1025 | handles: $Handles |
||
| 1026 | }); |
||
| 1027 | } |
||
| 1028 | } |
||
| 1029 | |||
| 1030 | |||
| 1031 | // Initialize slider |
||
| 1032 | |||
| 1033 | // Throw an error if the slider was already initialized. |
||
| 1034 | if ( $Target.hasClass(Classes[0]) ) { |
||
| 1035 | throw new Error('Slider was already initialized.'); |
||
| 1036 | } |
||
| 1037 | |||
| 1038 | // Create the base element, initialise HTML and set classes. |
||
| 1039 | // Add handles and links. |
||
| 1040 | $Base = addSlider( options, $Target ); |
||
| 1041 | $Handles = addHandles( options, $Base ); |
||
| 1042 | $Serialization = addLinks( options, $Handles ); |
||
| 1043 | |||
| 1044 | // Set the connect classes. |
||
| 1045 | addConnection ( options.connect, $Target, $Handles ); |
||
| 1046 | |||
| 1047 | // Attach user events. |
||
| 1048 | events( options.events ); |
||
| 1049 | |||
| 1050 | |||
| 1051 | // Methods |
||
| 1052 | |||
| 1053 | // Set the slider value. |
||
| 1054 | /** @expose */ |
||
| 1055 | target.vSet = function ( ) { |
||
| 1056 | |||
| 1057 | var args = Array.prototype.slice.call( arguments, 0 ), |
||
| 1058 | callback, link, update, animate, |
||
| 1059 | i, count, actual, to, values = asArray( args[0] ); |
||
| 1060 | |||
| 1061 | // Extract modifiers for value method. |
||
| 1062 | if ( typeof args[1] === 'object' ) { |
||
| 1063 | callback = args[1]['set']; |
||
| 1064 | link = args[1]['link']; |
||
| 1065 | update = args[1]['update']; |
||
| 1066 | animate = args[1]['animate']; |
||
| 1067 | |||
| 1068 | // Support the 'true' option. |
||
| 1069 | } else if ( args[1] === true ) { |
||
| 1070 | callback = true; |
||
| 1071 | } |
||
| 1072 | |||
| 1073 | // The RTL settings is implemented by reversing the front-end, |
||
| 1074 | // internal mechanisms are the same. |
||
| 1075 | if ( options.dir && options.handles > 1 ) { |
||
| 1076 | values.reverse(); |
||
| 1077 | } |
||
| 1078 | |||
| 1079 | // Animation is optional. |
||
| 1080 | if ( animate ) { |
||
| 1081 | addClassFor( $Target, Classes[14], 300 ); |
||
| 1082 | } |
||
| 1083 | |||
| 1084 | // Determine how often to set the handles. |
||
| 1085 | count = $Handles.length > 1 ? 3 : 1; |
||
| 1086 | if ( values.length === 1 ) { |
||
| 1087 | count = 1; |
||
| 1088 | } |
||
| 1089 | |||
| 1090 | // If there are multiple handles to be set run the setting |
||
| 1091 | // mechanism twice for the first handle, to make sure it |
||
| 1092 | // can be bounced of the second one properly. |
||
| 1093 | for ( i = 0; i < count; i++ ) { |
||
| 1094 | |||
| 1095 | to = link || $Serialization[i%2][0]; |
||
| 1096 | to = to.getValue( values[i%2] ); |
||
| 1097 | |||
| 1098 | if ( to === false ) { |
||
| 1099 | continue; |
||
| 1100 | } |
||
| 1101 | |||
| 1102 | // Calculate the new handle position |
||
| 1103 | to = toStepping( options, to ); |
||
| 1104 | |||
| 1105 | // Invert the value if this is a right-to-left slider. |
||
| 1106 | if ( options.dir ) { |
||
| 1107 | to = 100 - to; |
||
| 1108 | } |
||
| 1109 | |||
| 1110 | // Force delimitation. |
||
| 1111 | if ( setHandle( $Handles[i%2], to, true ) === true ) { |
||
| 1112 | continue; |
||
| 1113 | } |
||
| 1114 | |||
| 1115 | // Reset the input if it doesn't match the slider. |
||
| 1116 | $($Serialization[i%2]).each(function(index){ |
||
| 1117 | |||
| 1118 | if (!index) { |
||
| 1119 | actual = this.actual; |
||
| 1120 | return true; |
||
| 1121 | } |
||
| 1122 | |||
| 1123 | this.write( |
||
| 1124 | actual, |
||
| 1125 | $Handles[i%2].children(), |
||
| 1126 | $Target, |
||
| 1127 | update |
||
| 1128 | ); |
||
| 1129 | }); |
||
| 1130 | } |
||
| 1131 | |||
| 1132 | // Optionally fire the 'set' event. |
||
| 1133 | if( callback === true ) { |
||
| 1134 | fireEvents(['set']); |
||
| 1135 | } |
||
| 1136 | |||
| 1137 | return this; |
||
| 1138 | }; |
||
| 1139 | |||
| 1140 | // Get the slider value. |
||
| 1141 | /** @expose */ |
||
| 1142 | target.vGet = function ( ) { |
||
| 1143 | |||
| 1144 | var i, retour = []; |
||
| 1145 | |||
| 1146 | // Get the value from all handles. |
||
| 1147 | for ( i = 0; i < options.handles; i++ ){ |
||
| 1148 | retour[i] = $Serialization[i][0].saved; |
||
| 1149 | } |
||
| 1150 | |||
| 1151 | // If only one handle is used, return a single value. |
||
| 1152 | if ( retour.length === 1 ){ |
||
| 1153 | return retour[0]; |
||
| 1154 | } |
||
| 1155 | |||
| 1156 | if ( options.dir ) { |
||
| 1157 | return retour.reverse(); |
||
| 1158 | } |
||
| 1159 | |||
| 1160 | return retour; |
||
| 1161 | }; |
||
| 1162 | |||
| 1163 | // Destroy the slider and unbind all events. |
||
| 1164 | /** @expose */ |
||
| 1165 | target.destroy = function ( ) { |
||
| 1166 | |||
| 1167 | // Loop all linked serialization objects and unbind all |
||
| 1168 | // events in the noUiSlider namespace. |
||
| 1169 | $.each($Serialization, function(){ |
||
| 1170 | $.each(this, function(){ |
||
| 1171 | // Won't remove 'change' when bound implicitly. |
||
| 1172 | if ( this.target ) { |
||
| 1173 | this.target.off( namespace ); |
||
| 1174 | } |
||
| 1175 | }); |
||
| 1176 | }); |
||
| 1177 | |||
| 1178 | // Unbind events on the slider, remove all classes and child elements. |
||
| 1179 | $(this).off(namespace) |
||
| 1180 | .removeClass(Classes.join(' ')) |
||
| 1181 | .empty(); |
||
| 1182 | |||
| 1183 | // Return the original options from the closure. |
||
| 1184 | return originalOptions; |
||
| 1185 | }; |
||
| 1186 | |||
| 1187 | |||
| 1188 | // Value setting |
||
| 1189 | |||
| 1190 | // Use the public value method to set the start values. |
||
| 1191 | $Target.val( options.start ); |
||
| 1192 | } |
||
| 1193 | |||
| 1194 | |||
| 1195 | // Access points |
||
| 1196 | |||
| 1197 | // Run the standard initializer |
||
| 1198 | function initialize ( originalOptions ) { |
||
| 1199 | |||
| 1200 | // Throw error if group is empty. |
||
| 1201 | if ( !this.length ){ |
||
| 1202 | throw new Error("noUiSlider: Can't initialize slider on empty selection."); |
||
| 1203 | } |
||
| 1204 | |||
| 1205 | // Test the options once, not for every slider. |
||
| 1206 | var options = test( originalOptions, this ); |
||
| 1207 | |||
| 1208 | // Loop all items, and provide a new closed-scope environment. |
||
| 1209 | return this.each(function(){ |
||
| 1210 | closure(this, options, originalOptions); |
||
| 1211 | }); |
||
| 1212 | } |
||
| 1213 | |||
| 1214 | // Destroy the slider, then re-enter initialization. |
||
| 1215 | function rebuild ( options ) { |
||
| 1216 | |||
| 1217 | return this.each(function(){ |
||
| 1218 | |||
| 1219 | // Get the current values from the slider, |
||
| 1220 | // including the initialization options. |
||
| 1221 | var values = $(this).val(), |
||
| 1222 | originalOptions = this.destroy(), |
||
| 1223 | |||
| 1224 | // Extend the previous options with the newly provided ones. |
||
| 1225 | newOptions = $.extend( {}, originalOptions, options ); |
||
| 1226 | |||
| 1227 | // Run the standard initializer. |
||
| 1228 | $(this).noUiSlider( newOptions ); |
||
| 1229 | |||
| 1230 | // If the start option hasn't changed, |
||
| 1231 | // reset the previous values. |
||
| 1232 | if ( originalOptions.start === newOptions.start ) { |
||
| 1233 | $(this).val(values); |
||
| 1234 | } |
||
| 1235 | }); |
||
| 1236 | } |
||
| 1237 | |||
| 1238 | |||
| 1239 | // Remap the serialization constructor for legacy support. |
||
| 1240 | /** @expose */ |
||
| 1241 | $.noUiSlider = { 'Link': $.Link }; |
||
| 1242 | |||
| 1243 | // Extend jQuery/Zepto with the noUiSlider method. |
||
| 1244 | /** @expose */ |
||
| 1245 | $.fn.noUiSlider = function ( options, re ) { |
||
| 1246 | return ( re ? rebuild : initialize ).call(this, options); |
||
| 1247 | }; |
||
| 1248 | |||
| 1249 | // Attach a classbased val handler. |
||
| 1250 | $.classVal(Classes[0], 'vGet', 'vSet', false); |
||
| 1251 | |||
| 1252 | }( window['jQuery'] || window['Zepto'] )); |