1/*! 2 * ScrollMagic v2.0.8 (2020-08-14) 3 * The javascript library for magical scroll interactions. 4 * (c) 2020 Jan Paepke (@janpaepke) 5 * Project Website: http://scrollmagic.io 6 * 7 * @version 2.0.8 8 * @license Dual licensed under MIT license and GPL. 9 * @author Jan Paepke - [email protected] 10 * 11 * @file Debug Extension for ScrollMagic. 12 */ 13/** 14 * This plugin was formerly known as the ScrollMagic debug extension. 15 * 16 * It enables you to add visual indicators to your page, to be able to see exactly when a scene is triggered. 17 * 18 * To have access to this extension, please include `plugins/debug.addIndicators.js`. 19 * @mixin debug.addIndicators 20 */ 21(function (root, factory) { 22 if (typeof define === 'function' && define.amd) { 23 // AMD. Register as an anonymous module. 24 define(['ScrollMagic'], factory); 25 } else if (typeof exports === 'object') { 26 // CommonJS 27 factory(require('scrollmagic')); 28 } else { 29 // no browser global export needed, just execute 30 factory(root.ScrollMagic || (root.jQuery && root.jQuery.ScrollMagic)); 31 } 32}(this, function (ScrollMagic) { 33 "use strict"; 34 var NAMESPACE = "debug.addIndicators"; 35 36 var 37 console = window.console || {}, 38 err = Function.prototype.bind.call(console.error || console.log || function () {}, console); 39 if (!ScrollMagic) { 40 err("(" + NAMESPACE + ") -> ERROR: The ScrollMagic main module could not be found. Please make sure it's loaded before this plugin or use an asynchronous loader like requirejs."); 41 } 42 43 // plugin settings 44 var 45 FONT_SIZE = "0.85em", 46 ZINDEX = "9999", 47 EDGE_OFFSET = 15; // minimum edge distance, added to indentation 48 49 // overall vars 50 var 51 _util = ScrollMagic._util, 52 _autoindex = 0; 53 54 55 56 ScrollMagic.Scene.extend(function () { 57 var 58 Scene = this, 59 _indicator; 60 61 var log = function () { 62 if (Scene._log) { // not available, when main source minified 63 Array.prototype.splice.call(arguments, 1, 0, "(" + NAMESPACE + ")", "->"); 64 Scene._log.apply(this, arguments); 65 } 66 }; 67 68 /** 69 * Add visual indicators for a ScrollMagic.Scene. 70 * @memberof! debug.addIndicators# 71 * 72 * @example 73 * // add basic indicators 74 * scene.addIndicators() 75 * 76 * // passing options 77 * scene.addIndicators({name: "pin scene", colorEnd: "#FFFFFF"}); 78 * 79 * @param {object} [options] - An object containing one or more options for the indicators. 80 * @param {(string|object)} [options.parent] - A selector, DOM Object or a jQuery object that the indicators should be added to. 81 If undefined, the controller's container will be used. 82 * @param {number} [options.name=""] - This string will be displayed at the start and end indicators of the scene for identification purposes. If no name is supplied an automatic index will be used. 83 * @param {number} [options.indent=0] - Additional position offset for the indicators (useful, when having multiple scenes starting at the same position). 84 * @param {string} [options.colorStart=green] - CSS color definition for the start indicator. 85 * @param {string} [options.colorEnd=red] - CSS color definition for the end indicator. 86 * @param {string} [options.colorTrigger=blue] - CSS color definition for the trigger indicator. 87 */ 88 Scene.addIndicators = function (options) { 89 if (!_indicator) { 90 var 91 DEFAULT_OPTIONS = { 92 name: "", 93 indent: 0, 94 parent: undefined, 95 colorStart: "green", 96 colorEnd: "red", 97 colorTrigger: "blue", 98 }; 99 100 options = _util.extend({}, DEFAULT_OPTIONS, options); 101 102 _autoindex++; 103 _indicator = new Indicator(Scene, options); 104 105 Scene.on("add.plugin_addIndicators", _indicator.add); 106 Scene.on("remove.plugin_addIndicators", _indicator.remove); 107 Scene.on("destroy.plugin_addIndicators", Scene.removeIndicators); 108 109 // it the scene already has a controller we can start right away. 110 if (Scene.controller()) { 111 _indicator.add(); 112 } 113 } 114 return Scene; 115 }; 116 117 /** 118 * Removes visual indicators from a ScrollMagic.Scene. 119 * @memberof! debug.addIndicators# 120 * 121 * @example 122 * // remove previously added indicators 123 * scene.removeIndicators() 124 * 125 */ 126 Scene.removeIndicators = function () { 127 if (_indicator) { 128 _indicator.remove(); 129 this.off("*.plugin_addIndicators"); 130 _indicator = undefined; 131 } 132 return Scene; 133 }; 134 135 }); 136 137 138 /* 139 * ---------------------------------------------------------------- 140 * Extension for controller to store and update related indicators 141 * ---------------------------------------------------------------- 142 */ 143 // add option to globally auto-add indicators to scenes 144 /** 145 * Every ScrollMagic.Controller instance now accepts an additional option. 146 * See {@link ScrollMagic.Controller} for a complete list of the standard options. 147 * @memberof! debug.addIndicators# 148 * @method new ScrollMagic.Controller(options) 149 * @example 150 * // make a controller and add indicators to all scenes attached 151 * var controller = new ScrollMagic.Controller({addIndicators: true}); 152 * // this scene will automatically have indicators added to it 153 * new ScrollMagic.Scene() 154 * .addTo(controller); 155 * 156 * @param {object} [options] - Options for the Controller. 157 * @param {boolean} [options.addIndicators=false] - If set to `true` every scene that is added to the controller will automatically get indicators added to it. 158 */ 159 ScrollMagic.Controller.addOption("addIndicators", false); 160 // extend Controller 161 ScrollMagic.Controller.extend(function () { 162 var 163 Controller = this, 164 _info = Controller.info(), 165 _container = _info.container, 166 _isDocument = _info.isDocument, 167 _vertical = _info.vertical, 168 _indicators = { // container for all indicators and methods 169 groups: [] 170 }; 171 172 var log = function () { 173 if (Controller._log) { // not available, when main source minified 174 Array.prototype.splice.call(arguments, 1, 0, "(" + NAMESPACE + ")", "->"); 175 Controller._log.apply(this, arguments); 176 } 177 }; 178 if (Controller._indicators) {
179 log(2, "WARNING: Scene already has a property '_indicators', which will be overwritten by plugin."); 180 } 181 182 // add indicators container 183 this._indicators = _indicators; 184 /* 185 needed updates: 186 +++++++++++++++ 187 start/end position on scene shift (handled in Indicator class) 188 trigger parameters on triggerHook value change (handled in Indicator class) 189 bounds position on container scroll or resize (to keep alignment to bottom/right) 190 trigger position on container resize, window resize (if container isn't document) and window scroll (if container isn't document) 191 */ 192 193 // event handler for when associated bounds markers need to be repositioned 194 var handleBoundsPositionChange = function () { 195 _indicators.updateBoundsPositions(); 196 }; 197 198 // event handler for when associated trigger groups need to be repositioned 199 var handleTriggerPositionChange = function () { 200 _indicators.updateTriggerGroupPositions(); 201 }; 202 203 _container.addEventListener("resize", handleTriggerPositionChange); 204 if (!_isDocument) { 205 window.addEventListener("resize", handleTriggerPositionChange); 206 window.addEventListener("scroll", handleTriggerPositionChange); 207 } 208 // update all related bounds containers 209 _container.addEventListener("resize", handleBoundsPositionChange); 210 _container.addEventListener("scroll", handleBoundsPositionChange); 211 212 213 // updates the position of the bounds container to aligned to the right for vertical containers and to the bottom for horizontal 214 this._indicators.updateBoundsPositions = function (specificIndicator) { 215 var // constant for all bounds 216 groups = specificIndicator ? [_util.extend({}, specificIndicator.triggerGroup, { 217 members: [specificIndicator] 218 })] : // create a group with only one element 219 _indicators.groups, // use all 220 g = groups.length, 221 css = {}, 222 paramPos = _vertical ? "left" : "top", 223 paramDimension = _vertical ? "width" : "height", 224 edge = _vertical ? 225 _util.get.scrollLeft(_container) + _util.get.width(_container) - EDGE_OFFSET : 226 _util.get.scrollTop(_container) + _util.get.height(_container) - EDGE_OFFSET, 227 b, triggerSize, group; 228 while (g--) { // group loop 229 group = groups[g]; 230 b = group.members.length; 231 triggerSize = _util.get[paramDimension](group.element.firstChild); 232 while (b--) { // indicators loop 233 css[paramPos] = edge - triggerSize; 234 _util.css(group.members[b].bounds, css); 235 } 236 } 237 }; 238 239 // updates the positions of all trigger groups attached to a controller or a specific one, if provided 240 this._indicators.updateTriggerGroupPositions = function (specificGroup) { 241 var // constant vars 242 groups = specificGroup ? [specificGroup] : _indicators.groups, 243 i = groups.length, 244 container = _isDocument ? document.body : _container, 245 containerOffset = _isDocument ? { 246 top: 0, 247 left: 0 248 } : _util.get.offset(container, true), 249 edge = _vertical ? 250 _util.get.width(_container) - EDGE_OFFSET : 251 _util.get.height(_container) - EDGE_OFFSET, 252 paramDimension = _vertical ? "width" : "height", 253 paramTransform = _vertical ? "Y" : "X"; 254 var // changing vars 255 group, 256 elem, 257 pos, 258 elemSize, 259 transform; 260 while (i--) { 261 group = groups[i]; 262 elem = group.element; 263 pos = group.triggerHook * Controller.info("size"); 264 elemSize = _util.get[paramDimension](elem.firstChild.firstChild); 265 transform = pos > elemSize ? "translate" + paramTransform + "(-100%)" : ""; 266 267 _util.css(elem, { 268 top: containerOffset.top + (_vertical ? pos : edge - group.members[0].options.indent), 269 left: containerOffset.left + (_vertical ? edge - group.members[0].options.indent : pos) 270 }); 271 _util.css(elem.firstChild.firstChild, { 272 "-ms-transform": transform, 273 "-webkit-transform": transform, 274 "transform": transform 275 }); 276 } 277 }; 278 279 // updates the label for the group to contain the name, if it only has one member 280 this._indicators.updateTriggerGroupLabel = function (group) { 281 var 282 text = "trigger" + (group.members.length > 1 ? "" : " " + group.members[0].options.name), 283 elem = group.element.firstChild.firstChild, 284 doUpdate = elem.textContent !== text; 285 if (doUpdate) { 286 elem.textContent = text; 287 if (_vertical) { // bounds position is dependent on text length, so update 288 _indicators.updateBoundsPositions(); 289 } 290 } 291 }; 292 293 // add indicators if global option is set 294 this.addScene = function (newScene) { 295 296 if (this._options.addIndicators && newScene instanceof ScrollMagic.Scene && newScene.controller() === Controller) { 297 newScene.addIndicators(); 298 } 299 // call original destroy method 300 this.$super.addScene.apply(this, arguments); 301 }; 302 303 // remove all previously set listeners on destroy 304 this.destroy = function () { 305 _container.removeEventListener("resize", handleTriggerPositionChange); 306 if (!_isDocument) { 307 window.removeEventListener("resize", handleTriggerPositionChange); 308 window.removeEventListener("scroll", handleTriggerPositionChange); 309 } 310 _container.removeEventListener("resize", handleBoundsPositionChange); 311 _container.removeEventListener("scroll", handleBoundsPositionChange); 312 // call original destroy method 313 this.$super.destroy.apply(this, arguments); 314 }; 315 return Controller; 316 317 }); 318 319 /* 320 * ----------------------------------------------------------------
321 * Internal class for the construction of Indicators 322 * ---------------------------------------------------------------- 323 */ 324 var Indicator = function (Scene, options) { 325 var 326 Indicator = this, 327 _elemBounds = TPL.bounds(), 328 _elemStart = TPL.start(options.colorStart), 329 _elemEnd = TPL.end(options.colorEnd), 330 _boundsContainer = options.parent && _util.get.elements(options.parent)[0], 331 _vertical, 332 _ctrl; 333 334 var log = function () { 335 if (Scene._log) { // not available, when main source minified 336 Array.prototype.splice.call(arguments, 1, 0, "(" + NAMESPACE + ")", "->"); 337 Scene._log.apply(this, arguments); 338 } 339 }; 340 341 options.name = options.name || _autoindex; 342 343 // prepare bounds elements 344 _elemStart.firstChild.textContent += " " + options.name; 345 _elemEnd.textContent += " " + options.name; 346 _elemBounds.appendChild(_elemStart); 347 _elemBounds.appendChild(_elemEnd); 348 349 // set public variables 350 Indicator.options = options; 351 Indicator.bounds = _elemBounds; 352 // will be set later 353 Indicator.triggerGroup = undefined; 354 355 // add indicators to DOM 356 this.add = function () { 357 _ctrl = Scene.controller(); 358 _vertical = _ctrl.info("vertical"); 359 360 var isDocument = _ctrl.info("isDocument"); 361 362 if (!_boundsContainer) { 363 // no parent supplied or doesnt exist 364 _boundsContainer = isDocument ? document.body : _ctrl.info("container"); // check if window/document (then use body) 365 } 366 if (!isDocument && _util.css(_boundsContainer, "position") === 'static') {
367 // position mode needed for correct positioning of indicators 368 _util.css(_boundsContainer, { 369 position: "relative" 370 }); 371 } 372 373 // add listeners for updates 374 Scene.on("change.plugin_addIndicators", handleTriggerParamsChange); 375 Scene.on("shift.plugin_addIndicators", handleBoundsParamsChange); 376 377 // updates trigger & bounds (will add elements if needed) 378 updateTriggerGroup(); 379 updateBounds(); 380 381 setTimeout(function () { // do after all execution is finished otherwise sometimes size calculations are off 382 _ctrl._indicators.updateBoundsPositions(Indicator); 383 }, 0); 384 385 log(3, "added indicators"); 386 }; 387 388 // remove indicators from DOM 389 this.remove = function () { 390 if (Indicator.triggerGroup) { // if not set there's nothing to remove 391 Scene.off("change.plugin_addIndicators", handleTriggerParamsChange); 392 Scene.off("shift.plugin_addIndicators", handleBoundsParamsChange); 393 394 if (Indicator.triggerGroup.members.length > 1) { 395 // just remove from memberlist of old group 396 var group = Indicator.triggerGroup; 397 group.members.splice(group.members.indexOf(Indicator), 1); 398 _ctrl._indicators.updateTriggerGroupLabel(group); 399 _ctrl._indicators.updateTriggerGroupPositions(group); 400 Indicator.triggerGroup = undefined; 401 } else { 402 // remove complete group 403 removeTriggerGroup(); 404 } 405 removeBounds(); 406 407 log(3, "removed indicators"); 408 } 409 }; 410 411 /* 412 * ---------------------------------------------------------------- 413 * internal Event Handlers 414 * ---------------------------------------------------------------- 415 */ 416 417 // event handler for when bounds params change 418 var handleBoundsParamsChange = function () { 419 updateBounds(); 420 }; 421 422 // event handler for when trigger params change 423 var handleTriggerParamsChange = function (e) { 424 if (e.what === "triggerHook") { 425 updateTriggerGroup(); 426 } 427 }; 428 429 /* 430 * ---------------------------------------------------------------- 431 * Bounds (start / stop) management 432 * ---------------------------------------------------------------- 433 */ 434 435 // adds an new bounds elements to the array and to the DOM 436 var addBounds = function () { 437 var v = _ctrl.info("vertical"); 438 // apply stuff we didn't know before... 439 _util.css(_elemStart.firstChild, { 440 "border-bottom-width": v ? 1 : 0, 441 "border-right-width": v ? 0 : 1, 442 "bottom": v ? -1 : options.indent, 443 "right": v ? options.indent : -1, 444 "padding": v ? "0 8px" : "2px 4px", 445 }); 446 _util.css(_elemEnd, { 447 "border-top-width": v ? 1 : 0, 448 "border-left-width": v ? 0 : 1, 449 "top": v ? "100%" : "", 450 "right": v ? options.indent : "", 451 "bottom": v ? "" : options.indent, 452 "left": v ? "" : "100%", 453 "padding": v ? "0 8px" : "2px 4px" 454 }); 455 // append 456 _boundsContainer.appendChild(_elemBounds); 457 }; 458 459 // remove bounds from list and DOM 460 var removeBounds = function () { 461 _elemBounds.parentNode.removeChild(_elemBounds); 462 }; 463 464 // update the start and end positions of the scene 465 var updateBounds = function () { 466 if (_elemBounds.parentNode !== _boundsContainer) { 467 addBounds(); // Add Bounds elements (start/end) 468 } 469 var css = {}; 470 css[_vertical ? "top" : "left"] = Scene.triggerPosition(); 471 css[_vertical ? "height" : "width"] = Scene.duration(); 472 _util.css(_elemBounds, css); 473 _util.css(_elemEnd, { 474 display: Scene.duration() > 0 ? "" : "none" 475 }); 476 }; 477 478 /* 479 * ---------------------------------------------------------------- 480 * trigger and trigger group management 481 * ---------------------------------------------------------------- 482 */ 483 484 // adds an new trigger group to the array and to the DOM 485 var addTriggerGroup = function () { 486 var triggerElem = TPL.trigger(options.colorTrigger); // new trigger element 487 var css = {}; 488 css[_vertical ? "right" : "bottom"] = 0; 489 css[_vertical ? "border-top-width" : "border-left-width"] = 1; 490 _util.css(triggerElem.firstChild, css); 491 _util.css(triggerElem.firstChild.firstChild, { 492 padding: _vertical ? "0 8px 3px 8px" : "3px 4px" 493 }); 494 document.body.appendChild(triggerElem); // directly add to body 495 var newGroup = { 496 triggerHook: Scene.triggerHook(), 497 element: triggerElem, 498 members: [Indicator] 499 }; 500 _ctrl._indicators.groups.push(newGroup); 501 Indicator.triggerGroup = newGroup; 502 // update right away 503 _ctrl._indicators.updateTriggerGroupLabel(newGroup); 504 _ctrl._indicators.updateTriggerGroupPositions(newGroup); 505 }; 506 507 var removeTriggerGroup = function () { 508 _ctrl._indicators.groups.splice(_ctrl._indicators.groups.indexOf(Indicator.triggerGroup), 1); 509 Indicator.triggerGroup.element.parentNode.removeChild(Indicator.triggerGroup.element); 510 Indicator.triggerGroup = undefined; 511 }; 512 513 // updates the trigger group -> either join existing or add new one 514 /* 515 * Logic: 516 * 1 if a trigger group exist, check if it's in sync with Scene settings â if so, nothing else needs to happen 517 * 2 try to find an existing one that matches Scene parameters 518 * 2.1 If a match is found check if already assigned to an existing group 519 * If so: 520 * A: it was the last member of existing group -> kill whole group 521 * B: the existing group has other members -> just remove from member list 522 * 2.2 Assign to matching group 523 * 3 if no new match could be found, check if assigned to existing group 524 * A: yes, and it's the only member -> just update parameters and positions and keep using this group 525 * B: yes but there are other members -> remove from member list and create a new one 526 * C: no, so create a new one 527 */ 528 var updateTriggerGroup = function () { 529 var 530 triggerHook = Scene.triggerHook(), 531 closeEnough = 0.0001; 532 533 // Have a group, check if it still matches 534 if (Indicator.triggerGroup) { 535 if (Math.abs(Indicator.triggerGroup.triggerHook - triggerHook) < closeEnough) { 536 // _util.log(0, "trigger", options.name, "->", "no need to change, still in sync"); 537 return; // all good 538 } 539 } 540 // Don't have a group, check if a matching one exists 541 // _util.log(0, "trigger", options.name, "->", "out of sync!"); 542 var 543 groups = _ctrl._indicators.groups, 544 group, 545 i = groups.length; 546 while (i--) { 547 group = groups[i]; 548 if (Math.abs(group.triggerHook - triggerHook) < closeEnough) { 549 // found a match! 550 // _util.log(0, "trigger", options.name, "->", "found match"); 551 if (Indicator.triggerGroup) { // do I have an old group that is out of sync? 552 if (Indicator.triggerGroup.members.length === 1) { // is it the only remaining group? 553 // _util.log(0, "trigger", options.name, "->", "kill"); 554 // was the last member, remove the whole group 555 removeTriggerGroup(); 556 } else { 557 Indicator.triggerGroup.members.splice(Indicator.triggerGroup.members.indexOf(Indicator), 1); // just remove from memberlist of old group 558 _ctrl._indicators.updateTriggerGroupLabel(Indicator.triggerGroup); 559 _ctrl._indicators.updateTriggerGroupPositions(Indicator.triggerGroup); 560 // _util.log(0, "trigger", options.name, "->", "removing from previous member list"); 561 } 562 } 563 // join new group 564 group.members.push(Indicator); 565 Indicator.triggerGroup = group; 566 _ctrl._indicators.updateTriggerGroupLabel(group); 567 return; 568 } 569 } 570 571 // at this point I am obviously out of sync and don't match any other group 572 if (Indicator.triggerGroup) { 573 if (Indicator.triggerGroup.members.length === 1) { 574 // _util.log(0, "trigger", options.name, "->", "updating existing"); 575 // out of sync but i'm the only member => just change and update 576 Indicator.triggerGroup.triggerHook = triggerHook; 577 _ctrl._indicators.updateTriggerGroupPositions(Indicator.triggerGroup); 578 return; 579 } else { 580 // _util.log(0, "trigger", options.name, "->", "removing from previous member list"); 581 Indicator.triggerGroup.members.splice(Indicator.triggerGroup.members.indexOf(Indicator), 1); // just remove from memberlist of old group 582 _ctrl._indicators.updateTriggerGroupLabel(Indicator.triggerGroup); 583 _ctrl._indicators.updateTriggerGroupPositions(Indicator.triggerGroup); 584 Indicator.triggerGroup = undefined; // need a brand new group...
585 } 586 } 587 // _util.log(0, "trigger", options.name, "->", "add a new one"); 588 // did not find any match, make new trigger group 589 addTriggerGroup(); 590 }; 591 }; 592 593 /* 594 * ---------------------------------------------------------------- 595 * Templates for the indicators 596 * ---------------------------------------------------------------- 597 */ 598 var TPL = { 599 start: function (color) { 600 // inner element (for bottom offset -1, while keeping top position 0) 601 var inner = document.createElement("div"); 602 inner.textContent = "start"; 603 _util.css(inner, { 604 position: "absolute", 605 overflow: "visible", 606 "border-width": 0, 607 "border-style": "solid", 608 color: color, 609 "border-color": color 610 }); 611 var e = document.createElement('div'); 612 // wrapper 613 _util.css(e, { 614 position: "absolute", 615 overflow: "visible", 616 width: 0, 617 height: 0 618 }); 619 e.appendChild(inner); 620 return e; 621 }, 622 end: function (color) { 623 var e = document.createElement('div'); 624 e.textContent = "end"; 625 _util.css(e, { 626 position: "absolute", 627 overflow: "visible", 628 "border-width": 0, 629 "border-style": "solid", 630 color: color, 631 "border-color": color 632 }); 633 return e; 634 }, 635 bounds: function () { 636 var e = document.createElement('div'); 637 _util.css(e, { 638 position: "absolute", 639 overflow: "visible", 640 "white-space": "nowrap", 641 "pointer-events": "none", 642 "font-size": FONT_SIZE 643 }); 644 e.style.zIndex = ZINDEX; 645 return e; 646 }, 647 trigger: function (color) { 648 // inner to be above or below line but keep position 649 var inner = document.createElement('div'); 650 inner.textContent = "trigger"; 651 _util.css(inner, { 652 position: "relative", 653 }); 654 // inner wrapper for right: 0 and main element has no size 655 var w = document.createElement('div'); 656 _util.css(w, { 657 position: "absolute", 658 overflow: "visible", 659 "border-width": 0, 660 "border-style": "solid", 661 color: color, 662 "border-color": color 663 }); 664 w.appendChild(inner); 665 // wrapper 666 var e = document.createElement('div'); 667 _util.css(e, { 668 position: "fixed", 669 overflow: "visible", 670 "white-space": "nowrap", 671 "pointer-events": "none", 672 "font-size": FONT_SIZE 673 }); 674 e.style.zIndex = ZINDEX; 675 e.appendChild(w); 676 return e; 677 }, 678 }; 679 680}));
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.