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