PageSourceSearch

https://www.cwresources.org/wp-content/themes/wpcode-v2/js/ws-fader.js?v=1.03

js cwresources.org collected 2026-10-04 12:43:32 UTC 6,637 bytes, 258 lines download raw bytes

1/*
2*  A simple vanilla JS fader with infinite looping and play/pause button
3*
4*  @param string    id        the id of the fader element
5*  @param    int    interval  the time in milliseconds before fading next slide
6*/
7
8class WS_Fader {
9
10  button      = null;
11  clicked     = false;
12  clones_made = false;
13  counter     = 0;
14  cur_dot     = null;
15  cur_slide   = null;
16  dots        = null;
17  prev        = null;
18  slide_count = 0;
19  slides      = null;
20  timer       = null;
21
22
23  constructor( args ) {
24    this.id           = args['id'];
25    this.interval     = args['interval'];
26    this.show_dots    = args['dots'];
27    this.show_button  = args['button'];
28    this.normalize    = args['normalize'];
29
30    this.initialize();
31  }
32
33
34  /*
35  PERFORM SOME STARTING CHECKS
36  */
37  initialize = () => {
38
39    this.count_slides();
40
41    // If there are no slides, bail
42    if ( 0 == this.slide_count ) {
43      return;
44    }
45
46    // If there's only one slide, set its opacity to 1, hide the dots and pause button, then bail
47    if ( 1 == this.slide_count ) {
48      this.slides[0].style.opacity = 1;
49      this.slides[0].style.position = 'static';
50      return;
51    }
52
53    // If there are only 2 slides, clone them, then continue
54    if ( 2 == this.slide_count ) {
55      let clone_one = this.slides[0].cloneNode( true );
56      let clone_two = this.slides[1].cloneNode( true );
57      clone_one.classList.remove('on');
58
59      this.slides[0].after( clone_one );
60      this.slides[0].after( clone_two );
61
62      this.clones_made = true;
63
64      this.count_slides();
65    }
66
67    // Create dots if show_dots == true, there's more than 1 slide, and clones weren't made
68    if ( this.show_dots && this.slide_count > 1 && !this.clones_made) {
69      this.create_dots();
70    }
71
72    // Create pause button if show_button == true and there's more than 1 slide
73    if ( this.show_button && this.slide_count > 1 ) {
74      this.create_button();
75    }
76
77    // Start the fader
78    this.start();
79  } // initialize()
80
81
82  /*
83  COUNT THE NUMBER OF SLIDES
84  */
85  count_slides = () => {
86    this.slides = document.querySelectorAll( this.id + '>*:not(.dots):not(button)' );
87    this.slide_count = this.slides.length;
88  }
89
90
91  /*
92  SET INITIAL 'on' AND 'prev' SLIDE CLASSES, 'active' INDICATOR DOT, THEN SET FADER TIMER
93  */
94  start = () => {
95
96    // Set first slide
97    this.cur_slide = this.slides[0];
98    this.cur_slide.classList.add( 'on' );
99
100    // Set previous slide
101    this.prev = this.slides[this.slide_count-1];
102    this.prev.classList.add( 'prev' );
103
104    // Set first dot
105    if ( this.show_dots && !this.clones_made ) {
106      this.cur_dot = this.dots[0];
107      this.cur_dot.classList.add( 'active' );
108    }
109
110    // Normalize slide heights
111    if ( this.normalize ) {
112      this.normalize_heights();
113    }
114
115    // Start fader
116    this.fade();
117    this.timer = setInterval( this.fade, this.interval );
118  } // start()
119
120
121  /*
122  TOGGLE FADE CLASSES EVERY [INTERVAL] MILLISECONDS
123  */
124  fade = () => {
125
126    // Advance slide
127    this.cur_slide.classList.remove( 'on' );
128    this.cur_slide = this.slides[this.counter];
129    this.cur_slide.classList.add( 'on' );
130
131    this.prev.classList.remove('prev');
132    this.prev = this.slides[this.counter-1] ? this.slides[this.counter-1] : this.slides[this.slide_count-1];
133    this.prev.classList.add( 'prev' );
134
135    // Advance dot
136    if ( this.show_dots && !this.clones_made ) {
137      this.cur_dot.classList.remove( 'active' );
138      this.cur_dot = this.dots[this.counter];
139      this.cur_dot.classList.add( 'active' );
140    }
141
142    // If slide was advanced in response to a dot click, reset the cleared timer
143    if ( this.clicked ) {
144      this.clicked = false;
145      this.timer = setInterval( this.fade, this.interval );
146    }
147
148    // Increment counter for next slide
149    this.counter = (this.counter + 1 == this.slide_count) ? 0 : this.counter + 1;
150
151  } // fade()
152
153
154
155  /*
156  NORMALIZE SLIDE HEIGHTS
157  */
158  normalize_heights = () => {
159    let heights = [], tallest;
160    this.slides.forEach( element => {
161      heights.push(element.offsetHeight);
162    });
163    tallest = Math.max.apply( null, heights );
164    this.slides.forEach( element => {
165      element.style.height = tallest + 'px';
166    });
167  }
168
169
170
171  /*
172  CREATE SLIDE INDICATOR DOTS
173  */
174  create_dots = () => {
175
176    let indicator_list = document.createElement('ul');
177    indicator_list.classList.add( 'dots' );
178
179    for( let i=0; i<this.slide_count; i++ ) {
180      let new_dot = document.createElement('li');
181      let new_button = document.createElement('button');
182      new_dot.appendChild(new_button);
183      indicator_list.appendChild(new_dot);
184    }
185
186    // Add UL element to fader element
187    document.querySelector( this.id ).appendChild( indicator_list );
188
189    // Bind indicator dots event listener
190    document.querySelectorAll( this.id + ' ul.dots>li>button' ).forEach(button => {
191      button.addEventListener( 'click', this.dot_click );
192    });
193
194    this.dots = document.querySelectorAll( this.id + '>ul>li' );
195
196  }
197
198
199  /*
200  HANDLE INDICATOR DOT CLICK
201  */
202  dot_click = (e) => {
203    e.target.blur();
204
205    // Get index of indicator dot that was clicked
206    let all_dots    = document.querySelectorAll( this.id + ' ul.dots>li' );
207    let clicked_dot = e.target.parentNode;
208    let click_index = Array.prototype.indexOf.call( all_dots, clicked_dot );
209
210    // Set counter to clicked dot index
211    this.counter = click_index;
212
213    // Set click flag to true, clear timer, force slide change
214    this.clicked = true;
215    clearInterval( this.timer );
216
217    // Unpause play/pause button if it was paused
218    if( this.show_button) {
219      this.button.classList.remove( 'paused' );
220    }
221
222    // Trigger fade
223    this.fade();
224  }
225
226
227  /*
228  CREATE PAUSE BUTTON AND BIND CLICK EVENT LISTENER TO IT
229  */
230  create_button = () => {
231    let pause_button = document.createElement( 'button' );
232    pause_button.innerHTML = '<span class="sr-only">Pause image rotator</span>';
233    pause_button.setAttribute( 'aria-live', 'polite' );
234    
235    document.querySelector( this.id ).appendChild( pause_button );
236
237    this.button     = document.querySelector( this.id + '>button' );
238    this.button?.addEventListener( 'click', this.pause );
239  }
240
241
242  /*
243  HANDLE PAUSE BUTTON PRESS
244  */
245  pause = () => {
246    this.button.classList.toggle( 'paused' );
247
248    if( this.button.classList.contains( 'paused' ) ) {
249      clearInterval( this.timer );
250      this.button.querySelector('span.sr-only').textContent = 'Play image rotator';
251      return;
252    }
253
254    this.timer = setInterval( this.fade, this.interval );
255    this.button.querySelector('span.sr-only').textContent = 'Pause image rotator';
256  } // pause()
257
258}

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.