1/* Driversnote phone animation â locale: en-US 2 * Requires: mockup-locales.js must be loaded before this script. 3 */ 4(function () { 5 6 // Inject SVG sprite 7 var holder = document.createElement('div'); 8 holder.setAttribute('aria-hidden', 'true'); 9 holder.innerHTML = DN_SPRITE_HTML; 10 document.body.insertBefore(holder, document.body.firstChild); 11 12 var TOTAL_MS = 36000; 13 14 var phone = document.getElementById('anim-phone'); 15 var sceneLock = document.getElementById('scene-lockscreen'); 16 var sceneInbox = document.getElementById('scene-inbox'); 17 var sceneReport = document.getElementById('scene-report'); 18 var notification = document.getElementById('anim-notification'); 19 var inboxCount = document.getElementById('anim-inbox-count'); 20 var reviewAll = document.getElementById('anim-review-all'); 21 var sendName = document.getElementById('anim-send-name'); 22 var sendEmail = document.getElementById('anim-send-email'); 23 var noteTyped = document.getElementById('anim-note-typed'); 24 var inboxCat = document.getElementById('anim-inbox-cat'); 25 var progressFill = document.getElementById('anim-progress-fill'); 26 var ctrlIcon = document.getElementById('ctrl-icon'); 27 var ctrlLabel = document.getElementById('ctrl-label'); 28 29 function typewriter(el, text, msPerChar) { 30 el.classList.add('is-visible'); 31 el.textContent = ''; 32 var i = 0; 33 function next() { 34 if (i < text.length) { 35 el.textContent += text[i++]; 36 timers.push(setTimeout(next, msPerChar)); 37 } 38 } 39 next(); 40 } 41 42 // Resolves demo copy the timeline types out. TIMELINE is built before 43 // applyLocale() picks a locale, so this must be read when the callback fires â 44 // reading it here at construction time would pin every market to the first locale. 45 // Absent means undefined/null only â an empty string is a real value, matching 46 // the [data-l] walk below. Some locales legitimately ship "" (strings.medical 47 // and strings.charity are empty outside the US variant). 48 var activeLocaleData = null; 49 function sampleText(key, fallback) { 50 var sample = activeLocaleData && activeLocaleData.sample; 51 var val = sample ? sample[key] : undefined; 52 return (val === undefined || val === null) ? fallback : val; 53 } 54 55 var TIMELINE = [ 56 [0, function() { sceneLock.classList.add('is-visible'); }], 57 [800, function() { notification.classList.add('is-visible'); }], 58 [2700, function() { notification.classList.add('is-tapped'); }], 59 [3140, function() { notification.classList.remove('is-tapped'); }], 60 [4100, function() { sceneInbox.classList.add('is-visible'); }], 61 [4900, function() { sceneLock.classList.remove('is-visible'); }], 62 [8740, function() { 63 sceneInbox.classList.add('anim-note-active'); 64 typewriter(noteTyped, sampleText('note_text', 'Sales meeting'), 80); 65 }], 66 [14300, function() { sceneInbox.classList.add('anim-review-btn-pressed'); }], 67 [14660, function() { 68 sceneInbox.classList.add('anim-trip-reviewed'); 69 inboxCount.textContent = '0'; 70 reviewAll.setAttribute('data-disabled', 'true'); 71 }], 72 [18260, function() { sceneInbox.classList.remove('is-visible'); }], 73 [19060, function() { sceneReport.classList.add('is-visible'); }], 74 [23860, function() { sceneReport.classList.add('anim-modal-visible'); }], 75 [27660, function() { typewriter(sendName, sampleText('recipient_name', 'Jane (Accountant)'), 65); }], 76 [30460, function() { typewriter(sendEmail, sampleText('recipient_email', '[email protected]'), 60); }], 77 [34260, function() { sceneReport.classList.add('anim-send-pressed'); }], 78 [35060, function() { sceneReport.classList.remove('is-visible'); }], 79 ]; 80 81 var timers = []; 82 var rafId = null; 83 var isPlaying = true; 84 var startTime = 0; 85 var pausedAt = 0; 86 87 function resetAll() { 88 sceneLock.classList.remove('is-visible'); 89 sceneInbox.classList.remove('is-visible', 'anim-note-active', 'anim-trip-reviewed', 'anim-review-btn-pressed'); 90 sceneReport.classList.remove('is-visible', 'anim-modal-visible', 'anim-send-pressed');
91 notification.style.transition = 'none'; 92 notification.classList.remove('is-visible', 'is-tapped'); 93 notification.offsetHeight; // force reflow 94 notification.style.transition = ''; 95 sendName.classList.remove('is-visible'); 96 sendEmail.classList.remove('is-visible'); 97 noteTyped.textContent = ''; 98 sendName.textContent = ''; 99 sendEmail.textContent = ''; 100 inboxCount.textContent = '1'; 101 reviewAll.removeAttribute('data-disabled'); 102 } 103 104 function clearTimers() { 105 timers.forEach(clearTimeout); 106 timers = []; 107 cancelAnimationFrame(rafId); 108 } 109 110 function scheduleFrom(offsetMs) { 111 TIMELINE.forEach(function(entry) { 112 var delay = entry[0] - offsetMs; 113 if (delay >= 0) timers.push(setTimeout(entry[1], delay)); 114 }); 115 timers.push(setTimeout(function() { 116 resetAll(); 117 scheduleFrom(0); 118 startTime = Date.now(); 119 }, TOTAL_MS - offsetMs)); 120 } 121 122 function tickProgress() { 123 if (progressFill) { 124 var elapsed = (Date.now() - startTime) % TOTAL_MS; 125 progressFill.style.width = (elapsed / TOTAL_MS * 100).toFixed(2) + '%'; 126 } 127 rafId = requestAnimationFrame(tickProgress); 128 } 129 130 function setPlayIcon(showPlay) { 131 if (!ctrlIcon || !ctrlLabel) return; 132 if (showPlay) { 133 ctrlIcon.innerHTML = '<polygon points="2,1.5 10.5,6 2,10.5"/>'; 134 ctrlLabel.textContent = 'Play'; 135 } else { 136 ctrlIcon.innerHTML = '<rect x="1.5" y="1.5" width="3.5" height="9" rx="1"/><rect x="7" y="1.5" width="3.5" height="9" rx="1"/>'; 137 ctrlLabel.textContent = 'Pause'; 138 } 139 } 140 141 function play(offsetMs) { 142 offsetMs = offsetMs || 0; 143 startTime = Date.now() - offsetMs; 144 scheduleFrom(offsetMs); 145 tickProgress(); 146 isPlaying = true; 147 setPlayIcon(false); 148 } 149 150 function pause() { 151 pausedAt = (Date.now() - startTime) % TOTAL_MS; 152 clearTimers(); 153 isPlaying = false; 154 setPlayIcon(true); 155 } 156 157 var playpauseBtn = document.getElementById('ctrl-playpause'); 158 if (playpauseBtn) { 159 playpauseBtn.addEventListener('click', function() { 160 if (isPlaying) pause(); 161 else play(pausedAt); 162 }); 163 } 164 var restartBtn = document.getElementById('ctrl-restart'); 165 if (restartBtn) { 166 restartBtn.addEventListener('click', function() { 167 clearTimers(); 168 resetAll(); 169 pausedAt = 0; 170 play(0); 171 }); 172 } 173 174 var inboxMapImage = document.getElementById('anim-inbox-map-image'); 175 176 function applyLocale(localeKey) { 177 var entry = DN_LOCALES[localeKey]; 178 if (!entry || !entry.data) return; 179 var data = entry.data; 180 activeLocaleData = data; 181 document.querySelectorAll('[data-l]').forEach(function(el) { 182 var parts = el.getAttribute('data-l').split('.'); 183 var section = parts[0]; 184 var key = parts[1]; 185 var val = data[section] && data[section][key]; 186 if (val !== undefined && val !== null) el.textContent = val; 187 }); 188 if (inboxMapImage && data.sample && data.sample.map_image_1) { 189 // Trip image paths are stored relative to dist/. In the exported zip, 190 // animation.html sits at the bundle root next to assets/, so we strip 191 // the leading "../" and resolve to "assets/maps/{locale}/..." directly. 192 inboxMapImage.setAttribute('src', data.sample.map_image_1.replace(/^\.\.\//, '')); 193 } 194 var variant = (data.meta && data.meta.category_variant) || 'standard'; 195 inboxCat.className = 'dn-mockup__category-selector dn-mockup__category-selector--' + variant; 196 // Most European markets write the currency after the amount (10,03 â¬). 197 // Absent meta, keep the historical prefix layout. 198 // Toggled on the currency row only â the distance row shares the same class 199 // and must keep its number-then-unit order. 200 var currencySuffixed = !!(data.meta && data.meta.currencyPosition === 'suffix'); 201 document.querySelectorAll('.dn-mockup__map-meta-currency').forEach(function(el) { 202 if (el.parentNode && el.parentNode.classList) { 203 el.parentNode.classList.toggle('dn-mockup__map-meta-value--currency-suffix', currencySuffixed); 204 } 205 }); 206 clearTimers(); 207 resetAll(); 208 if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 209 sceneInbox.classList.add('is-visible'); 210 } else { 211 play(0); 212 } 213 } 214 215 applyLocale(Object.keys(DN_LOCALES)[0]); 216 217})();
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.