1// --- Slide controller registry --- 2const controllers = new WeakMap(); // slideEl -> { play, stop } 3 4function registerSlideController(slideEl, controller) { 5 console.log("registerSlideController:", { slideEl, controller }); 6 controllers.set(slideEl, controller); 7} 8 9// --- Respond to slide changes (page 3 only) --- 10document.addEventListener("slide:change", (e) => { 11 const { prev, next } = e.detail || {}; 12 console.log("slide change:", { prev, next }); 13 console.log(controllers, controllers.get(next)) 14 if (prev) controllers.get(prev)?.stop?.(); 15 if (next) controllers.get(next)?.play?.(); 16}); 17 18document.addEventListener("DOMContentLoaded", () => { 19 20 /** 21 * ------ locks nearby detected - geofence radius fixed ------- 22 */ 23 24 // in app help for overlapping geofences where geofence radius editing is not possible 25 // mogn_: map-overlapping-geofences-no 26 const mogn_geofences = gsap.utils.toArray("#mogn_geofence_1, #mogn_geofence_2"); 27 const mogn_intersection = document.getElementById("mogn_intersection"); 28 29 if (mogn_geofences.length && mogn_intersection) { 30 31 // Configure defaults once so you can focus on creative timing 32 gsap.defaults({ 33 ease: "power1.inOut", 34 duration: 1, 35 }); 36 37 gsap.set(mogn_geofences, { 38 opacity: 0, 39 scale: 1, 40 transformOrigin: "70px 70px", 41 }); 42 43 44 gsap.set(mogn_intersection, { opacity: 0 }); 45 46 // TimelineMax is still provided for older GSAP syntax, but gsap.timeline is the modern API 47 const tl = gsap.timeline({ paused: true, repeat: -1, repeatDelay: 1 }); 48 49 tl.to(mogn_geofences, { opacity: 0.6, duration: 1 }) // 1. fade in geofences 50 .to(mogn_intersection, { opacity: 0.5, duration: 0.4 }, "+=1") // 2. show intersection 51 .to( 52 mogn_intersection, 53 { 54 opacity: 0, 55 repeat: 3, 56 yoyo: true, 57 duration: 0.4, 58 }, 59 ) // 3. blink the intersection twice 60 .to( 61 mogn_intersection, 62 { opacity: 0, duration: 0.4 }, 63 ) // 4. fade out intersection 64 .to( 65 mogn_geofences, 66 { scale: 0.7, duration: 1 }, 67 "-=0.1", 68 ) // 4. scale geofences down from center 69 .to(mogn_geofences, { opacity: 0, duration: 1 }, "+=1") // 5. hide geofences 70 .set(mogn_geofences, { scale: 1 }); // reset for loop 71 72 tl.play(); 73 74 // replayBtn?.addEventListener("click", () => { 75 // tl.restart(); 76 // }); 77 78 } 79 80 81 /** 82 * ------ locks nearby detected - geofence radius limited ------- 83 */ 84 85 86 // in app help for overlapping geofences where geofence radius editing is possible but limited 87 // mogl_: map-overlapping-geofences-limited 88 const mogl_geofences = gsap.utils.toArray("#mogl_geofence_1, #mogl_geofence_2"); 89 const mogle_intersection = document.getElementById("mogl_intersection"); 90 91 if (mogl_geofences.length && mogle_intersection) { 92 93 // Configure defaults once so you can focus on creative timing 94 gsap.defaults({ 95 ease: "power1.inOut", 96 duration: 1, 97 }); 98 99 gsap.set(mogl_geofences, { 100 opacity: 0, 101 scale: 1, 102 transformOrigin: "70px 70px", 103 }); 104 105 106 gsap.set("#mogl_circle_highlighted", { x: 86 }); 107 gsap.set("#mogl_slider", { opacity: 0 }); 108 gsap.set("#mogl_line", { scaleX: 1.5, transformOrigin: "left center" }); 109 110 111 gsap.set(mogle_intersection, { opacity: 0 }); 112 113 // TimelineMax is still provided for older GSAP syntax, but gsap.timeline is the modern API 114 const tl = gsap.timeline({ paused: true, repeat: -1, repeatDelay: 1 }); 115 116 tl.to(mogl_geofences, { opacity: 0.6, duration: 1 }) // 1. fade in geofences 117 .to( 118 "#mogl_slider", 119 { opacity: 1, duration: 1 }, 120 "<", 121 ) // show the slider 122 123 .to(mogle_intersection, { opacity: 0.5, duration: 0.4 }, "+=1") // 2. show intersection 124 .to( 125 mogle_intersection, 126 { 127 opacity: 0, 128 repeat: 3, 129 yoyo: true, 130 duration: 0.4, 131 }, 132 ) // 3. blink the intersection twice 133 .to( 134 mogle_intersection, 135 { opacity: 0, duration: 0.4 }, 136 ) // 4. fade out intersection 137 .to( 138 mogl_geofences, 139 { scale: 0.7, duration: 1 }, 140 "-=0.1", 141 ) // 4. scale geofences down from center 142 .to( 143 "#mogl_circle_highlighted", 144 { x: 0, duration: 1 }, 145 "<", 146 ) // 5. move dot in slider to the left 147 .to( 148 "#mogl_line", 149 { scaleX: 1, duration: 1 }, 150 "<", 151 ) // 6. simultanious scale the line back to original 152 .to( 153 "#mogl_line_dashed", 154 { 155 stroke: "#FF0000", 156 repeat: 4, 157 yoyo: true, 158 duration: 0.4, 159 }, 160 ) // blink the dashed line 161 .to( 162 "#mogl_circle_outlined > path", 163 { 164 fill: "#FF0000", 165 repeat: 4, 166 yoyo: true, 167 duration: 0.4, 168 }, 169 "<" 170 ) // simultanious change color of outlined circle 171 172 .to(mogl_geofences, { opacity: 0, duration: 1 }, "+=1") // 5. hide geofences 173 .to( 174 "#mogl_slider", 175 { opacity: 0, duration: 1 }, 176 "<", 177 ) // fade out the slider 178 .set(mogl_geofences, { scale: 1 }); // reset for loop 179 180 // Include extra GSAP plugins before app.js if your SVG animation needs them 181 tl.play(); 182 } 183 184 185 186 187 188 189 /** 190 * ------ merged geofencee section ------- 191 */ 192 193 194 /** 195 * ------ section 1: map merged geofence merging (mmgm) ------- 196 */ 197 198 const mmgmGeofence1 = document.getElementById("mmgm-geofence_1"); 199 const mmgmGeofence2 = document.getElementById("mmgm-geofence_2"); 200 const mmgmMergedGeofence = document.getElementById("mmgm-merged_geofence"); 201 const mmgmIntersection = document.getElementById("mmgm-intersection"); 202 if ( 203 mmgmGeofence1 && 204 mmgmGeofence2 && 205 mmgmMergedGeofence && 206 mmgmIntersection && 207 window.MorphSVGPlugin 208 ) { 209 210 /** 211 * if the animation is part of a slide, we need to find the slide element to register play/stop controllers. 212 * initially the animation is paused, so it won't play until the slide is active. 213 * otherwise the animation might be part of a different page not containing a slide 214 * in this case the animation will play as soon as the DOM is loaded 215 */ 216 217 const slideEl1 = mmgmGeofence1.closest(".sw-inapp-help-page"); // goes upwards in the DOM tree to find the parent with class .sw-inapp-help-page 218 219 let mmgmTlPaused = true; // start paused until we confirm the slide exists and register controllers 220 221 if (!slideEl1) { // slide not found - start playing the animation immediately for non-sli
221de content 222 mmgmTlPaused = false; 223 } 224 225 gsap.set([mmgmGeofence1, mmgmGeofence2, mmgmMergedGeofence, mmgmIntersection], { autoAlpha: 0 }); 226 227 const mmgmTl = gsap.timeline({ paused: mmgmTlPaused, repeat: -1, defaults: { ease: "power1.inOut" } }); 228 229 mmgmTl.to([mmgmGeofence1, mmgmGeofence2], { autoAlpha: 1, duration: 1}) // 1. fade in geofences 230 .to( 231 mmgmIntersection, 232 { 233 autoAlpha: 1, 234 repeat: 3, 235 yoyo: true, 236 duration: 0.4, 237 }, 238 ) // 3. blink the intersection twice 239 .to( 240 [mmgmGeofence1, mmgmGeofence2, mmgmIntersection], 241 { autoAlpha: 0, duration: 0.4 }, 242 ) // 4. fade out intersection + geofences 243 .to( 244 mmgmMergedGeofence, 245 { autoAlpha: 1, duration: 1 }, 246 "-=1" 247 ) // 5. show merged geofence 248 .to( 249 mmgmMergedGeofence, 250 { autoAlpha: 0, duration: 1 }, 251 "+=1" 252 ); 253 254 if (slideEl1) { // slide found - register controllers to play/pause the animation based on slide visibility 255 registerSlideController(slideEl1, { 256 play: () => mmgmTl.restart(true), 257 stop: () => mmgmTl.pause(0), 258 }); 259 } 260 261 } 262 263 264 /** 265 * ------ section 2: map merged geofence notifications (mmgn) ------- 266 */ 267 const mmgnManikinCircle = document.getElementById("mmgn-manikin_circle"); 268 const mmgnBellFlag = document.getElementById("mmgn-bell_icon_flag"); 269 const mmgnBellIcon = document.getElementById("mmgn-bell_icon"); 270 const mmgnManikinLegsStart = document.getElementById("mmgn-manikin-legs-start"); 271 const mmgnManikinLegsEnd = document.getElementById("mmgn-manikin-legs-end"); 272 if ( 273 mmgnManikinCircle && 274 mmgnBellFlag && 275 mmgnBellIcon && 276 mmgnManikinLegsStart && 277 mmgnManikinLegsEnd && 278 window.MorphSVGPlugin 279 ) { 280 281 const slideEl2 = mmgnManikinCircle.closest(".sw-inapp-help-page"); 282 // if (!slideEl2) return; 283 284 let mmgnTlPaused = true; // start paused until we confirm the slide exists and register controllers 285 286 if (!slideEl2) { // slide not found - start playing the animation immediately for non-slide content 287 mmgnTlPaused = false; 288 } 289 290 291 gsap.set(mmgnManikinCircle, { x: 80, autoAlpha: 0 }); 292 gsap.set(mmgnBellFlag, { autoAlpha: 0 }); 293 gsap.set(mmgnBellIcon, { 294 transformOrigin: "50% 15%", 295 }); 296 gsap.set(mmgnManikinLegsEnd, { autoAlpha: 0 }); 297 const walkLoop = gsap.timeline({ 298 paused: true, 299 repeat: -1, 300 yoyo: true, 301 defaults: { duration: 0.5, ease: "power1.inOut" }, 302 }); 303 walkLoop.to(mmgnManikinLegsStart, { morphSVG: mmgnManikinLegsEnd }); 304 305 const mmgnTl = gsap.timeline({ paused: mmgnTlPaused, repeat: -1, defaults: { ease: "power1.inOut" } }); 306 307 mmgnTl.to(mmgnManikinCircle, { autoAlpha: 1, duration: 1}) // 1. fade in 308 .to( 309 mmgnManikinCircle, 310 { 311 x: 0, 312 duration: 2, 313 onStart: () => walkLoop.play(), 314 onComplete: () => walkLoop.pause(0), 315 ease: "power1.inOut", 316 }, 317 "-=0.5" 318 ) // 2. move while walking 319 .to(mmgnBellFlag, { autoAlpha: 1, duration: 0.5 }) // 3. show flag 320 .to( 321 mmgnBellIcon, 322 { rotation: 10, duration: 0.12 } 323 ) 324 .to( 325 mmgnBellIcon, 326 { rotation: -10, repeat: 2, yoyo: true, duration: 0.15 } 327 ) 328 .to( 329 mmgnBellIcon, 330 { rotation: 0, duration: 0.12 } 331 ) 332 // 4. ring twice 333 .to([mmgnManikinCircle, mmgnBellFlag], { autoAlpha: 0, duration: 1 }, "+=1") // 5. fade out 334 .set(mmgnManikinCircle, { x: 80 }); 335 336 if (slideEl2) { // slide found - register controllers to play/pause the animation based on slide visibility 337 registerSlideController(slideEl2, { 338 play: () => mmgnTl.restart(true), 339 stop: () => mmgnTl.pause(0), 340 }); 341 } 342 343 } 344 345 346 /** 347 * ------ section 3: map merged geofence keyless entry (mmgke) ------- 348 */ 349 const mmgkeManikinCircle = document.getElementById("mmgke-manikin_circle"); 350 const mmgkeCircleTick = document.getElementById("mmgke-circle_tick"); 351 const mmgkeCircleX = document.getElementById("mmgke-circle_x"); 352 const mmgkeManikinLegsStart = document.getElementById("mmgke-manikin-legs-start"); 353 const mmgkeManikinLegsEnd = document.getElementById("mmgke-manikin-legs-end"); 354 const mmgkeDoor1 = document.getElementById("mmgke-poi_1"); 355 const mmgkeDoor2 = document.getElementById("mmgke-poi_2"); 356 if ( 357 mmgkeManikinCircle && 358 mmgkeCircleTick && 359 mmgkeCircleX && 360 mmgkeManikinLegsStart && 361 mmgkeManikinLegsEnd && 362 window.MorphSVGPlugin 363 ) { 364 365 const slideEl3 = mmgkeDoor2.closest(".sw-inapp-help-page"); 366 // if (!slideEl3) return; 367 let mmgkeTlPaused = true; // start paused until we confirm the slide exists and register controllers 368 369 if (!slideEl3) { // slide not found - start playing the animation immediately for non-sli
369de content 370 mmgkeTlPaused = false; 371 } 372 373 374 gsap.set(mmgkeManikinCircle, { x: 80, autoAlpha: 0 }); 375 gsap.set([mmgkeCircleTick, mmgkeCircleX], { autoAlpha: 0 }); 376 gsap.set(mmgkeManikinLegsEnd, { autoAlpha: 0 }); 377 378 const walkLoop = gsap.timeline({ 379 paused: true, 380 repeat: -1, 381 yoyo: true, 382 defaults: { duration: 0.5, ease: "power1.inOut" }, 383 }); 384 walkLoop.to(mmgkeManikinLegsStart, { morphSVG: mmgkeManikinLegsEnd }); 385 386 const mmgkeTl = gsap.timeline({ paused: mmgkeTlPaused, repeat: -1, defaults: { ease: "power1.inOut" } }); 387 388 mmgkeTl.to(mmgkeManikinCircle, { autoAlpha: 1, duration: 1}) // 1. fade in 389 .to( 390 mmgkeManikinCircle, 391 { 392 x: 0, 393 duration: 2, 394 onStart: () => walkLoop.play(), 395 onComplete: () => walkLoop.pause(0), 396 ease: "power1.inOut", 397 }, 398 "-=0.5" 399 ) // 2. move while walking 400 .to( 401 mmgkeDoor2, 402 { scale: 1.1, repeat: 3, yoyo: true, duration: 0.3, transformOrigin: "50% 100%" } 403 ) 404 405 .to(mmgkeCircleTick, { autoAlpha: 1, duration: 0.5 }) // 3. show flag 406 .to( 407 mmgkeDoor1, 408 { rotate: 10, repeat: 3, yoyo: true, duration: 0.3, transformOrigin: "50% 100%" } 409 ) 410 411 .to(mmgkeCircleX, { autoAlpha: 1, duration: 0.5 }) // 3. show flag 412 .to([mmgkeManikinCircle, mmgkeCircleTick, mmgkeCircleX], { autoAlpha: 0, duration: 1 }, "+=1") // 5. fade out 413 .set(mmgkeManikinCircle, { x: 80 }); 414 415 if(slideEl3){ 416 registerSlideController(slideEl3, { 417 play: () => mmgkeTl.restart(true), 418 stop: () => mmgkeTl.pause(0), 419 }); 420 } 421 } 422 423 424});
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.