PageSourceSearch

https://circuitoftheamericas.com/wp-content/themes/cota/assets/js/debug.addIndicators.js

js circuitoftheamericas.com collected 2026-09-24 09:14:32 UTC 23,298 bytes, 670 lines download raw bytes

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

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.