1 2//gsap highlight element 3document.addEventListener("DOMContentLoaded", (event) => { 4 const highlight = document.getElementById("highlight-style"); //set this id highlight-style to style object 5 6 gsap.registerPlugin(ScrollTrigger); 7 8 gsap.utils.toArray(".text-highlight").forEach((highlight) => { //add the highlight element css property to the element 9 ScrollTrigger.create({ 10 trigger: highlight, 11 start: "-100px center", 12 onEnter: () => highlight.classList.add("active") //start highlighting immediately 13 }); 14 }); 15 16 //coin scroll trigger 17 gsap.utils.toArray(".attribute").forEach(function(elem, i) { //for each attribute element we need to add the attribute 18 19 const dx = [600, -400, -500, 300, 800, -120]; 20 const dy = [-50, 20, -150, -120, -80, 40]; 21 const dr = [-190, 200, 60, -290, 230, -540]; 22 23 24 let tl = gsap.timeline({ 25 scrollTrigger: { 26 trigger: elem, 27 start: "top 500", 28 scrub: 0.2, 29 }, 30 }) 31 .to( elem, { 32 x: dx[i], 33 y: dy[i], 34 rotation: dr[i], 35 } ) 36 }); 37 38 const setHighlightStyle = (value) => 39 document.body.setAttribute("data-highlight", value); //set the highlight style to style object from the document body attribute value 40 41 highlight.addEventListener("change", (e) => setHighlightStyle(e.target.value)); //set highlight style to value attribute on change 42 43 setHighlightStyle(highlight.value); 44}); 45 46//Scroll Trigger animation 47document.addEventListener("DOMContentLoaded", (event) => { 48 const scroller = new LocomotiveScroll({ 49 el: document.querySelector('[data-scroll-container]'), //use locomotive scroll 50 smooth: true 51 }); 52}); 53 54// Cursor Mousemove: 55document.addEventListener("DOMContentLoaded", (event) => { 56 const cursorBox = $(".cursor"); 57 58 gsap.set($(cursorBox), { xPercent: -50, yPercent: -50 }); 59 let xTo = gsap.quickTo($(cursorBox), "x", { duration: 0.6, ease: "power3" }), 60 yTo = gsap.quickTo($(cursorBox), "y", { duration: 0.6, ease: "power3" }); 61 62 window.addEventListener("mousemove", (e) => { 63 // xTo(e.pageX); 64 // yTo(e.pageY); 65 xTo(e.clientX); 66 yTo(e.clientY); 67 }); 68 69 const links = $("a"); 70 var mouseInAnimation = false; 71 links.on({ 72 mouseover: function () { 73 mouseInAnimation = gsap.to($(cursorBox), { 74 duration: 1, 75 height: 90, 76 width: 90, 77 backgroundColor: "transparent", 78 ease: Elastic.easeOut, 79 }); 80 }, 81 mouseleave: function () { 82 var delay = 0; 83 if (mouseInAnimation.isActive()) { 84 delay = 0.25; 85 } 86 gsap.to($(cursorBox), { 87 duration: 0.5, 88 delay: delay, 89 height: 4, 90 width: 4, 91 backgroundColor: "#00c288", 92 ease: "power3", 93 }); 94 }, 95 }); 96}); 97 98// Initialize slider: 99document.addEventListener("DOMContentLoaded", (event) => { 100 $(document).ready(function () { 101 $(".noUi-handle").on("click", function () { 102 $(this).width(50); 103 }); 104 var rangeSlider = document.getElementById("slider-range"); 105 var moneyFormat = wNumb({ 106 decimals: 0, 107 thousand: ",", 108 prefix: "$", 109 }); 110 noUiSlider.create(rangeSlider, { 111 start: [500000, 1000000], 112 step: 1, 113 range: { 114 min: [100000], 115 max: [1000000], 116 }, 117 format: moneyFormat, 118 connect: true, 119 }); 120 121 // Set visual min and max values and also update value hidden form inputs 122 rangeSlider.noUiSlider.on("update", function (values, handle) { 123 document.getElementById("slider-range-value1").innerHTML = values[0]; 124 document.getElementById("slider-range-value2").innerHTML = values[1]; 125 document.getElementsByName("min-value").value = moneyFormat.from(values[0]); 126 document.getElementsByName("max-value").value = moneyFormat.from(values[1]); 127 }); 128 });
129 130 // https://refreshless.com/nouislider/ 131 /*! nouislider - 8.3.0 - 2016-02-14 17:37:19 */ 132 (function (factory) { 133 if (typeof define === "function" && define.amd) { 134 // AMD. Register as an anonymous module. 135 define([], factory); 136 } else if (typeof exports === "object") { 137 // Node/CommonJS 138 module.exports = factory(); 139 } else { 140 // Browser globals 141 window.noUiSlider = factory(); 142 } 143 })(function () { 144 "use strict"; 145 // Removes duplicates from an array. 146 function unique(array) { 147 return array.filter(function (a) { 148 return !this[a] ? (this[a] = true) : false; 149 }, {}); 150 } 151 // Round a value to the closest 'to'. 152 153 function closest(value, to) { 154 return Math.round(value / to) * to; 155 } 156 // Current position of an element relative to the document. 157 158 function offset(elem) { 159 var rect = elem.getBoundingClientRect(), 160 doc = elem.ownerDocument, 161 docElem = doc.documentElement, 162 pageOffset = getPageOffset(); 163 // getBoundingClientRect contains left scroll in Chrome on Android. 164 // I haven't found a feature detection that proves this. Worst case 165 // scenario on mis-match: the 'tap' feature on horizontal sliders breaks. 166 if (/webkit.*Chrome.*Mobile/i.test(navigator.userAgent)) { 167 pageOffset.x = 0; 168 } 169 return { 170 top: rect.top + pageOffset.y - docElem.clientTop, 171 left: rect.left + pageOffset.x - docElem.clientLeft, 172 }; 173 } 174 // Checks whether a value is numerical. 175 176 function isNumeric(a) { 177 return typeof a === "number" && !isNaN(a) && isFinite(a); 178 } 179 // Rounds a number to 7 supported decimals. 180 181 function accurateNumber(number) { 182 var p = Math.pow(10, 7); 183 return Number((Math.round(number * p) / p).toFixed(7)); 184 } 185 // Sets a class and removes it after [duration] ms. 186 187 function addClassFor(element, className, duration) { 188 addClass(element, className); 189 setTimeout(function () { 190 removeClass(element, className); 191 }, duration); 192 } 193 // Limits a value to 0 - 100 194 195 function limit(a) { 196 return Math.max(Math.min(a, 100), 0); 197 } 198 // Wraps a variable as an array, if it isn't one yet. 199 200 function asArray(a) { 201 return Array.isArray(a) ? a : [a]; 202 } 203 // Counts decimals 204 205 function countDecimals(numStr) { 206 var pieces = numStr.split("."); 207 return pieces.length > 1 ? pieces[1].length : 0; 208 } 209 // http://youmightnotneedjquery.com/#add_class 210 211 function addClass(el, className) { 212 if (el.classList) { 213 el.classList.add(className); 214 } else { 215 el.className += " " + className; 216 } 217 } 218 // http://youmightnotneedjquery.com/#remove_class 219 220 function removeClass(el, className) { 221 if (el.classList) { 222 el.classList.remove(className); 223 } else { 224 el.className = el.className.replace(new RegExp("(^|\\b)" + className.split(" ").join("|") + "(\\b|$)", "gi"), " "); 225 } 226 } 227 // https://plainjs.com/javascript/attributes/adding-removing-and-testing-for-classes-9/ 228 229 function hasClass(el, className) { 230 return el.classList ? el.classList.contains(className) : new RegExp("\\b" + className + "\\b").test(el.className); 231 } 232 // https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes 233 234 function getPageOffset() { 235 var supportPageOffset = window.pageXOffset !== undefined, 236 isCSS1Compat = (document.compatMode || "") === "CSS1Compat", 237 x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft, 238 y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop; 239 return { 240 x: x, 241 y: y, 242 }; 243 } 244 // Shorthand for stopPropagation so we don't have to create a dynamic method 245 246 function stopPropagation(e) { 247 e.stopPropagation(); 248 } 249 // todo 250 251 function addCssPrefix(cssPrefix) { 252 return function (className) { 253 return cssPrefix + className; 254 }; 255 } 256 var // Determine the events to bind. IE11 implements pointerEvents without 257 // a prefix, which breaks compatibility with the IE10 implementation. 258 /** @const */ 259 actions = window.navigator.pointerEnabled 260 ? { 261 start: "pointerdown", 262 move: "pointermove", 263 end: "pointerup", 264 } 265 : window.navigator.msPointerEnabled 266 ? { 267 start: "MSPointerDown", 268 move: "MSPointerMove", 269 end: "MSPointerUp", 270 } 271 : { 272 start: "mousedown touchstart", 273 move: "mousemove touchmove", 274 end: "mouseup touchend", 275 }, 276 defaultCssPrefix = "noUi-"; 277 // Value calculation 278 // Determine the size of a sub-range in relation to a full range. 279 function subRangeRatio(pa, pb) { 280 return 100 / (pb - pa); 281 } 282 // (percentage) How many percent is this value of this range? 283 284 function fromPercentage(range, value) { 285 return (value * 100) / (range[1] - range[0]); 286 } 287 // (percentage) Where is this value on this range? 288 289 function toPercentage(range, value) { 290 return fromPercentage(range, range[0] < 0 ? value + Math.abs(range[0]) : value - range[0]); 291 } 292 // (value) How much is this percentage on this range? 293 294 function isPercentage(range, value) { 295 return (value * (range[1] - range[0])) / 100 + range[0]; 296 } 297 // Range conversion 298 299 function getJ(value, arr) { 300 var j = 1; 301 while (value >= arr[j]) { 302 j += 1; 303 } 304 return j; 305 } 306 // (percentage) Input a value, find where, on a scale of 0-100, it applies. 307 308 function toStepping(xVal, xPct, value) { 309 if (value >= xVal.slice(-1)[0]) { 310 return 100; 311 } 312 var j = getJ(value, xVal), 313 va, 314 vb, 315 pa, 316 pb; 317 va = xVal[j - 1]; 318 vb = xVal[j]; 319 pa = xPct[j - 1]; 320 pb = xPct[j]; 321 return pa + toPercentage([va, vb], value) / subRangeRatio(pa, pb); 322 } 323 // (value) Input a percentage, find where it is on the specified range. 324 325 function fromStepping(xVal, xPct, value) { 326 // There is no range group that fits 100 327 if (value >= 100) { 328 return xVal.slice(-1)[0]; 329 } 330 var j = getJ(value, xPct), 331 va, 332 vb, 333 pa, 334 pb; 335 va = xVal[j - 1]; 336 vb = xVal[j]; 337 pa = xPct[j - 1]; 338 pb = xPct[j]; 339 return isPercentage([va, vb], (value - pa) * subRangeRatio(pa, pb)); 340 } 341 // (percentage) Get the step that applies at a certain value. 342 343 function getStep(xPct, xSteps, snap, value) { 344 if (value === 100) { 345 return value; 346 } 347 var j = getJ(value, xPct), 348 a, 349 b; 350 // If 'snap' is set, steps are used as fixed points on the slider. 351 if (snap) { 352 a = xPct[j - 1]; 353 b = xPct[j]; 354 // Find the closest position, a or b. 355 if (value - a > (b - a) / 2) { 356 return b; 357 } 358 return a; 359 } 360 if (!xSteps[j - 1]) { 361 return value; 362 } 363 return xPct[j - 1] + closest(value - xPct[j - 1], xSteps[j - 1]); 364 } 365 // Entry parsing 366 367 function handleEntryPoint(index, value, that) { 368 var percentage; 369 // Wrap numerical input in an array. 370 if (typeof value === "number") { 371 value = [value]; 372 }
373 // Reject any invalid input, by testing whether value is an array. 374 if (Object.prototype.toString.call(value) !== "[object Array]") { 375 throw new Error("noUiSlider: 'range' contains invalid value."); 376 } 377 // Covert min/max syntax to 0 and 100. 378 if (index === "min") { 379 percentage = 0; 380 } else if (index === "max") { 381 percentage = 100; 382 } else { 383 percentage = parseFloat(index); 384 } 385 // Check for correct input. 386 if (!isNumeric(percentage) || !isNumeric(value[0])) { 387 throw new Error("noUiSlider: 'range' value isn't numeric."); 388 } 389 // Store values. 390 that.xPct.push(percentage); 391 that.xVal.push(value[0]); 392 // NaN will evaluate to false too, but to keep 393 // logging clear, set step explicitly. Make sure 394 // not to override the 'step' setting with false. 395 if (!percentage) { 396 if (!isNaN(value[1])) { 397 that.xSteps[0] = value[1]; 398 } 399 } else { 400 that.xSteps.push(isNaN(value[1]) ? false : value[1]); 401 } 402 } 403 404 function handleStepPoint(i, n, that) { 405 // Ignore 'false' stepping. 406 if (!n) { 407 return true; 408 } 409 // Factor to range ratio 410 that.xSteps[i] = fromPercentage([that.xVal[i], that.xVal[i + 1]], n) / subRangeRatio(that.xPct[i], that.xPct[i + 1]); 411 } 412 // Interface 413 // The interface to Spectrum handles all direction-based 414 // conversions, so the above values are unaware. 415 416 function Spectrum(entry, snap, direction, singleStep) { 417 this.xPct = []; 418 this.xVal = []; 419 this.xSteps = [singleStep || false]; 420 this.xNumSteps = [false]; 421 this.snap = snap; 422 this.direction = direction; 423 var index, 424 ordered = [ 425 /* [0, 'min'], [1, '50%'], [2, 'max'] */ 426 ]; 427 // Map the object keys to an array. 428 for (index in entry) { 429 if (entry.hasOwnProperty(index)) { 430 ordered.push([entry[index], index]); 431 } 432 } 433 // Sort all entries by value (numeric sort). 434 if (ordered.length && typeof ordered[0][0] === "object") { 435 ordered.sort(function (a, b) { 436 return a[0][0] - b[0][0]; 437 }); 438 } else { 439 ordered.sort(function (a, b) { 440 return a[0] - b[0]; 441 }); 442 } 443 // Convert all entries to subranges. 444 for (index = 0; index < ordered.length; index++) { 445 handleEntryPoint(ordered[index][1], ordered[index][0], this); 446 } 447 // Store the actual step values. 448 // xSteps is sorted in the same order as xPct and xVal. 449 this.xNumSteps = this.xSteps.slice(0); 450 // Convert all numeric steps to the percentage of the subrange they represent. 451 for (index = 0; index < this.xNumSteps.length; index++) { 452 handleStepPoint(index, this.xNumSteps[index], this); 453 } 454 } 455 Spectrum.prototype.getMargin = function (value) { 456 return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false; 457 }; 458 Spectrum.prototype.toStepping = function (value) { 459 value = toStepping(this.xVal, this.xPct, value); 460 // Invert the value if this is a right-to-left slider. 461 if (this.direction) { 462 value = 100 - value; 463 } 464 return value; 465 }; 466 Spectrum.prototype.fromStepping = function (value) { 467 // Invert the value if this is a right-to-left slider. 468 if (this.direction) { 469 value = 100 - value; 470 } 471 return accurateNumber(fromStepping(this.xVal, this.xPct, value)); 472 }; 473 Spectrum.prototype.getStep = function (value) { 474 // Find the proper step for rtl sliders by search in inverse direction. 475 // Fixes issue #262. 476 if (this.direction) { 477 value = 100 - value; 478 } 479 value = getStep(this.xPct, this.xSteps, this.snap, value); 480 if (this.direction) { 481 value = 100 - value; 482 } 483 return value; 484 }; 485 Spectrum.prototype.getApplicableStep = function (value) { 486 // If the value is 100%, return the negative step twice. 487 var j = getJ(value, this.xPct), 488 offset = value === 100 ? 2 : 1; 489 return [this.xNumSteps[j - 2], this.xVal[j - offset], this.xNumSteps[j - offset]]; 490 }; 491 // Outside testing 492 Spectrum.prototype.convert = function (value) {
493 return this.getStep(this.toStepping(value)); 494 }; 495 /* Every input option is tested and parsed. This'll prevent 496 endless validation in internal methods. These tests are 497 structured with an item for every option available. An 498 option can be marked as required by setting the 'r' flag. 499 The testing function is provided with three arguments: 500 - The provided value for the option; 501 - A reference to the options object; 502 - The name for the option; 503 504 The testing function returns false when an error is detected, 505 or true when everything is OK. It can also modify the option 506 object, to make sure all values can be correctly looped elsewhere. */ 507 var defaultFormatter = { 508 to: function (value) { 509 return value !== undefined && value.toFixed(2); 510 }, 511 from: Number, 512 }; 513 514 function testStep(parsed, entry) { 515 if (!isNumeric(entry)) { 516 throw new Error("noUiSlider: 'step' is not numeric."); 517 } 518 // The step option can still be used to set stepping 519 // for linear sliders. Overwritten if set in 'range'. 520 parsed.singleStep = entry; 521 } 522 523 function testRange(parsed, entry) { 524 // Filter incorrect input. 525 if (typeof entry !== "object" || Array.isArray(entry)) { 526 throw new Error("noUiSlider: 'range' is not an object."); 527 } 528 // Catch missing start or end. 529 if (entry.min === undefined || entry.max === undefined) { 530 throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); 531 } 532 // Catch equal start or end. 533 if (entry.min === entry.max) { 534 throw new Error("noUiSlider: 'range' 'min' and 'max' cannot be equal."); 535 } 536 parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); 537 } 538 539 function testStart(parsed, entry) { 540 entry = asArray(entry); 541 // Validate input. Values aren't tested, as the public .val method 542 // will always provide a valid location. 543 if (!Array.isArray(entry) || !entry.length || entry.length > 2) { 544 throw new Error("noUiSlider: 'start' option is incorrect."); 545 } 546 // Store the number of handles. 547 parsed.handles = entry.length; 548 // When the slider is initialized, the .val method will 549 // be called with the start options. 550 parsed.start = entry; 551 } 552 553 function testSnap(parsed, entry) { 554 // Enforce 100% stepping within subranges. 555 parsed.snap = entry; 556 if (typeof entry !== "boolean") { 557 throw new Error("noUiSlider: 'snap' option must be a boolean."); 558 } 559 } 560 561 function testAnimate(parsed, entry) { 562 // Enforce 100% stepping within subranges. 563 parsed.animate = entry; 564 if (typeof entry !== "boolean") { 565 throw new Error("noUiSlider: 'animate' option must be a boolean."); 566 } 567 } 568 569 function testConnect(parsed, entry) { 570 if (entry === "lower" && parsed.handles === 1) { 571 parsed.connect = 1; 572 } else if (entry === "upper" && parsed.handles === 1) { 573 parsed.connect = 2; 574 } else if (entry === true && parsed.handles === 2) { 575 parsed.connect = 3; 576 } else if (entry === false) { 577 parsed.connect = 0; 578 } else { 579 throw new Error("noUiSlider: 'connect' option doesn't match handle count."); 580 } 581 } 582 583 function testOrientation(parsed, entry) { 584 // Set orientation to an a numerical value for easy 585 // array selection. 586 switch (entry) { 587 case "horizontal": 588 parsed.ort = 0; 589 break; 590 case "vertical": 591 parsed.ort = 1; 592 break; 593 default: 594 throw new Error("noUiSlider: 'orientation' option is invali
594d."); 595 } 596 } 597 598 function testMargin(parsed, entry) { 599 if (!isNumeric(entry)) { 600 throw new Error("noUiSlider: 'margin' option must be numeric."); 601 } 602 // Issue #582 603 if (entry === 0) { 604 return; 605 } 606 parsed.margin = parsed.spectrum.getMargin(entry); 607 if (!parsed.margin) { 608 throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); 609 } 610 } 611 612 function testLimit(parsed, entry) { 613 if (!isNumeric(entry)) { 614 throw new Error("noUiSlider: 'limit' option must be numeric."); 615 } 616 parsed.limit = parsed.spectrum.getMargin(entry); 617 if (!parsed.limit) { 618 throw new Error("noUiSlider: 'limit' option is only supported on linear sliders."); 619 } 620 } 621 622 function testDirection(parsed, entry) { 623 // Set direction as a numerical value for easy parsing. 624 // Invert connection for RTL sliders, so that the proper 625 // handles get the connect/background classes. 626 switch (entry) { 627 case "ltr": 628 parsed.dir = 0; 629 break; 630 case "rtl": 631 parsed.dir = 1; 632 parsed.connect = [0, 2, 1, 3][parsed.connect]; 633 break; 634 default: 635 throw new Error("noUiSlider: 'direction' option was not recognized."); 636 } 637 } 638 639 function testBehaviour(parsed, entry) { 640 // Make sure the input is a string. 641 if (typeof entry !== "string") { 642 throw new Error("noUiSlider: 'behaviour' must be a string containing options."); 643 } 644 // Check if the string contains any keywords. 645 // None are required. 646 var tap = entry.indexOf("tap") >= 0, 647 drag = entry.indexOf("drag") >= 0, 648 fixed = entry.indexOf("fixed") >= 0, 649 snap = entry.indexOf("snap") >= 0, 650 hover = entry.indexOf("hover") >= 0; 651 // Fix #472 652 if (drag && !parsed.connect) { 653 throw new Error("noUiSlider: 'drag' behaviour must be used with 'connect': true."); 654 } 655 parsed.events = { 656 tap: tap || snap, 657 drag: drag, 658 fixed: fixed, 659 snap: snap, 660 hover: hover, 661 }; 662 } 663 664 function testTooltips(parsed, entry) { 665 var i; 666 if (entry === false) { 667 return; 668 } else if (entry === true) { 669 parsed.tooltips = []; 670 for (i = 0; i < parsed.handles; i++) { 671 parsed.tooltips.push(true); 672 } 673 } else { 674 parsed.tooltips = asArray(entry); 675 if (parsed.tooltips.length !== parsed.handles) { 676 throw new Error("noUiSlider: must pass a formatter for all handles."); 677 } 678 parsed.tooltips.forEach(function (formatter) { 679 if (typeof formatter !== "boolean" && (typeof formatter !== "object" || typeof formatter.to !== "function")) { 680 throw new Error("noUiSlider: 'tooltips' must be passed a formatter or 'false'."); 681 } 682 }); 683 } 684 } 685 686 function testFormat(parsed, entry) { 687 parsed.format = entry; 688 // Any object with a to and from method is supported. 689 if (typeof entry.to === "function" && typeof entry.from === "function") { 690 return true; 691 } 692 throw new Error("noUiSlider: 'format' requires 'to' and 'from' methods."); 693 } 694 695 function testCssPrefix(parsed, entry) { 696 if (entry !== undefined && typeof entry !== "string") { 697 throw new Error("noUiSlider: 'cssPrefix' must be a string."); 698 } 699 parsed.cssPrefix = entry; 700 } 701 // Test all developer settings and parse to assumption-safe values. 702 703 function testOptions(options) { 704 // To prove a fix for #537, freeze options here. 705 // If the object is modified, an error will be thrown. 706 // Object.freeze(options); 707 var parsed = { 708 margin: 0, 709 limit: 0, 710 animate: true, 711 format: defaultFormatter, 712 }, 713 tests; 714 // Tests are executed in the order they are presented here. 715 tests = { 716 step: { 717 r: false, 718 t: testStep, 719 }, 720 start: { 721 r: true, 722 t: testStart, 723 }, 724 connect: { 725 r: true, 726 t: testConnect, 727 }, 728 direction: { 729 r: true, 730 t: testDirection, 731 }, 732 snap: { 733 r: false, 734 t: testSnap, 735 }, 736 animate: { 737 r: false, 738 t: testAnimate, 739 }, 740 range: { 741 r: true, 742 t: testRange, 743 }, 744 orientation: { 745 r: false, 746 t: testOrientation, 747 }, 748 margin: { 749 r: false, 750 t: testMargin, 751 }, 752 limit: { 753 r: false, 754 t: testLimit, 755 }, 756 behaviour: { 757 r: true, 758 t: testBehaviour, 759 }, 760 format: { 761 r: false, 762 t: testFormat, 763 }, 764 tooltips: { 765 r: false, 766 t: testTooltips, 767 }, 768 cssPrefix: { 769 r: false, 770 t: testCssPrefix, 771 }, 772 }; 773 var defaults = { 774 connect: false, 775 direction: "ltr", 776 behaviour: "tap", 777 orientation: "horizontal", 778 }; 779 // Run all options through a testing mechanism to ensure correct 780 // input. It should be noted that options might get modified to 781 // be handled properly. E.g. wrapping integers in arrays.
782 Object.keys(tests).forEach(function (name) { 783 // If the option isn't set, but it is required, throw an error. 784 if (options[name] === undefined && defaults[name] === undefined) { 785 if (tests[name].r) { 786 throw new Error("noUiSlider: '" + name + "' is required."); 787 } 788 return true; 789 } 790 tests[name].t(parsed, options[name] === undefined ? defaults[name] : options[name]); 791 }); 792 // Forward pips options 793 parsed.pips = options.pips; 794 // Pre-define the styles. 795 parsed.style = parsed.ort ? "top" : "left"; 796 return parsed; 797 } 798 799 function closure(target, options) { 800 // All variables local to 'closure' are prefixed with 'scope_' 801 var scope_Target = target, 802 scope_Locations = [-1, -1], 803 scope_Base, 804 scope_Handles, 805 scope_Spectrum = options.spectrum, 806 scope_Values = [], 807 scope_Events = {}, 808 scope_Self;
809 var cssClasses = [ 810 /* 0 */ 811 "target", 812 /* 1 */ 813 "base", 814 /* 2 */ 815 "origin", 816 /* 3 */ 817 "handle", 818 /* 4 */ 819 "horizontal", 820 /* 5 */ 821 "vertical", 822 /* 6 */ 823 "background", 824 /* 7 */ 825 "connect", 826 /* 8 */ 827 "ltr", 828 /* 9 */ 829 "rtl", 830 /* 10 */ 831 "draggable", 832 /* 11 */ 833 "", 834 /* 12 */ 835 "state-drag", 836 /* 13 */ 837 "", 838 /* 14 */ 839 "state-tap", 840 /* 15 */ 841 "active", 842 /* 16 */ 843 "", 844 /* 17 */ 845 "stacking", 846 /* 18 */ 847 "tooltip", 848 /* 19 */ 849 "", 850 /* 20 */ 851 "pips", 852 /* 21 */ 853 "marker", 854 /* 22 */ 855 "value", 856 ].map(addCssPrefix(options.cssPrefix || defaultCssPrefix)); 857 // Delimit proposed values for handle positions. 858 function getPositions(a, b, delimit) { 859 // Add movement to current position. 860 var c = a + b[0], 861 d = a + b[1]; 862 // Only alter the other position on drag, 863 // not on standard sliding. 864 if (delimit) { 865 if (c < 0) { 866 d += Math.abs(c); 867 } 868 if (d > 100) { 869 c -= d - 100; 870 } 871 // Limit values to 0 and 100. 872 return [limit(c), limit(d)]; 873 } 874 return [c, d]; 875 } 876 // Provide a clean event with standardized offset values. 877 878 function fixEvent(e, pageOffset) { 879 // Prevent scrolling and panning on touch events, while 880 // attempting to slide. The tap event also depends on this. 881 e.preventDefault(); 882 // Filter the event to register the type, which can be 883 // touch, mouse or pointer. Offset changes need to be 884 // made on an event specific basis. 885 var touch = e.type.indexOf("touch") === 0, 886 mouse = e.type.indexOf("mouse") === 0, 887 pointer = e.type.indexOf("pointer") === 0, 888 x, 889 y, 890 event = e; 891 // IE10 implemented pointer events with a prefix; 892 if (e.type.indexOf("MSPointer") === 0) { 893 pointer = true; 894 } 895 if (touch) { 896 // noUiSlider supports one movement at a time, 897 // so we can select the first 'changedTouch'. 898 x = e.changedTouches[0].pageX; 899 y = e.changedTouches[0].pageY; 900 } 901 pageOffset = pageOffset || getPageOffset(); 902 if (mouse || pointer) { 903 x = e.clientX + pageOffset.x; 904 y = e.clientY + pageOffset.y; 905 } 906 event.pageOffset = pageOffset; 907 event.points = [x, y]; 908 event.cursor = mouse || pointer; // Fix #435 909 return event; 910 } 911 // Append a handle to the base. 912 913 function addHandle(direction, index) { 914 var origin = document.createElement("div"), 915 handle = document.createElement("div"), 916 additions = ["-lower", "-upper"]; 917 if (direction) { 918 additions.reverse(); 919 }
920 addClass(handle, cssClasses[3]); 921 addClass(handle, cssClasses[3] + additions[index]); 922 addClass(origin, cssClasses[2]); 923 origin.appendChild(handle); 924 return origin; 925 } 926 // Add the proper connection classes. 927 928 function addConnection(connect, target, handles) { 929 // Apply the required connection classes to the elements 930 // that need them. Some classes are made up for several 931 // segments listed in the class list, to allow easy 932 // renaming and provide a minor compression benefit. 933 switch (connect) { 934 case 1: 935 addClass(target, cssClasses[7]); 936 addClass(handles[0], cssClasses[6]); 937 break; 938 case 3: 939 addClass(handles[1], cssClasses[6]); 940 /* falls through */ 941 case 2: 942 addClass(handles[0], cssClasses[7]); 943 /* falls through */ 944 case 0: 945 addClass(target, cssClasses[6]); 946 break; 947 } 948 } 949 // Add handles to the slider base. 950 951 function addHandles(nrHandles, direction, base) { 952 var index, 953 handles = []; 954 // Append handles. 955 for (index = 0; index < nrHandles; index += 1) { 956 // Keep a list of all added handles. 957 handles.push(base.appendChild(addHandle(direction, index))); 958 } 959 return handles; 960 } 961 // Initialize a single slider. 962 963 function addSlider(direction, orientation, target) { 964 // Apply classes and data to the target. 965 addClass(target, cssClasses[0]); 966 addClass(target, cssClasses[8 + direction]); 967 addClass(target, cssClasses[4 + orientation]); 968 var div = document.createElement("div"); 969 addClass(div, cssClasses[1]); 970 target.appendChild(div); 971 return div; 972 } 973 974 function addTooltip(handle, index) { 975 if (!options.tooltips[index]) { 976 return false; 977 } 978 var element = document.createElement("div"); 979 element.className = cssClasses[18]; 980 return handle.firstChild.appendChild(element); 981 } 982 // The tooltips option is a shorthand for using the 'update' event. 983 984 function tooltips() { 985 if (options.dir) { 986 options.tooltips.reverse(); 987 } 988 // Tooltips are added with options.tooltips in original order. 989 var tips = scope_Handles.map(addTooltip); 990 if (options.dir) { 991 tips.reverse(); 992 options.tooltips.reverse(); 993 } 994 bindEvent("update", function (f, o, r) { 995 if (tips[o]) { 996 tips[o].innerHTML = options.tooltips[o] === true ? f[o] : options.tooltips[o].to(r[o]); 997 } 998 }); 999 } 1000 1001 function getGroup(mode, values, stepped) { 1002 // Use the range. 1003 if (mode === "range" || mode === "steps") { 1004 return scope_Spectrum.xVal; 1005 } 1006 if (mode === "count") { 1007 // Divide 0 - 100 in 'count' parts. 1008 var spread = 100 / (values - 1), 1009 v, 1010 i = 0; 1011 values = []; 1012 // List these parts and have them handled as 'positions'. 1013 while ((v = i++ * spread) <= 100) { 1014 values.push(v); 1015 } 1016 mode = "positions"; 1017 } 1018 if (mode === "positions") { 1019 // Map all percentages to on-range values. 1020 return values.map(function (value) { 1021 return scope_Spectrum.fromStepping(stepped ? scope_Spectrum.getStep(value) : value); 1022 }); 1023 } 1024 if (mode === "values") { 1025 // If the value must be stepped, it needs to be converted to a percentage first. 1026 if (stepped) { 1027 return values.map(function (value) { 1028 // Convert to percentage, apply step, return to value. 1029 return scope_Spectrum.fromStepping(scope_Spectrum.getStep(scope_Spectrum.toStepping(value))); 1030 }); 1031 } 1032 // Otherwise, we can simply use the values. 1033 return values; 1034 } 1035 } 1036 1037 function generateSpread(density, mode, group) { 1038 function safeIncrement(value, increment) { 1039 // Avoid floating point variance by dropping the smallest decimal places. 1040 return (value + increment).toFixed(7) / 1; 1041 } 1042 var originalSpectrumDirection = scope_Spectrum.direction, 1043 indexes = {}, 1044 firstInRange = scope_Spectrum.xVal[0], 1045 lastInRange = scope_Spectrum.xVal[scope_Spectrum.xVal.length - 1], 1046 ignoreFirst = false, 1047 ignoreLast = false, 1048 prevPct = 0; 1049 // This function loops the spectrum in an ltr linear fashion, 1050 // while the toStepping method is direction aware. Trick it into 1051 // believing it is ltr. 1052 scope_Spectrum.direction = 0; 1053 // Create a copy of the group, sort it and filter away all duplicates. 1054 group = unique( 1055 group.slice().sort(function (a, b) { 1056 return a - b; 1057 }) 1058 ); 1059 // Make sure the range starts with the first element. 1060 if (group[0] !== firstInRange) { 1061 group.unshift(firstInRange); 1062 ignoreFirst = true; 1063 } 1064 // Likewise for the last one. 1065 if (group[group.length - 1] !== lastInRange) { 1066 group.push(lastInRange); 1067 ignoreLast = true; 1068 } 1069 group.forEach(function (current, index) { 1070 // Get the current step and the lower + upper positions. 1071 var step, 1072 i, 1073 q, 1074 low = current, 1075 high = group[index + 1], 1076 newPct, 1077 pctDifference, 1078 pctPos, 1079 type, 1080 steps, 1081 realSteps, 1082 stepsize; 1083 // When using 'steps' mode, use the provided steps. 1084 // Otherwise, we'll step on to the next subrange. 1085 if (mode === "steps") { 1086 step = scope_Spectrum.xNumSteps[index]; 1087 } 1088 // Default to a 'full' step. 1089 if (!step) { 1090 step = high - low; 1091 } 1092 // Low can be 0, so test for false. If high is undefined, 1093 // we are at the last subrange. Index 0 is already handled. 1094 if (low === false || high === undefined) { 1095 return; 1096 } 1097 // Find all steps in the subrange. 1098 for (i = low; i <= high; i = safeIncrement(i, step)) { 1099 // Get the percentage value for the current step, 1100 // calculate the size for the subrange. 1101 newPct = scope_Spectrum.toStepping(i); 1102 pctDifference = newPct - prevPct; 1103 steps = pctDifference / density; 1104 realSteps = Math.round(steps); 1105 // This ratio represents the ammount of percentage-space a point indicates. 1106 // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided. 1107 // Round the percentage offset to an even number, then divide by two 1108 // to spread the offset on both sides of the range. 1109 stepsize = pctDifference / realSteps; 1110 // Divide all points evenly, adding the correct number to this subrange. 1111 // Run up to <= so that 100% gets a point, event if ignoreLast is set. 1112 for (q = 1; q <= realSteps; q += 1) { 1113 // The ratio between the rounded value and the actual size might be ~1% off. 1114 // Correct the percentage offset by the number of points 1115 // per subrange. density = 1 will result in 100 points on the 1116 // full range, 2 for 50, 4 for 25, etc. 1117 pctPos = prevPct + q * stepsize; 1118 indexes[pctPos.toFixed(5)] = ["x", 0]; 1119 } 1120 // Determine the point type. 1121 type = group.indexOf(i) > -1 ? 1 : mode === "steps" ? 2 : 0; 1122 // Enforce the 'ignoreFirst' option by overwriting the type for 0. 1123 if (!index && ignoreFirst) { 1124 type = 0; 1125 } 1126 if (!(i === high && ignoreLast)) { 1127 // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. 1128 indexes[newPct.toFixed(5)] = [i, type]; 1129 } 1130 // Update the percentage count. 1131 prevPct = newPct; 1132 } 1133 }); 1134 // Reset the spectrum. 1135 scope_Spectrum.direction = originalSpectrumDirection; 1136 return indexes; 1137 } 1138 1139 function addMarking(spread, filterFunc, formatter) { 1140 var style = ["horizontal", "vertical"][options.ort], 1141 element = document.createElement("div"), 1142 out = "";
1143 addClass(element, cssClasses[20]); 1144 addClass(element, cssClasses[20] + "-" + style); 1145 1146 function getSize(type) { 1147 return ["-normal", "-large", "-sub"][type]; 1148 } 1149 1150 function getTags(offset, source, values) { 1151 return 'class="' + source + " " + source + "-" + style + " " + source + getSize(values[1]) + '" style="' + options.style + ": " + offset + '%"'; 1152 } 1153 1154 function addSpread(offset, values) { 1155 if (scope_Spectrum.direction) { 1156 offset = 100 - offset; 1157 } 1158 // Apply the filter function, if it is set. 1159 values[1] = values[1] && filterFunc ? filterFunc(values[0], values[1]) : values[1]; 1160 // Add a marker for every point 1161 out += "<div " + getTags(offset, cssClasses[21], values) + "></div>"; 1162 // Values are only appended for points marked '1' or '2'. 1163 if (values[1]) { 1164 out += "<div " + getTags(offset, cssClasses[22], values) + ">" + formatter.to(values[0]) + "</div>"; 1165 } 1166 } 1167 // Append all points. 1168 Object.keys(spread).forEach(function (a) { 1169 addSpread(a, spread[a]); 1170 }); 1171 element.innerHTML = out; 1172 return element; 1173 } 1174 1175 function pips(grid) { 1176 var mode = grid.mode, 1177 density = grid.density || 1, 1178 filter = grid.filter || false, 1179 values = grid.values || false, 1180 stepped = grid.stepped || false, 1181 group = getGroup(mode, values, stepped), 1182 spread = generateSpread(density, mode, group), 1183 format = grid.format || { 1184 to: Math.round, 1185 }; 1186 return scope_Target.appendChild(addMarking(spread, filter, format)); 1187 } 1188 // Shorthand for base dimensions. 1189 1190 function baseSize() { 1191 var rect = scope_Base.getBoundingClientRect(), 1192 alt = "offset" + ["Width", "Height"][options.ort]; 1193 return options.ort === 0 ? rect.width || scope_Base[alt] : rect.height || scope_Base[alt]; 1194 } 1195 // External event handling 1196 1197 function fireEvent(event, handleNumber, tap) { 1198 if (handleNumber !== undefined && options.handles !== 1) { 1199 handleNumber = Math.abs(handleNumber - options.dir); 1200 } 1201 Object.keys(scope_Events).forEach(function (targetEvent) { 1202 var eventType = targetEvent.split(".")[0]; 1203 if (event === eventType) { 1204 scope_Events[targetEvent].forEach(function (callback) { 1205 callback.call( 1206 // Use the slider public API as the scope ('this') 1207 scope_Self, 1208 // Return values as array, so arg_1[arg_2] is always valid. 1209 asArray(valueGet()), 1210 // Handle index, 0 or 1 1211 handleNumber, 1212 // Unformatted slider values 1213 asArray(inSliderOrder(Array.prototype.slice.call(scope_Values))), 1214 // Event is fired by tap, true or false 1215 tap || false, 1216 // Left offset of the handle, in relation to the slider 1217 scope_Locations 1218 ); 1219 }); 1220 } 1221 }); 1222 } 1223 // Returns the input array, respecting the slider direction configuration. 1224 1225 function inSliderOrder(values) { 1226 // If only one handle is used, return a single value. 1227 if (values.length === 1) { 1228 return values[0]; 1229 } 1230 if (options.dir) { 1231 return values.reverse(); 1232 } 1233 return values; 1234 } 1235 // Handler for attaching events trough a proxy. 1236 1237 function attach(events, element, callback, data) { 1238 // This function can be used to 'filter' events to the slider. 1239 // element is a node, not a nodeList 1240 var method = function (e) { 1241 if (scope_Target.hasAttribute("disabled")) { 1242 return false; 1243 } 1244 // Stop if an active 'tap' transition is taking place.
1245 if (hasClass(scope_Target, cssClasses[14])) { 1246 return false; 1247 } 1248 e = fixEvent(e, data.pageOffset); 1249 // Ignore right or middle clicks on start #454 1250 if (events === actions.start && e.buttons !== undefined && e.buttons > 1) { 1251 return false; 1252 } 1253 // Ignore right or middle clicks on start #454 1254 if (data.hover && e.buttons) { 1255 return false; 1256 } 1257 e.calcPoint = e.points[options.ort]; 1258 // Call the event handler with the event [ and additional data ]. 1259 callback(e, data); 1260 }, 1261 methods = []; 1262 // Bind a closure on the target for every event type. 1263 events.split(" ").forEach(function (eventName) { 1264 element.addEventListener(eventName, method, false); 1265 methods.push([eventName, method]); 1266 }); 1267 return methods; 1268 } 1269 // Handle movement on document for handle and range drag. 1270 1271 function move(event, data) { 1272 // Fix #498 1273 // Check value of .buttons in 'start' to work around a bug in IE10 mobile (data.buttonsProperty). 1274 // https://connect.microsoft.com/IE/feedback/details/927005/mobile-ie10-windows-phone-buttons-property-of-pointermove-event-always-zero 1275 // IE9 has .buttons and .which zero on mousemove. 1276 // Firefox breaks the spec MDN defines. 1277 if (navigator.appVersion.indexOf("MSIE 9") === -1 && event.buttons === 0 && data.buttonsProperty !== 0) { 1278 return end(event, data); 1279 } 1280 var handles = data.handles || scope_Handles, 1281 positions, 1282 state = false, 1283 proposal = ((event.calcPoint - data.start) * 100) / data.baseSize, 1284 handleNumber = handles[0] === scope_Handles[0] ? 0 : 1, 1285 i; 1286 // Calculate relative positions for the handles. 1287 positions = getPositions(proposal, data.positions, handles.length > 1); 1288 state = setHandle(handles[0], positions[handleNumber], handles.length === 1); 1289 if (handles.length > 1) { 1290 state = setHandle(handles[1], positions[handleNumber ? 0 : 1], false) || state; 1291 if (state) { 1292 // fire for both handles 1293 for (i = 0; i < data.handles.length; i++) { 1294 fireEvent("slide", i); 1295 } 1296 } 1297 } else if (state) { 1298 // Fire for a single handle 1299 fireEvent("slide", handleNumber); 1300 } 1301 } 1302 // Unbind move events on document, call callbacks. 1303 1304 function end(event, data) { 1305 // The handle is no longer active, so remove the class. 1306 var active = scope_Base.querySelector("." + cssClasses[15]), 1307 handleNumber = data.handles[0] === scope_Handles[0] ? 0 : 1; 1308 if (active !== null) { 1309 removeClass(active, cssClasses[15]); 1310 } 1311 // Remove cursor styles and text-selection events bound to the body. 1312 if (event.cursor) { 1313 document.body.style.cursor = ""; 1314 document.body.removeEventListener("selectstart", document.body.noUiListener); 1315 } 1316 var d = document.documentElement; 1317 // Unbind the move and end events, which are added on 'start'. 1318 d.noUiListeners.forEach(function (c) { 1319 d.removeEventListener(c[0], c[1]); 1320 }); 1321 // Remove dragging class.
1322 removeClass(scope_Target, cssClasses[12]); 1323 // Fire the change and set events. 1324 fireEvent("set", handleNumber); 1325 fireEvent("change", handleNumber); 1326 // If this is a standard handle movement, fire the end event. 1327 if (data.handleNumber !== undefined) { 1328 fireEvent("end", data.handleNumber); 1329 } 1330 } 1331 // Fire 'end' when a mouse or pen leaves the document. 1332 1333 function documentLeave(event, data) { 1334 if (event.type === "mouseout" && event.target.nodeName === "HTML" && event.relatedTarget === null) { 1335 end(event, data); 1336 } 1337 } 1338 // Bind move events on document. 1339 1340 function start(event, data) { 1341 var d = document.documentElement; 1342 // Mark the handle as 'active' so it can be styled. 1343 if (data.handles.length === 1) { 1344 addClass(data.handles[0].children[0], cssClasses[15]); 1345 // Support 'disabled' handles 1346 if (data.handles[0].hasAttribute("disabled")) { 1347 return false; 1348 } 1349 } 1350 // Fix #551, where a handle gets selected instead of dragged. 1351 event.preventDefault(); 1352 // A drag should never propagate up to the 'tap' event. 1353 event.stopPropagation(); 1354 // Attach the move and end events. 1355 var moveEvent = attach(actions.move, d, move, { 1356 start: event.calcPoint, 1357 baseSize: baseSize(), 1358 pageOffset: event.pageOffset, 1359 handles: data.handles, 1360 handleNumber: data.handleNumber, 1361 buttonsProperty: event.buttons, 1362 positions: [scope_Locations[0], scope_Locations[scope_Handles.length - 1]], 1363 }), 1364 endEvent = attach(actions.end, d, end, { 1365 handles: data.handles, 1366 handleNumber: data.handleNumber, 1367 }); 1368 var outEvent = attach("mouseout", d, documentLeave, { 1369 handles: data.handles, 1370 handleNumber: data.handleNumber, 1371 }); 1372 d.noUiListeners = moveEvent.concat(endEvent, outEvent); 1373 // Text selection isn't an issue on touch devices, 1374 // so adding cursor styles can be skipped. 1375 if (event.cursor) { 1376 // Prevent the 'I' cursor and extend the range-drag cursor. 1377 document.body.style.cursor = getComputedStyle(event.target).cursor; 1378 // Mark the target with a dragging state. 1379 if (scope_Handles.length > 1) { 1380 addClass(scope_Target, cssClasses[12]); 1381 } 1382 var f = function () { 1383 return false; 1384 }; 1385 document.body.noUiListener = f; 1386 // Prevent text selection when dragging the handles. 1387 document.body.addEventListener("selectstart", f, false); 1388 } 1389 if (data.handleNumber !== undefined) { 1390 fireEvent("start", data.handleNumber); 1391 } 1392 } 1393 // Move closest handle to tapped location. 1394 1395 function tap(event) { 1396 var location = event.calcPoint, 1397 total = 0, 1398 handleNumber, 1399 to; 1400 // The tap event shouldn't propagate up and cause 'edge' to run. 1401 event.stopPropagation(); 1402 // Add up the handle offsets. 1403 scope_Handles.forEach(function (a) { 1404 total += offset(a)[options.style]; 1405 }); 1406 // Find the handle closest to the tapped position. 1407 handleNumber = location < total / 2 || scope_Handles.length === 1 ? 0 : 1; 1408 // Check if handler is not disablet if yes set number to the next handler 1409 if (scope_Handles[handleNumber].hasAttribute("disabled")) { 1410 handleNumber = handleNumber ? 0 : 1; 1411 } 1412 location -= offset(scope_Base)[options.style]; 1413 // Calculate the new position. 1414 to = (location * 100) / baseSize(); 1415 if (!options.events.snap) { 1416 // Flag the slider as it is now in a transitional state. 1417 // Transition takes 300 ms, so re-enable the slider afterwards.
1418 addClassFor(scope_Target, cssClasses[14], 300); 1419 } 1420 // Support 'disabled' handles 1421 if (scope_Handles[handleNumber].hasAttribute("disabled")) { 1422 return false; 1423 } 1424 // Find the closest handle and calculate the tapped point. 1425 // The set handle to the new position. 1426 setHandle(scope_Handles[handleNumber], to); 1427 fireEvent("slide", handleNumber, true); 1428 fireEvent("set", handleNumber, true); 1429 fireEvent("change", handleNumber, true); 1430 if (options.events.snap) { 1431 start(event, { 1432 handles: [scope_Handles[handleNumber]], 1433 }); 1434 } 1435 } 1436 // Fires a 'hover' event for a hovered mouse/pen position. 1437 1438 function hover(event) { 1439 var location = event.calcPoint - offset(scope_Base)[options.style], 1440 to = scope_Spectrum.getStep((location * 100) / baseSize()), 1441 value = scope_Spectrum.fromStepping(to); 1442 Object.keys(scope_Events).forEach(function (targetEvent) { 1443 if ("hover" === targetEvent.split(".")[0]) { 1444 scope_Events[targetEvent].forEach(function (callback) { 1445 callback.call(scope_Self, value); 1446 }); 1447 } 1448 }); 1449 } 1450 // Attach events to several slider parts. 1451 1452 function events(behaviour) { 1453 var i, drag; 1454 // Attach the standard drag event to the handles. 1455 if (!behaviour.fixed) { 1456 for (i = 0; i < scope_Handles.length; i += 1) { 1457 // These events are only bound to the visual handle 1458 // element, not the 'real' origin element. 1459 attach(actions.start, scope_Handles[i].children[0], start, { 1460 handles: [scope_Handles[i]], 1461 handleNumber: i, 1462 }); 1463 } 1464 } 1465 // Attach the tap event to the slider base. 1466 if (behaviour.tap) { 1467 attach(actions.start, scope_Base, tap, { 1468 handles: scope_Handles, 1469 }); 1470 } 1471 // Fire hover events 1472 if (behaviour.hover) { 1473 attach(actions.move, scope_Base, hover, { 1474 hover: true, 1475 }); 1476 for (i = 0; i < scope_Handles.length; i += 1) { 1477 ["mousemove MSPointerMove pointermove"].forEach(function (eventName) { 1478 scope_Handles[i].children[0].addEventListener(eventName, stopPropagation, false); 1479 }); 1480 } 1481 } 1482 // Make the range draggable. 1483 if (behaviour.drag) { 1484 drag = [scope_Base.querySelector("." + cssClasses[7])]; 1485 addClass(drag[0], cssClasses[10]); 1486 // When the range is fixed, the entire range can 1487 // be dragged by the handles. The handle in the first 1488 // origin will propagate the start event upward, 1489 // but it needs to be bound manually on the other. 1490 if (behaviour.fixed) { 1491 drag.push(scope_Handles[drag[0] === scope_Handles[0] ? 1 : 0].children[0]); 1492 } 1493 drag.forEach(function (element) { 1494 attach(actions.start, element, start, { 1495 handles: scope_Handles, 1496 }); 1497 }); 1498 } 1499 } 1500 // Test suggested values and apply margin, step. 1501 1502 function setHandle(handle, to, noLimitOption) { 1503 var trigger = handle !== scope_Handles[0] ? 1 : 0, 1504 lowerMargin = scope_Locations[0] + options.margin, 1505 upperMargin = scope_Locations[1] - options.margin, 1506 lowerLimit = scope_Locations[0] + options.limit, 1507 upperLimit = scope_Locations[1] - options.limit; 1508 // For sliders with multiple handles, 1509 // limit movement to the other handle. 1510 // Apply the margin option by adding it to the handle positions. 1511 if (scope_Handles.length > 1) { 1512 to = trigger ? Math.max(to, lowerMargin) : Math.min(to, upperMargin); 1513 } 1514 // The limit option has the opposite effect, limiting handles to a 1515 // maximum distance from another. Limit must be > 0, as otherwise 1516 // handles would be unmoveable. 'noLimitOption' is set to 'false' 1517 // for the .val() method, except for pass 4/4. 1518 if (noLimitOption !== false && options.limit && scope_Handles.length > 1) { 1519 to = trigger ? Math.min(to, lowerLimit) : Math.max(to, upperLimit); 1520 } 1521 // Handle the step option. 1522 to = scope_Spectrum.getStep(to); 1523 // Limit to 0/100 for .val input, trim anything beyond 7 digits, as 1524 // JavaScript has some issues in its floating point implementation. 1525 to = limit(parseFloat(to.toFixed(7))); 1526 // Return false if handle can't move 1527 if (to === scope_Locations[trigger]) { 1528 return false; 1529 } 1530 // Set the handle to the new position. 1531 // Use requestAnimationFrame for efficient painting. 1532 // No significant effect in Chrome, Edge sees dramatic 1533 // performace improvements. 1534 if (window.requestAnimationFrame) { 1535 window.requestAnimationFrame(function () { 1536 handle.style[options.style] = to + "%"; 1537 }); 1538 } else { 1539 handle.style[options.style] = to + "%"; 1540 } 1541 // Force proper handle stacking 1542 if (!handle.previousSibling) {
1543 removeClass(handle, cssClasses[17]); 1544 if (to > 50) { 1545 addClass(handle, cssClasses[17]); 1546 } 1547 } 1548 // Update locations. 1549 scope_Locations[trigger] = to; 1550 // Convert the value to the slider stepping/range. 1551 scope_Values[trigger] = scope_Spectrum.fromStepping(to); 1552 fireEvent("update", trigger); 1553 return true; 1554 } 1555 // Loop values from value method and apply them. 1556 1557 function setValues(count, values) { 1558 var i, trigger, to; 1559 // With the limit option, we'll need another limiting pass. 1560 if (options.limit) { 1561 count += 1; 1562 } 1563 // If there are multiple handles to be set run the setting 1564 // mechanism twice for the first handle, to make sure it 1565 // can be bounced of the second one properly. 1566 for (i = 0; i < count; i += 1) { 1567 trigger = i % 2; 1568 // Get the current argument from the array. 1569 to = values[trigger]; 1570 // Setting with null indicates an 'ignore'. 1571 // Inputting 'false' is invalid. 1572 if (to !== null && to !== false) { 1573 // If a formatted number was passed, attemt to decode it. 1574 if (typeof to === "number") { 1575 to = String(to); 1576 } 1577 to = options.format.from(to); 1578 // Request an update for all links if the value was invali
1578d. 1579 // Do so too if setting the handle fails. 1580 if (to === false || isNaN(to) || setHandle(scope_Handles[trigger], scope_Spectrum.toStepping(to), i === 3 - options.dir) === false) { 1581 fireEvent("update", trigger); 1582 } 1583 } 1584 } 1585 } 1586 // Set the slider value. 1587 1588 function valueSet(input) { 1589 var count, 1590 values = asArray(input), 1591 i; 1592 // The RTL settings is implemented by reversing the front-end, 1593 // internal mechanisms are the same. 1594 if (options.dir && options.handles > 1) { 1595 values.reverse(); 1596 } 1597 // Animation is optional. 1598 // Make sure the initial values where set before using animated placement. 1599 if (options.animate && scope_Locations[0] !== -1) { 1600 addClassF
1600or(scope_Target, cssClasses[14], 300); 1601 } 1602 // Determine how often to set the handles. 1603 count = scope_Handles.length > 1 ? 3 : 1; 1604 if (values.length === 1) { 1605 count = 1; 1606 } 1607 setValues(count, values); 1608 // Fire the 'set' event for both handles. 1609 for (i = 0; i < scope_Handles.length; i++) { 1610 // Fire the event only for handles that received a new value, as per #579 1611 if (values[i] !== null) { 1612 fireEvent("set", i); 1613 } 1614 } 1615 } 1616 // Get the slider value. 1617 1618 function valueGet() { 1619 var i, 1620 retour = []; 1621 // Get the value from all handles. 1622 for (i = 0; i < options.handles; i += 1) { 1623 retour[i] = options.format.to(scope_Values[i]); 1624 } 1625 return inSliderOrder(retour); 1626 } 1627 // Removes classes from the root and empties it. 1628 1629 function destroy() { 1630 cssClasses.forEach(function (cls) { 1631 if (!cls) { 1632 return; 1633 } // Ignore empty classes 1634 removeClass(scope_Target, cls); 1635 }); 1636 while (scope_Target.firstChild) { 1637 scope_Target.removeChild(scope_Target.firstChild); 1638 } 1639 delete scope_Target.noUiSlider; 1640 } 1641 // Get the current step size for the slider. 1642 1643 function getCurrentStep() { 1644 // Check all locations, map them to their stepping point. 1645 // Get the step point, then find it in the input list. 1646 var retour = scope_Locations.map(function (location, index) { 1647 var step = scope_Spectrum.getApplicableStep(location), 1648 // As per #391, the comparison for the decrement step can have some rounding issues. 1649 // Round the value to the precision used in the step. 1650 stepDecimals = countDecimals(String(step[2])), 1651 // Get the current numeric value 1652 value = scope_Values[index], 1653 // To move the slider 'one step up', the current step value needs to be added. 1654 // Use null if we are at the maximum slider value. 1655 increment = location === 100 ? null : step[2], 1656 // Going 'one step down' might put the slider in a different sub-range, so we 1657 // need to switch between the current or the previous step. 1658 prev = Number((value - step[2]).toFixed(stepDecimals)), 1659 // If the value fits the step, return the current step value. Otherwise, use the 1660 // previous step. Return null if the slider is at its minimum value. 1661 decrement = location === 0 ? null : prev >= step[1] ? step[2] : step[0] || false; 1662 return [decrement, increment]; 1663 }); 1664 // Return values in the proper order. 1665 return inSliderOrder(retour); 1666 } 1667 // Attach an event to this slider, possibly including a namespace 1668 1669 function bindEvent(namespacedEvent, callback) { 1670 scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || []; 1671 scope_Events[namespacedEvent].push(callback); 1672 // If the event bound is 'update,' fire it immediately for all handles. 1673 if (namespacedEvent.split(".")[0] === "update") { 1674 scope_Handles.forEach(function (a, index) { 1675 fireEvent("update", index); 1676 }); 1677 } 1678 } 1679 // Undo attachment of event 1680 1681 function removeEvent(namespacedEvent) { 1682 var event = namespacedEvent.split(".")[0], 1683 namespace = namespacedEvent.substring(event.length);
1684 Object.keys(scope_Events).forEach(function (bind) { 1685 var tEvent = bind.split(".")[0], 1686 tNamespace = bind.substring(tEvent.length); 1687 if ((!event || event === tEvent) && (!namespace || namespace === tNamespace)) { 1688 delete scope_Events[bind]; 1689 } 1690 }); 1691 } 1692 // Updateable: margin, limit, step, range, animate, snap 1693 1694 function updateOptions(optionsToUpdate) { 1695 var v = valueGet(), 1696 i, 1697 newOptions = testOptions({ 1698 start: [0, 0], 1699 margin: optionsToUpdate.margin, 1700 limit: optionsToUpdate.limit, 1701 step: optionsToUpdate.step, 1702 range: optionsToUpdate.range, 1703 animate: optionsToUpdate.animate, 1704 snap: optionsToUpdate.snap === undefined ? options.snap : optionsToUpdate.snap, 1705 }); 1706 ["margin", "limit", "step", "range", "animate"].forEach(function (name) { 1707 if (optionsToUpdate[name] !== undefined) { 1708 options[name] = optionsToUpdate[name]; 1709 } 1710 }); 1711 // Save current spectrum direction as testOptions in testRange call 1712 // doesn't rely on current direction 1713 newOptions.spectrum.direction = scope_Spectrum.direction; 1714 scope_Spectrum = newOptions.spectrum; 1715 // Invalidate the current positioning so valueSet forces an update. 1716 scope_Locations = [-1, -1]; 1717 valueSet(v); 1718 for (i = 0; i < scope_Handles.length; i++) { 1719 fireEvent("update", i); 1720 } 1721 } 1722 // Throw an error if the slider was already initialized. 1723 if (scope_Target.noUiSlider) { 1724 throw new Error("Slider was already initialized."); 1725 } 1726 // Create the base element, initialise HTML and set classes. 1727 // Add handles and links. 1728 scope_Base = addSlider(options.dir, options.ort, scope_Target); 1729 scope_Handles = addHandles(options.handles, options.dir, scope_Base); 1730 // Set the connect classes. 1731 addConnection(options.connect, scope_Target, scope_Handles); 1732 if (options.pips) { 1733 pips(options.pips); 1734 } 1735 if (options.tooltips) { 1736 tooltips(); 1737 } 1738 scope_Self = { 1739 destroy: destroy, 1740 steps: getCurrentStep, 1741 on: bindEvent, 1742 off: removeEvent, 1743 get: valueGet, 1744 set: valueSet, 1745 updateOptions: updateOptions, 1746 options: options, // Issue #600 1747 target: scope_Target, // Issue #597 1748 pips: pips, // Issue #594 1749 }; 1750 // Attach user events. 1751 events(options.events); 1752 return scope_Self; 1753 } 1754 // Run the standard initializer 1755 1756 function initialize(target, originalOptions) { 1757 if (!target.nodeName) { 1758 throw new Error("noUiSlider.create requires a single element."); 1759 } 1760 // Test the options and create the slider environment; 1761 var options = testOptions(originalOptions, target), 1762 slider = closure(target, options); 1763 // Use the public value method to set the start values. 1764 slider.set(options.start); 1765 target.noUiSlider = slider; 1766 return slider; 1767 } 1768 // Use an object instead of a function for future expansibility; 1769 return { 1770 create: initialize, 1771 }; 1772 }); 1773 // wNumb number formatter: https://refreshless.com/wnumb/ 1774 (function () { 1775 "use strict"; 1776 var /** @const */ 1777 FormatOptions = ["decimals", "thousand", "mark", "prefix", "postfix", "encoder", "decoder", "negativeBefore", "negative", "edit", "undo"]; 1778 // General 1779 // Reverse a string 1780 function strReverse(a) { 1781 return a.split("").reverse().join(""); 1782 } 1783 // Check if a string starts with a specified prefix. 1784 1785 function strStartsWith(input, match) { 1786 return input.substring(0, match.length) === match; 1787 } 1788 // Check is a string ends in a specified postfix. 1789 1790 function strEndsWith(input, match) { 1791 return input.slice(-1 * match.length) === match; 1792 } 1793 // Throw an error if formatting options are incompatible. 1794
vendor: 5,571 bytes, lines 1795-1927
1795 function throwEqualError(F, a, b) { 1796 if ((F[a] || F[b]) && F[a] === F[b]) { 1797 throw new Error(a); 1798 } 1799 } 1800 // Check if a number is finite and not NaN 1801 1802 function isValidNumber(input) { 1803 return typeof input === "number" && isFinite(input); 1804 } 1805 // Provide rounding-accurate toFixed method. 1806 1807 function toFixed(value, decimals) { 1808 var scale = Math.pow(10, decimals); 1809 return (Math.round(value * scale) / scale).toFixed(decimals); 1810 } 1811 // Formatting 1812 // Accept a number as input, output formatted string. 1813 1814 function formatTo(decimals, thousand, mark, prefix, postfix, encoder, decoder, negativeBefore, negative, edit, undo, input) { 1815 var originalInput = input, 1816 inputIsNegative, 1817 inputPieces, 1818 inputBase, 1819 inputDecimals = "", 1820 output = ""; 1821 // Apply user encoder to the input. 1822 // Expected outcome: number. 1823 if (encoder) { 1824 input = encoder(input); 1825 } 1826 // Stop if no valid number was provided, the number is infinite or NaN. 1827 if (!isValidNumber(input)) { 1828 return false; 1829 } 1830 // Rounding away decimals might cause a value of -0 1831 // when using very small ranges. Remove those cases. 1832 if (decimals !== false && parseFloat(input.toFixed(decimals)) === 0) { 1833 input = 0; 1834 } 1835 // Formatting is done on absolute numbers, 1836 // decorated by an optional negative symbol. 1837 if (input < 0) { 1838 inputIsNegative = true; 1839 input = Math.abs(input); 1840 } 1841 // Reduce the number of decimals to the specified option. 1842 if (decimals !== false) { 1843 input = toFixed(input, decimals); 1844 } 1845 // Transform the number into a string, so it can be split. 1846 input = input.toString(); 1847 // Break the number on the decimal separator. 1848 if (input.indexOf(".") !== -1) { 1849 inputPieces = input.split("."); 1850 inputBase = inputPieces[0]; 1851 if (mark) { 1852 inputDecimals = mark + inputPieces[1]; 1853 } 1854 } else { 1855 // If it isn't split, the entire number will do. 1856 inputBase = input; 1857 } 1858 // Group numbers in sets of three. 1859 if (thousand) { 1860 inputBase = strReverse(inputBase).match(/.{1,3}/g); 1861 inputBase = strReverse(inputBase.join(strReverse(thousand))); 1862 } 1863 // If the number is negative, prefix with negation symbol. 1864 if (inputIsNegative && negativeBefore) { 1865 output += negativeBefore; 1866 } 1867 // Prefix the number 1868 if (prefix) { 1869 output += prefix; 1870 } 1871 // Normal negative option comes after the prefix. Defaults to '-'. 1872 if (inputIsNegative && negative) { 1873 output += negative; 1874 } 1875 // Append the actual number. 1876 output += inputBase; 1877 output += inputDecimals; 1878 // Apply the postfix. 1879 if (postfix) { 1880 output += postfix; 1881 } 1882 // Run the output through a user-specified post-formatter. 1883 if (edit) { 1884 output = edit(output, originalInput); 1885 } 1886 // All done. 1887 return output; 1888 } 1889 // Accept a sting as input, output decoded number. 1890 1891 function formatFrom(decimals, thousand, mark, prefix, postfix, encoder, decoder, negativeBefore, negative, edit, undo, input) { 1892 var originalInput = input, 1893 inputIsNegative, 1894 output = ""; 1895 // User defined pre-decoder. Result must be a non empty string. 1896 if (undo) { 1897 input = undo(input); 1898 } 1899 // Test the input. Can't be empty. 1900 if (!input || typeof input !== "string") { 1901 return false; 1902 } 1903 // If the string starts with the negativeBefore value: remove it. 1904 // Remember is was there, the number is negative. 1905 if (negativeBefore && strStartsWith(input, negativeBefore)) { 1906 input = input.replace(negativeBefore, ""); 1907 inputIsNegative = true; 1908 } 1909 // Repeat the same procedure for the prefix. 1910 if (prefix && strStartsWith(input, prefix)) { 1911 input = input.replace(prefix, ""); 1912 } 1913 // And again for negative. 1914 if (negative && strStartsWith(input, negative)) { 1915 input = input.replace(negative, ""); 1916 inputIsNegative = true; 1917 } 1918 // Remove the postfix. 1919 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice 1920 if (postfix && strEndsWith(input, postfix)) { 1921 input = input.slice(0, -1 * postfix.length); 1922 } 1923 // Remove the thousand grouping. 1924 if (thousand) { 1925 input = input.split(thousand).join(""); 1926 } 1927 // Set the decimal separator back to period.
1928 if (mark) { 1929 input = input.replace(mark, "."); 1930 } 1931 // Prepend the negative symbol. 1932 if (inputIsNegative) { 1933 output += "-"; 1934 } 1935 // Add the number 1936 output += input; 1937 // Trim all non-numeric characters (allow '.' and '-'); 1938 output = output.replace(/[^0-9\.\-.]/g, ""); 1939 // The value contains no parse-able number. 1940 if (output === "") { 1941 return false; 1942 } 1943 // Covert to number. 1944 output = Number(output); 1945 // Run the user-specified post-decoder. 1946 if (decoder) { 1947 output = decoder(output); 1948 } 1949 // Check is the output is valid, otherwise: return false. 1950 if (!isValidNumber(output)) { 1951 return false; 1952 } 1953 return output; 1954 } 1955 // Framework 1956 // Validate formatting options 1957 1958 function validate(inputOptions) { 1959 var i, 1960 optionName, 1961 optionValue, 1962 filteredOptions = {}; 1963 for (i = 0; i < FormatOptions.length; i += 1) { 1964 optionName = FormatOptions[i]; 1965 optionValue = inputOptions[optionName]; 1966 if (optionValue === undefined) { 1967 // Only default if negativeBefore isn't set. 1968 if (optionName === "negative" && !filteredOptions.negativeBefore) { 1969 filteredOptions[optionName] = "-"; 1970 // Don't set a default for mark when 'thousand' is set. 1971 } else if (optionName === "mark" && filteredOptions.thousand !== ".") { 1972 filteredOptions[optionName] = "."; 1973 } else { 1974 filteredOptions[optionName] = false; 1975 } 1976 // Floating points in JS are stable up to 7 decimals. 1977 } else if (optionName === "decimals") { 1978 if (optionValue >= 0 && optionValue < 8) { 1979 filteredOptions[optionName] = optionValue; 1980 } else { 1981 throw new Error(optionName); 1982 } 1983 // These options, when provided, must be functions. 1984 } else if (optionName === "encoder" || optionName === "decoder" || optionName === "edit" || optionName === "undo") { 1985 if (typeof optionValue === "function") { 1986 filteredOptions[optionName] = optionValue; 1987 } else { 1988 throw new Error(optionName); 1989 } 1990 // Other options are strings. 1991 } else { 1992 if (typeof optionValue === "string") { 1993 filteredOptions[optionName] = optionValue; 1994 } else { 1995 throw new Error(optionName); 1996 } 1997 } 1998 } 1999 // Some values can't be extracted from a 2000 // string if certain combinations are present. 2001 throwEqualError(filteredOptions, "mark", "thousand"); 2002 throwEqualError(filteredOptions, "prefix", "negative"); 2003 throwEqualError(filteredOptions, "prefix", "negativeBefore"); 2004 return filteredOptions; 2005 } 2006 // Pass all options as function arguments 2007 2008 function passAll(options, method, input) { 2009 var i, 2010 args = []; 2011 // Add all options in order of FormatOptions 2012 for (i = 0; i < FormatOptions.length; i += 1) { 2013 args.push(options[FormatOptions[i]]); 2014 } 2015 // Append the input, then call the method, presenting all 2016 // options as arguments. 2017 args.push(input); 2018 return method.apply("", args); 2019 } 2020 /** @constructor */ 2021 2022 function wNumb(options) { 2023 if (!(this instanceof wNumb)) { 2024 return new wNumb(options); 2025 } 2026 if (typeof options !== "object") { 2027 return; 2028 } 2029 options = validate(options); 2030 // Call 'formatTo' with proper arguments. 2031 this.to = function (input) { 2032 return passAll(options, formatTo, input); 2033 }; 2034 // Call 'formatFrom' with proper arguments. 2035 this.from = function (input) { 2036 return passAll(options, formatFrom, input); 2037 }; 2038 } 2039 /** @export */ 2040 window.wNumb = wNumb; 2041 })(); 2042}); 2043 2044// Timer 2045document.addEventListener('DOMContentLoaded', (event) => { 2046 // Set the date we're counting down to 2047 var countDownDate = new Date("May 5, 2025 15:37:25").getTime(); 2048 2049 // Update the count down every 1 second 2050 var x = setInterval(function() { 2051 2052 // Get today's date and time 2053 var now = new Date().getTime(); 2054 2055 // Find the distance between now and the count down date 2056 var distance = countDownDate - now; 2057 2058 // Time calculations for days, hours, minutes and seconds 2059 var days = Math.floor(distance / (1000 * 60 * 60 * 24)); 2060 var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); 2061 var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); 2062 var seconds = Math.floor((distance % (1000 * 60)) / 1000); 2063 2064 // Output the result in an element with id="countdown" 2065 document.getElementById("countdown").innerHTML = days + "d " + hours + "h " + 2066 minutes + "m " + seconds + "s "; 2067 2068 // If the count down is over, write some text 2069 if (distance < 0) { 2070 clearInterval(x); 2071 document.getElementById("countdown").innerHTML = "EXPIRED"; 2072 } 2073 }, 1000); 2074}); 2075 2076// Dark Mode 2077document.addEventListener('DOMContentLoaded', (event) => { 2078 const toggleBtn = document.getElementById('darkMode'); 2079 const body = document.body; 2080 2081 // Check for saved theme preference in localStorage 2082 const savedTheme = localStorage.getItem('theme'); 2083 if (savedTheme) { 2084 body.className=savedTheme; 2085 } else { 2086 // If no theme preference is saved, default to light mode 2087 body.className='crypt-dark'; 2088 } 2089 2090 toggleBtn.addEventListener('click', () => { 2091 if (body.classList.contains('crypt-light')) { 2092 body.classList.replace('crypt-light', 'crypt-dark'); 2093 localStorage.setItem('theme', 'crypt-dark'); // Save the theme to localStorage 2094 } else { 2095 body.classList.replace('crypt-dark', 'crypt-light'); 2096 localStorage.setItem('theme', 'crypt-light'); // Save the theme to localStorage 2097 } 2098 });
2099}); 2100 2101// Dark Mode button 2102document.addEventListener("DOMContentLoaded", (event) => { 2103 //check to see if this current container is within viewport 2104 document.querySelector(".mode").addEventListener("click", () => { 2105 document.querySelector(".sun-logo").classList.toggle("animate-sun"); 2106 document.querySelector(".moon-logo").classList.toggle("animate-moon"); 2107 document.querySelector("body").classList.toggle("theme"); 2108 }) 2109}); 2110 2111/* Scroll-based animations */ 2112document.addEventListener('DOMContentLoaded', (event) => { 2113 let $animation_elements = $('.animation-element'); 2114 let $window = $(window); 2115 2116 function check_if_in_view() { 2117 let window_height = $window.height(); 2118 let window_top_position = $window.scrollTop(); 2119 let window_bottom_position = (window_top_position + window_height); 2120 2121 $.each($animation_elements, function() { 2122 let $element = $(this); 2123 let element_height = $element.outerHeight(); 2124 let element_top_position = $element.offset().top; 2125 let element_bottom_position = (element_top_position + element_height); 2126 2127 //check to see if this current container is within viewport 2128 if ((element_bottom_position >= window_top_position) && 2129 (element_top_position <= window_bottom_position)) { 2130 $element.addClass('in-view'); 2131 } else { 2132 $element.removeClass('in-view'); 2133 } 2134 }); 2135 } 2136 2137 $window.on('scroll resize', check_if_in_view); 2138 $('#myModal').on('shown.bs.modal', function(e) { 2139 check_if_in_view(); 2140 }); 2141 $window.trigger('scroll'); 2142}); 2143 2144/* Tooltip Trigger */ 2145document.addEventListener('DOMContentLoaded', (event) => { 2146 const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]') 2147 const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)) 2148}); 2149 2150/* Encrypt Value */ 2151document.addEventListener('DOMContentLoaded', (event) => { 2152 let crypt_state = false; 2153 let _el = document.getElementById('encrypt'); 2154 if (_el) { 2155 _el.addEventListener('click', function() { 2156 crypt_state = !crypt_state; 2157 let elements = document.querySelectorAll(".encrypted"); 2158 elements.forEach((el) => el.innerText = crypt_state ? "***" : el.dataset.content); 2159 }); 2160 } 2161 2162 window.addEventListener("DOMContentLoaded", function() { 2163 let elements = document.querySelectorAll(".encrypted"); 2164 elements.forEach((el) => el.setAttribute("data-content", el.innerText)); 2165 }) 2166}); 2167 2168/*--- Chart ---*/ 2169document.addEventListener('DOMContentLoaded', (event) => { 2170 // colors 2171 var colors = ['#007bff', '#28a745', '#ffcd2d', '#7000f0', '#dc3545', '#c90097']; 2172 2173 var donutOptions = { 2174 cutoutPercentage: 60, 2175 legend: { 2176 position: 'right', 2177 padding: 0, 2178 labels: { 2179 pointStyle: 'circle', 2180 usePointStyle: true 2181 } 2182 } 2183 }; 2184 2185 // Configuration 2186 var chDonutData = { 2187 labels: ['BTC', 'DOT', 'ETH', 'DOGE'], 2188 datasets: [{ 2189 backgroundColor: colors.slice(0, 4), 2190 borderWidth: 0, 2191 data: [40, 11, 40, 20] 2192 }] 2193 }; 2194 2195 var chDonut = document.getElementById("chDonut"); 2196 if (chDonut) { 2197 new Chart(chDonut, { 2198 type: 'pie', 2199 data: chDonutData, 2200 options: donutOptions 2201 }); 2202 } 2203}); 2204 2205/*--- Dropdown on Hover ---*/ 2206document.addEventListener('DOMContentLoaded', (event) => { 2207 $(document).ready(function() { 2208 $('.dropdown').hover(function() { 2209 $(this).addClass('show'); 2210 $(this).find('.dropdown-menu-shows').addClass('show'); 2211 }, function() { 2212 $(this).removeClass('show'); 2213 $(this).find('.dropdown-menu-shows').removeClass('show'); 2214 }); 2215 }); 2216 2217 $(document).ready(function() { 2218 $('.dropdown').hover(function() { 2219 $(this).find('.dropdown-menu-shows') 2220 .stop(true, true).delay(50).fadeIn(200); 2221 }, function() { 2222 $(this).find('.dropdown-menu-shows') 2223 .stop(true, true).delay(50).fadeOut(200); 2224 }); 2225 }); 2226}); 2227 2228// Favorite Button 2229document.addEventListener('DOMContentLoaded', (event) => { 2230 $('.favme').click(function() { 2231 $(this).toggleClass('active'); 2232 }); 2233 2234 /* when a user clicks, toggle the 'is-animating' class */ 2235 $(".favme").on('click touchstart', function() { 2236 $(this).toggleClass('is_animating'); 2237 }); 2238 2239 /*when the animation is over, remove the class*/ 2240 $(".favme").on('animationend', function() { 2241 $(this).toggleClass('is_animating'); 2242 }); 2243}); 2244 2245// Scrollspy 2246document.addEventListener('DOMContentLoaded', (event) => { 2247 document.querySelectorAll('#nav-tab>[data-bs-toggle="tab"]').forEach(el => { 2248 el.addEventListener('shown.bs.tab', () => { 2249 const target = el.getAttribute('data-bs-target') 2250 const scrollElem = document.querySelector(`${target} [data-bs-spy="scroll"]`) 2251 bootstrap.ScrollSpy.getOrCreateInstance(scrollElem).refresh() 2252 }) 2253 }) 2254}); 2255 2256// ApexChart 2257document.addEventListener('DOMContentLoaded', (event) => { 2258 const chartOptions = { 2259 chart: { 2260 type: "area", 2261 height: 120, 2262 toolbar: { 2263 show: false 2264 }, 2265 zoom: { 2266 enabled: false 2267 } 2268 }, 2269 colors: ["#0d6efd"], 2270 series: [{ 2271 name: "Total Assets", 2272 data: [0.00, 0.00, 4.55, 0.00, 0.00, 0.00] 2273 }], 2274 dataLabels: { 2275 enabled: false 2276 }, 2277 stroke: { 2278 width: 3, 2279 curve: "smooth" 2280 }, 2281 fill: { 2282 type: "gradient", 2283 gradient: { 2284 shadeIntensity: 0, 2285 opacityFrom: 0.4, 2286 opacityTo: 0, 2287 stops: [0, 90, 100] 2288 } 2289 }, 2290 xaxis: { 2291 categories: ["Feb", "Apr", "Jun", "Aug", "Oct", "Dec"], 2292 axisBorder: { 2293 show: false 2294 }, 2295 labels: { 2296 style: { 2297 colors: "#adb5bd", 2298 fontFamily: "inter" 2299 } 2300 } 2301 }, 2302 yaxis: { 2303 show: false 2304 }, 2305 grid: { 2306 borderColor: "rgba(0, 0, 0, 0, 0)", 2307 padding: { 2308 top: -30, 2309 bottom: -8, 2310 left: 12, 2311 right: 12 2312 } 2313 }, 2314 tooltip: { 2315 enabled: true, 2316 y: { 2317 formatter: value => `${value} BTC` 2318 }, 2319 style: { 2320 fontFamily: "inter" 2321 } 2322 }, 2323 markers: { 2324 show: true 2325 } 2326 } 2327 2328 const chart = new ApexCharts(document.querySelector(".chart-area"), chartOptions) 2329 chart.render() 2330}); 2331 2332//Piechart 2333document.addEventListener('DOMContentLoaded', (event) => { 2334 var chart; 2335var legend; 2336 2337var chartData = [{ 2338 country: "Ecosystem", 2339 value: 260}, 2340{ 2341 country: "Airdrop", 2342 value: 201}, 2343{ 2344 country: "Private Sale", 2345 value: 65}, 2346{ 2347 country: "Contibutors", 2348 value: 39}, 2349{ 2350 country: "Advisory", 2351 value: 19}, 2352{ 2353 country: "Launchpool", 2354 value: 10}]; 2355 2356AmCharts.ready(function() { 2357 // PIE CHART 2358 chart = new AmCharts.AmPieChart(); 2359 chart.dataProvider = chartData; 2360 chart.titleField = "country"; 2361 chart.valueField = "value"; 2362 chart.outlineColor = ""; 2363 chart.outlineAlpha = 0.8; 2364 chart.outlineThickness = 2; 2365 // this makes the chart 3D 2366 chart.depth3D = 20; 2367 chart.angle = 30; 2368 2369 // WRITE 2370 chart.write("chartdiv"); 2371}); 2372}); 2373 2374// Price Meter 2375document.addEventListener('DOMContentLoaded', (event) => { 2376 (function() { 2377 var leaseMeter, meterBar, meterBarWidth, meterValue, progressNumber; 2378 2379 /*Get value of value attribute*/ 2380 var valueGetter = function() { 2381 leaseMeter = document.getElementsByClassName('leaseMeter'); 2382 for (var i = 0; i < leaseMeter.length; i++) { 2383 meterValue = leaseMeter[i].getAttribute('value'); 2384 return meterValue; 2385 } 2386 } 2387 2388 /*Convert value of value attribute to percentage*/ 2389 var getPercent = function() { 2390 meterBarWidth = parseInt(valueGetter()) * 0.10; 2391 meterBarWidth.toString; 2392 meterBarWidth = meterBarWidth + "%"; 2393 return meterBarWidth; 2394 } 2395 2396 /*Apply percentage to width of .meterBar*/ 2397 var adjustWidth = function() { 2398 meterBar = document.getElementsByClassName('meterBar'); 2399 for (var i = 0; i < meterBar.length; i++) { 2400 meterBar[i].style['width'] = getPercent(); 2401 } 2402 } 2403 2404 /*Update value indicator*/ 2405 var indicUpdate = function() { 2406 progressNumber = document.getElementsByClassName('progressNumber'); 2407 for (var i = 0; i < progressNumber.length; i++) { 2408 progressNumber[i].innerHTML = valueGetter(); 2409 } 2410 } 2411 2412 adjustWidth(); 2413 indicUpdate(); 2414 })();
2415}); 2416 2417// Validation 2418document.addEventListener('DOMContentLoaded', (event) => { 2419 (() => { 2420 'use strict' 2421 2422 // Fetch all the forms we want to apply custom Bootstrap validation styles to 2423 const forms = document.querySelectorAll('.needs-validation') 2424 2425 // Loop over them and prevent submission 2426 Array.from(forms).forEach(form => { 2427 form.addEventListener('submit', event => { 2428 if (!form.checkValidity()) { 2429 event.preventDefault() 2430 event.stopPropagation() 2431 } 2432 2433 form.classList.add('was-validated') 2434 }, false) 2435 }) 2436 })(); 2437}); 2438 2439// Password Strength 2440document.addEventListener('DOMContentLoaded', (event) => { 2441 $('#password').keyup(function(e) { 2442 var strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g"); 2443 var mediumRegex = new RegExp("^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g"); 2444 var enoughRegex = new RegExp("(?=.{6,}).*", "g"); 2445 if (false == enoughRegex.test($(this).val())) { 2446 $('#passstrength').html('More characters'); 2447 document.getElementById("passstrength").style.color = "#dc3545"; 2448 } else if (strongRegex.test($(this).val())) { 2449 document.getElementById("passstrength").style.color = "#0d6efd"; 2450 $('#passstrength').html('Strong!'); 2451 } else if (mediumRegex.test($(this).val())) { 2452 document.getElementById("passstrength").style.color = "#0dcaf0"; 2453 $('#passstrength').html('Medium!'); 2454 } else { 2455 document.getElementById("passstrength").style.color = "#ffc107"; 2456 $('#passstrength').html('Weak!'); 2457 } 2458 return true; 2459 }); 2460}); 2461 2462 2463// Password Visibility 2464var state = false; 2465 2466function toggle() { 2467 if (state) { 2468 document.getElementById("password").setAttribute("type", "password"); 2469 document.getElementById("eye").style.color = "#7a797e"; 2470 state = false; 2471 } else { 2472 document.getElementById("password").setAttribute("type", "text"); 2473 document.getElementById("eye").style.color = "#0d6efd"; 2474 state = true; 2475 } 2476} 2477 2478 2479// Newsletter Signup Button 2480document.addEventListener('DOMContentLoaded', (event) => { 2481 (function () { 2482 $(document).mousemove(function (e) { 2483 var mX = e.pageX; 2484 var mY = e.pageY; 2485 var from = { x: mX, y: mY }; 2486 var $n = $(".signup-btn"); 2487 var off = $n.offset(), 2488 nx1 = off.left, 2489 ny1 = off.top, 2490 nx2 = nx1 + $n.outerWidth(), 2491 ny2 = ny1 + $n.outerHeight(), 2492 maxX1 = Math.max(mX, nx1), 2493 minX2 = Math.min(mX, nx2), 2494 maxY1 = Math.max(mY, ny1), 2495 minY2 = Math.min(mY, ny2), 2496 intersectX = minX2 >= maxX1, 2497 intersectY = minY2 >= maxY1, 2498 to = { 2499 x: intersectX ? mX : nx2 < mX ? nx2 : nx1, 2500 y: intersectY ? mY : ny2 < mY ? ny2 : ny1, 2501 }; 2502 var distX = to.x - from.x, 2503 distY = to.y - from.y, 2504 hypot = Math.sqrt(distX * distX + distY * distY); 2505 final = Math.round(hypot); 2506 2507 $("#distance_text").text(final); //this will output 0 when next to your element. 2508 2509 if (final == 0) { 2510 // document.getElementById("news").style.color = "yellow" 2511 document.getElementById("news").classList.add("active"); 2512 } else { 2513 // document.getElementById("news").style.color = "black" 2514 document.getElementById("news").classList.remove("active"); 2515 } 2516 }); 2517 })(); 2518}); 2519 2520// Popovers 2521document.addEventListener('DOMContentLoaded', (event) => { 2522 const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]') 2523 const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl)) 2524}); 2525 2526//Sidebar Collapse 2527document.addEventListener("DOMContentLoaded", function() { 2528 let arrow_el = document.getElementById("sidebar-collapse"); 2529 let body_el = document.querySelector("body"); 2530 if(!arrow_el || !body_el) return null; 2531 2532 arrow_el.addEventListener("click", function() { 2533 body_el.classList.toggle("sidebar-close"); 2534 });
2535 }); 2536 2537 2538//mobile sidebar toggle 2539 document.addEventListener("DOMContentLoaded", function() { 2540 let arrow_el = document.getElementById("sidebar-mobile-toggle"); 2541 let body_el = document.querySelector("body"); 2542 if(!arrow_el || !body_el) return null; 2543 2544 arrow_el.addEventListener("click", function() { 2545 body_el.classList.toggle("sidebar-mobile-close"); 2546 }); 2547 }); 2548 2549 2550 2551
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.