1/* ============================================================ 2 ÐбÑий CTA-модÑÐ»Ñ Ð²Ð¸ÑалÑнÑÑ ÐºÐ°Ð»ÑкÑлÑÑоÑов CoinKeeper 3 ÐодклÑÑаеÑÑÑ ÐÐСÐРопÑÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ window.CALC_CONFIG и налиÑÐ¸Ñ <div id="cta-root">. 4 5 ÐдинÑй «пÑоÑÑой» паÑÑеÑн (как regret-calculator): 6 [ ÐоделиÑÑÑÑ ÑезÑлÑÑаÑом ] â ÑендеÑÐ¸Ñ ÐºÐ°ÑÑÐ¸Ð½ÐºÑ 9:16, показÑÐ²Ð°ÐµÑ ÐÐ ÐÐЬЮ, 7 заÑем ÑлÑÑ ÑеÑез Web Share / ÑÐ¾Ñ Ñанение 8 ÑÑÑока-пÑиÑина (моÑÑ Â«ÑиÑÑа â CoinKeeper») 9 [ УÑÑановиÑÑ CoinKeeper ] 10 11 CALC_CONFIG = { 12 id: 'earnings-counter', 13 getInstallReason: () => 'HTML-ÑÑÑока', 14 getShareCard: () => ({ eyebrow, currency, big, unit, sub, hook }), 15 getShareText: () => 'ÑекÑÑ-подпиÑÑ Ðº каÑÑинке', 16 related: [ { id, href, icon, title, hook } ] 17 } 18 19 ÐÑблиÑное API: window.CTA.refresh() â пеÑеÑиÑоваÑÑ ÑекÑÑ-пÑиÑинÑ. 20 ============================================================ */ 21(function () { 22 'use strict'; 23 24 var cfg = window.CALC_CONFIG; 25 var root = document.getElementById('cta-root'); 26 if (!cfg || !root) { return; } 27 28 function detectPlatform() { 29 var ua = navigator.userAgent || ''; 30 if (/Android/i.test(ua)) { return 'android'; } 31 if (/iPhone|iPod/i.test(ua)) { return 'ios'; } 32 // iPadOS 13+ маÑкиÑÑеÑÑÑ Ð¿Ð¾Ð´ Mac â оÑлиÑаем по touch-ÑоÑкам 33 if (/iPad/i.test(ua) || (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1)) { return 'ios'; } 34 return 'desktop'; 35 } 36 function installUrl(platform) { 37 var utm = '?utm_source=viral&utm_medium=calculator&utm_campaign=' + cfg.id; 38 if (platform === 'ios') { return 'https://apps.apple.com/app/id1335547405' + utm; } 39 if (platform === 'android') { return 'https://play.google.com/store/apps/details?id=com.disrapp.coinkeeper3' + utm; } 40 return 'https://coinkeeper.me/3/' + utm; 41 } 42 var INSTALL_PLATFORM = detectPlatform(); 43 var INSTALL = installUrl(INSTALL_PLATFORM); 44 45 /* --- ÐÑедзагÑÑзка оÑиÑиалÑного логоÑипа Ð´Ð»Ñ ÐºÐ°ÑÑоÑки (same-origin â canvas не tainted) --- */ 46 var logoImg = new Image(); 47 var logoReady = new Promise(function (resolve) { 48 logoImg.onload = resolve; logoImg.onerror = resolve; 49 }); 50 logoImg.src = 'logoname.png'; 51 52 /* --- GA4 / dataLayer (акÑивно: analytics.js гÑÑÐ·Ð¸Ñ G-W3NJLXBFND на вÑÐµÑ ÑÑÑаниÑÐ°Ñ ) --- */ 53 function track(event, params) { 54 params = params || {}; 55 params.calc_id = cfg.id; 56 try { 57 if (typeof window.gtag === 'function') { window.gtag('event', event, params); } 58 else { (window.dataLayer = window.dataLayer || []).push(Object.assign({ event: event }, params)); } 59 } catch (e) {} 60 if (window.console && console.debug) { console.debug('[cta]', event, params); } 61 } 62 63 /* ============================================================ 64 Ð ÐµÐ½Ð´ÐµÑ ÐºÐ°ÑÑоÑки ÑезÑлÑÑаÑа на canvas (Stories 9:16) 65 ============================================================ */ 66 var COLORS = { ink: '#1E2857', ink2: '#273572', yellow: '#FFB433', orange: '#FF791F', white: '#FFFFFF' }; 67 68 function roundRect(ctx, x, y, w, h, r) { 69 ctx.beginPath(); 70 ctx.moveTo(x + r, y); 71 ctx.arcTo(x + w, y, x + w, y + h, r); 72 ctx.arcTo(x + w, y + h, x, y + h, r); 73 ctx.arcTo(x, y + h, x, y, r); 74 ctx.arcTo(x, y, x + w, y, r); 75 ctx.closePath(); 76 } 77 78 function drawCard(W, H, card) { 79 var c = document.createElement('canvas'); 80 c.width = W; c.height = H; 81 var ctx = c.getContext('2d'); 82 83 // Ñон: ÑÑмнÑй гÑÐ°Ð´Ð¸ÐµÐ½Ñ + ÑÑплÑй блик 84 var g = ctx.createLinearGradient(0, 0, W, H); 85 g.addColorStop(0, COLORS.ink); g.addColorStop(1, COLORS.ink2); 86 ctx.fillStyle = g; ctx.fillRect(0, 0, W, H); 87 var rg = ctx.createRadialGradient(W, 0, 0, W, 0, W * 0.9); 88 rg.addColorStop(0, 'rgba(255,121,31,0.30)'); rg.addColorStop(1, 'rgba(255,121,31,0)'); 89 ctx.fillStyle = rg; ctx.fillRect(0, 0, W, H); 90 91 var cx = W / 2; 92 var S = W / 1080; // маÑÑÑаб по ÑиÑине 93 ctx.textAlign = 'center'; 94 95 // бÑенд ÑвеÑÑ Ñ â оÑиÑиалÑнÑй логоÑип на белой плаÑке (на ÑÑмном Ñоне Ñам wordmark ÑÑмнÑй)
96 if (logoImg.complete && logoImg.naturalWidth) { 97 var lw = 360 * S; 98 var lh = lw * (logoImg.naturalHeight / logoImg.naturalWidth); 99 var padX = 32 * S, padY = 24 * S; 100 var chipW = lw + padX * 2, chipH = lh + padY * 2; 101 var chipX = cx - chipW / 2, chipY = 96 * S; 102 roundRect(ctx, chipX, chipY, chipW, chipH, 30 * S); 103 ctx.fillStyle = '#FFFFFF'; ctx.fill(); 104 ctx.drawImage(logoImg, chipX + padX, chipY + padY, lw, lh); 105 } else { 106 // ÑоллбÑк, еÑли каÑÑинка не загÑÑзилаÑÑ 107 ctx.fillStyle = 'rgba(255,255,255,0.92)'; 108 ctx.font = '800 ' + (44 * S) + 'px Nunito, sans-serif'; 109 ctx.fillText('CoinKeeper', cx, 170 * S); 110 } 111 112 // ÑенÑÑалÑнÑй ÑÑек (еÑли еÑÑÑ drawExtra â Ñдвигаем вÑÑе, ÑÑÐ¾Ð±Ñ Ð¾ÑвободиÑÑ Ð¼ÐµÑÑо Ð´Ð»Ñ Ð³ÑаÑика) 113 var hasExtra = typeof card.drawExtra === 'function'; 114 var cy = H / 2; 115 var yOff = hasExtra ? -180 * S : 0; 116 // eyebrow 117 ctx.fillStyle = 'rgba(255,255,255,0.62)'; 118 ctx.font = '800 ' + (34 * S) + 'px Nunito, sans-serif'; 119 wrapText(ctx, (card.eyebrow || '').toUpperCase(), cx, cy - 200 * S + yOff, W - 180 * S, 46 * S, 2); 120 121 // болÑÑое ÑиÑло (⽠жÑлÑÑм + знаÑение белÑм) 122 var bigFontSize = hasExtra ? 140 : 170; 123 var numFont = '900 ' + (bigFontSize * S) + 'px Nunito, sans-serif'; 124 ctx.font = numFont; 125 var cur = card.currency ? (card.currency + ' ') : ''; 126 var num = String(card.big || ''); 127 var curW = ctx.measureText(cur).width; 128 var numW = ctx.measureText(num).width; 129 // еÑли не Ð²Ð»ÐµÐ·Ð°ÐµÑ â ÑменÑÑаем 130 var total = curW + numW; 131 var maxW = W - 120 * S; 132 if (total > maxW) { 133 var k = maxW / total; 134 numFont = '900 ' + Math.floor(bigFontSize * S * k) + 'px Nunito, sans-serif'; 135 ctx.font = numFont; curW = ctx.measureText(cur).width; numW = ctx.measureText(num).width; total = curW + numW; 136 } 137 var startX = cx - total / 2; 138 ctx.textAlign = 'left'; 139 ctx.fillStyle = COLORS.yellow; ctx.fillText(cur, startX, cy + 30 * S + yOff); 140 ctx.fillStyle = COLORS.white; ctx.fillText(num, startX + curW, cy + 30 * S + yOff); 141 ctx.textAlign = 'center'; 142 143 // unit 144 ctx.fillStyle = 'rgba(255,255,255,0.78)'; 145 ctx.font = '600 ' + (44 * S) + 'px Nunito, sans-serif'; 146 ctx.fillText(card.unit || '', cx, cy + 130 * S + yOff); 147 148 // sub-ÑÑÑока 149 if (card.sub) { 150 ctx.fillStyle = 'rgba(255,255,255,0.55)'; 151 ctx.font = '700 ' + (38 * S) + 'px Nunito, sans-serif'; 152 ctx.fillText(card.sub, cx, cy + 200 * S + yOff); 153 } 154 155 // опÑионалÑнÑй каÑÑомнÑй блок (напÑимеÑ, мини-гÑаÑик) 156 if (hasExtra) { 157 try { card.drawExtra(ctx, W, H, S); } catch (e) {} 158 } 159 160 // низ: hook + url 161 ctx.fillStyle = COLORS.yellow; 162 ctx.font = '800 ' + (40 * S) + 'px Nunito, sans-serif'; 163 ctx.fillText((card.hook || 'ÐоÑÑиÑай Ñвой ÑезÑлÑÑаÑ') + ' â', cx, H - 200 * S); 164 ctx.fillStyle = 'rgba(255,255,255,0.5)'; 165 ctx.font = '700 ' + (34 * S) + 'px Nunito, sans-serif'; 166 ctx.fillText('calc.coinkeeper.me', cx, H - 130 * S); 167 168 return c; 169 } 170 171 function wrapText(ctx, text, x, y, maxWidth, lineHeight, maxLines) { 172 var words = String(text).split(' '), line = '', lines = []; 173 for (var i = 0; i < words.length; i++) { 174 var test = line ? line + ' ' + words[i] : words[i]; 175 if (ctx.measureText(test).width > maxWidth && line) { lines.push(line); line = words[i]; } 176 else { line = test; } 177 } 178 if (line) { lines.push(line); } 179 if (maxLines) { lines = lines.slice(0, maxLines); } 180 var startY = y - (lines.length - 1) * lineHeight; 181 for (var j = 0; j < lines.length; j++) { ctx.fillText(lines[j], x, startY + j * lineHeight); } 182 } 183 184 function canvasToBlob(canvas) { 185 return new Promise(function (resolve) { 186 if (canvas.toBlob) { canvas.toBlob(resolve, 'image/png'); } 187 else { resolve(dataURItoBlob(canvas.toDataURL('image/png'))); } 188 }); 189 } 190 function dataURItoBlob(uri) { 191 var b = atob(uri.split(',')[1]), arr = new Uint8Array(b.length); 192 for (var i = 0; i < b.length; i++) { arr[i] = b.charCodeAt(i); } 193 return new Blob([arr], { type: 'image/png' }); 194 } 195 196 // оÑÑендеÑиÑÑ story-каÑÑÐ¸Ð½ÐºÑ 9:16 â { blob, url, file } 197 async function buildCard() { 198 try { if (document.fonts && document.fonts.ready) { await document.fonts.ready; } } catch (e) {} 199 await logoReady; 200 var card = cfg.getShareCard ? cfg.getShareCard() : {}; 201 var canvas = drawCard(1080, 1920, card); 202 var blob = await canvasToBlob(canvas); 203 var file = new File([blob], cfg.id + '-story.png', { type: 'image/png' }); 204 return { blob: blob, url: URL.createObjectURL(blob), file: file }; 205 } 206 207 function downloadBlob(blob, name) { 208 var url = URL.createObjectURL(blob); 209 var a = document.createElement('a'); 210 a.href = url;
210 a.download = name; document.body.appendChild(a); a.click(); 211 document.body.removeChild(a); setTimeout(function () { URL.revokeObjectURL(url); }, 1000); 212 } 213 214 /* --- Toast --- */ 215 var toastEl; 216 function toast(msg) { 217 if (!toastEl) { toastEl = document.createElement('div'); toastEl.className = 'cta-toast'; document.body.appendChild(toastEl); } 218 toastEl.textContent = msg; toastEl.classList.add('show'); 219 clearTimeout(toast._t); toast._t = setTimeout(function () { toastEl.classList.remove('show'); }, 2400); 220 } 221 222 /* ============================================================ 223 ÐÑевÑÑ-ÑиÑ: показÑваем ÑгенеÑиÑованнÑÑ ÐºÐ°ÑÑинкÑ, заÑем ÑаÑим/ÑÐ¾Ñ ÑанÑем 224 ============================================================ */ 225 var overlay, current; // current = { blob, url, file } 226 227 function buildSheet() { 228 overlay = document.createElement('div'); 229 overlay.className = 'cta-preview-overlay'; 230 overlay.innerHTML = 231 '<div class="cta-preview" role="dialog" aria-modal="true" aria-label="ÐоделиÑÑÑÑ ÑезÑлÑÑаÑом">' + 232 '<div class="cta-preview-grip"></div>' + 233 '<div class="cta-preview-title">ÐÐ¾Ñ ÑÑо Ð²Ñ Ð¾ÑпÑавиÑе</div>' + 234 '<div class="cta-preview-stage">' + 235 '<div class="cta-preview-loader" id="cta-prev-loader"><span class="cta-spinner"></span>ÐоÑовим каÑÑинкÑâ¦</div>' + 236 '<img class="cta-preview-img" id="cta-prev-img" alt="ÐÑевÑÑ ÑезÑлÑÑаÑа" hidden />' + 237 '</div>' + 238 '<div class="cta-preview-actions" id="cta-prev-actions" hidden>' + 239 '<button class="btn btn-primary" data-act="share" id="cta-prev-share">ÐоделиÑÑÑÑ</button>' + 240 '<button class="btn btn-secondary" data-act="save">Ð¡Ð¾Ñ ÑаниÑÑ ÐºÐ°ÑÑинкÑ</button>' + 241 '</div>' + 242 '<button class="cta-preview-cancel" data-act="cancel">ÐÑмена</button>' + 243 '</div>'; 244 document.body.appendChild(overlay); 245 246 overlay.addEventListener('click', function (e) { 247 if (e.target === overlay) { closeSheet(); return; } 248 var btn = e.target.closest('[data-act]'); 249 if (!btn) { return; } 250 var act = btn.dataset.act; 251 if (act === 'cancel') { closeSheet(); return; } 252 if (act === 'save' && current) { 253 downloadBlob(current.blob, cfg.id + '-story.png'); 254 track('share_image', { method: 'download' }); 255 toast('ÐаÑÑинка ÑÐ¾Ñ Ñанена'); closeSheet(); return; 256 } 257 if (act === 'share' && current) { 258 // навигаÑоÑ.share вÑзÑваеÑÑÑ Ð¿ÑÑмо из Ñапа (user gesture) â Ñайл Ñже гоÑов 259 if (navigator.canShare && navigator.canShare({ files: [current.file] })) { 260 navigator.share({ files: [current.file], text: cfg.getShareText ? cfg.getShareText() : '' }) 261 .then(function () { track('share_image', { method: 'native' }); closeSheet(); }) 262 .catch(function (err) { if (!err || err.name !== 'AbortError') { /* оÑÑаÑмÑÑ Ð² пÑевÑÑ */ } }); 263 } else { 264 downloadBlob(current.blob, cfg.id + '-story.png'); 265 track('share_image', { method: 'download' }); 266 toast('ÐаÑÑинка ÑÐ¾Ñ Ñанена â вÑложиÑе в ÑÑоÑиÑ'); closeSheet(); 267 } 268 } 269 }); 270 document.addEventListener('keydown', function (e) { if (e.key === 'Escape') { closeSheet(); } }); 271 } 272 273 function openPreview() { 274 track('share_open', {}); 275 if (!overlay) { buildSheet(); } 276 var img = overlay.querySelector('#cta-prev-img'); 277 var loader = overlay.querySelector('#cta-prev-loader'); 278 var actions = overlay.querySelector('#cta-prev-actions'); 279 var shareBtn = overlay.querySelector('#cta-prev-share'); 280 // ÑбÑÐ¾Ñ ÑоÑÑоÑÐ½Ð¸Ñ 281 img.hidden = true; loader.hidden = false; actions.hidden = true; 282 if (current && current.url) { URL.revokeObjectURL(current.url); current = null; } 283 284 requestAnimationFrame(function () { overlay.classList.add('open'); }); 285 286 buildCard().then(function (res) { 287 current = res; 288 img.src = res.url; img.hidden = false; loader.hidden = true; 289 // показаÑÑ Â«ÐоделиÑÑÑÑ» ÑолÑко еÑли поддеÑживаеÑÑÑ ÑеÑинг Ñайла 290 var canShareFile = !!(navigator.canShare && navigator.canShare({ files: [res.file] })); 291 shareBtn.hidden = !canShareFile; 292 actions.hidden = false;
293 }).catch(function () { 294 loader.textContent = 'Ðе ÑдалоÑÑ Ð¿Ð¾Ð´Ð³Ð¾ÑовиÑÑ ÐºÐ°ÑÑинкÑ'; 295 }); 296 } 297 298 function closeSheet() { 299 if (overlay) { overlay.classList.remove('open'); } 300 if (current && current.url) { URL.revokeObjectURL(current.url); current = null; } 301 } 302 303 /* ============================================================ 304 ÐÑомо подпиÑки Platinum Plus 305 ÐдинÑй апÑелл-блок на вÑÐµÑ ÐºÐ°Ð»ÑкÑлÑÑоÑÐ°Ñ . Ðожно оÑклÑÑиÑÑ 306 на конкÑеÑной ÑÑÑаниÑе ÑеÑез window.CALC_CONFIG.platinum = false. 307 ============================================================ */ 308 var PLATINUM_URL = 'https://coinkeeper.me/3?utm_source=viral&utm_medium=calc_platinum&utm_campaign=' + cfg.id + '#pricing'; 309 var PLATINUM_FEATURES = [ 310 { icon: 'ð', title: 'ÐолоÑовой ввод', hook: 'ÐÑодикÑÑйÑе ÑÑаÑÑ Ð¾Ð±ÑÑнÑми Ñловами â ÐÐ Ð·Ð°Ð¿Ð¾Ð»Ð½Ð¸Ñ ÑÑммÑ, каÑегоÑÐ¸Ñ Ð¸ ÑÑÑÑ' }, 311 { icon: 'ð·', title: 'Ðобавление по ÑоÑо', hook: 'Снимок Ñека или ÑкÑана банка пÑевÑаÑаеÑÑÑ Ð² гоÑовÑе опеÑаÑии' }, 312 { icon: 'ð¬', title: 'ÐÐ-аÑÑиÑÑÐµÐ½Ñ Ð¿Ð¾ денÑгам', hook: 'СпÑоÑиÑе, кÑда ÑÑ Ð¾Ð´ÑÑ Ð´ÐµÐ½Ñги и где ÑокÑаÑиÑÑ â оÑвеÑÐ¸Ñ Ð¿ÑоÑÑÑм ÑзÑком' }, 313 { icon: 'ð', title: 'ÐиджеÑÑ Ð½Ð° главнÑй ÑкÑан', hook: 'ÐимиÑÑ Ð¸ ÑаÑÑ Ð¾Ð´Ñ Ð¿Ð¾Ð´ конÑÑолем, не оÑкÑÑÐ²Ð°Ñ Ð¿Ñиложение' } 314 ]; 315 316 function platinumBlock() { 317 if (cfg.platinum === false) { return ''; } 318 var rows = PLATINUM_FEATURES.map(function (f) { 319 return '<div class="cta-plat-feat">' + 320 '<span class="cpf-icon">' + f.icon + '</span>' + 321 '<span class="cpf-body"><span class="cpf-title">' + f.title + '</span>' + 322 '<span class="cpf-hook">' + f.hook + '</span></span></div>'; 323 }).join(''); 324 return '<div class="cta-platinum">' + 325 '<div class="cta-plat-eyebrow">â Platinum Plus</div>' + 326 '<div class="cta-plat-title">CoinKeeper Ñмнее Ñ Platinum Plus</div>' + 327 '<div class="cta-plat-sub">СÑиÑаÑÑ Ð´ÐµÐ½Ñги вÑÑÑнÑÑ â пÑоÑлÑй век. ÐодпиÑка добавлÑÐµÑ ÐÐ, коÑоÑÑй вноÑÐ¸Ñ Ð¾Ð¿ÐµÑаÑии и подÑказÑваеÑ, где Ð²Ñ Ð¿ÐµÑеплаÑиваеÑе.</div>' + 328 '<div class="cta-plat-feats">' + rows + '</div>' + 329 '<a class="btn cta-plat-cta" id="cta-plat-btn" href="' + PLATINUM_URL + '" target="_blank" rel="noopener">ÐоÑмоÑÑеÑÑ ÑаÑиÑÑ â</a>' + 330 '</div>'; 331 } 332 333 /* ============================================================ 334 ÐÑомо кÑÑÑов CoinKeeper (edu.coinkeeper.me) 335 ÐоказÑваеÑÑÑ ÐÐÐ ÐРблоком Platinum Plus на калÑкÑлÑÑоÑÐ°Ñ , 336 ÑÑÑ Ñема ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ñ ÐºÑÑÑом: window.CALC_CONFIG.course = 337 'svoboda' | 'finplan' | 'pravilainvest'. Ðез Ð¿Ð¾Ð»Ñ â блока неÑ. 338 ============================================================ */ 339 var COURSES = { 340 svoboda: { 341 title: 'ÐÑÑÑ Â«Ð¡Ð²Ð¾Ð±Ð¾Ð´Ð° Ð¾Ñ Ðолгов»', 342 sub: 'ÐалÑкÑлÑÑÐ¾Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð» ÑиÑÑÑ â кÑÑÑ Ð¿Ð¾ÐºÐ°Ð·ÑваеÑ, ÑÑо Ñ Ð½Ð¸Ð¼Ð¸ делаÑÑ. 10 пÑакÑиÑеÑÐºÐ¸Ñ ÑÑоков: как вÑбÑаÑÑÑÑ Ð¸Ð· долгов, пеÑеÑÑаÑÑ Ð±ÑаÑÑ Ð½Ð¾Ð²Ñе и веÑнÑÑÑ ÐºÐ¾Ð½ÑÑÐ¾Ð»Ñ Ð½Ð°Ð´ денÑгами.', 343 features: [ 344 { icon: 'ð', title: 'ÐÑениÑÑ Ð´Ð¾Ð»Ð³Ð¾Ð²ÑÑ Ð½Ð°Ð³ÑÑзкÑ', hook: 'РазбеÑÑÑеÑÑ, ÑколÑко на Ñамом деле ÑÑедаÑÑ Ð¿Ð»Ð°Ñежи и ÑÑо делаÑÑ Ð² пеÑвÑÑ Ð¾ÑеÑедÑ' }, 345 { icon: 'ð', title: 'РеÑинанÑиÑование', hook: 'Ðак законно ÑнизиÑÑ ÑÑÐ°Ð²ÐºÑ Ð¸ пеÑеплаÑÑ Ð¿Ð¾ дейÑÑвÑÑÑим кÑедиÑам' }, 346 { icon: 'â¡', title: 'ÐоÑÑоÑное погаÑение', hook: 'СÑÑаÑегиÑ, коÑоÑÐ°Ñ Ð¿ÑÐ¸Ð±Ð»Ð¸Ð¶Ð°ÐµÑ Ð´Ð°ÑÑ ÑÐ²Ð¾Ð±Ð¾Ð´Ñ Ð¸ ÑÐºÐ¾Ð½Ð¾Ð¼Ð¸Ñ Ð½Ð° пÑоÑенÑÐ°Ñ ' }, 347 { icon: 'ð§ ', title: 'ÐÑÐ¸Ñ Ð¾Ð»Ð¾Ð³Ð¸Ñ Ð´Ð¾Ð»Ð³Ð¾Ð²', hook: 'ÐоÑÐµÐ¼Ñ Ð¼Ñ Ð±ÐµÑÑм в долг â и как пеÑеÑÑаÑÑ Ð·Ð°Ð»ÐµÐ·Ð°ÑÑ Ð² новÑе' } 348 ] 349 }, 350 finplan: { 351 title: 'ÐÑÑÑ Â«ÐиÑнÑй
351ÑинанÑовÑй план»', 352 sub: 'ÐалÑкÑлÑÑÐ¾Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð» ÑиÑÑÑ â кÑÑÑ Ð´Ð¾Ð²Ð¾Ð´Ð¸Ñ ÐµÑ Ð´Ð¾ плана. 12 ÑÑоков-маÑаÑон: как накопиÑÑ Ð½Ð° кÑÑпнÑÑ ÑÐµÐ»Ñ Ð¸ пеÑеÑÑаÑÑ Ð³Ð°Ð´Ð°ÑÑ, кÑда ÑаÑÑ Ð¾Ð´ÑÑÑÑ Ð´ÐµÐ½Ñги.', 353 features: [ 354 { icon: 'ð¯', title: 'ÐÑÑпнÑе Ñели', hook: 'ÐваÑÑиÑа, маÑина, обÑазование деÑÑм â как поÑÑиÑаÑÑ ÑÑок и ÑÑммÑ, а не меÑÑаÑÑ' }, 355 { icon: 'ð', title: 'ÐодÑÑка безопаÑноÑÑи', hook: 'СколÑко деÑжаÑÑ Ð¿Ñо Ð·Ð°Ð¿Ð°Ñ Ð¸Ð¼ÐµÐ½Ð½Ð¾ вам и где ÑÑи денÑги Ñ ÑаниÑÑ' }, 356 { icon: 'ð', title: 'ÐиÑнÑй ÑинанÑовÑй план', hook: 'РабоÑÐ°Ñ ÑеÑÑадÑ: Ð´Ð¾Ñ Ð¾Ð´Ñ, ÑаÑÑ Ð¾Ð´Ñ, Ñели и ÑÑоки â в одной ÑаблиÑе' }, 357 { icon: 'ð', title: 'ФинанÑовÑе пÑивÑÑки', hook: 'Ðак оÑкладÑваÑÑ ÑегÑлÑÑно, не ÑÑÑваÑÑÑ Ð½Ð° вÑоÑой меÑÑÑ' } 358 ] 359 }, 360 pravilainvest: { 361 title: 'ÐÑÑÑ Â«ÐÑавила ÐнвеÑÑиÑий»', 362 sub: 'ÐалÑкÑлÑÑÐ¾Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð», ÑколÑко ÑÑÐ¾Ð¸Ñ Ð±ÐµÐ·Ð´ÐµÐ¹ÑÑвие. ÐÑÑÑ Ð¸Ð· 15 ÑÑоков â как ÑобÑаÑÑ Ð¿Ð¾ÑÑÑÐµÐ»Ñ Ð¿Ð¾Ð´ ÑÐ²Ð¾Ñ ÑÐµÐ»Ñ Ð¸ не паниковаÑÑ Ð½Ð° пÑоÑÐ°Ð´ÐºÐ°Ñ .', 363 features: [ 364 { icon: 'ð§', title: 'ÐнвеÑÑиÑионнÑй план', hook: 'Ðод ваÑÑ ÑÐµÐ»Ñ Ð¸ ÑÑок, а не «кÑпиÑÑ Ñо, ÑÑо ÑовеÑÑÑÑ Ð² ÑаÑе»' }, 365 { icon: 'ð', title: 'ÐкÑии и акÑивÑ', hook: 'Ðакие клаÑÑÑ Ð°ÐºÑивов бÑваÑÑ Ð¸ как вÑбиÑаÑÑ Ð¼ÐµÐ¶Ð´Ñ Ð½Ð¸Ð¼Ð¸' }, 366 { icon: 'ð¦', title: 'ÐблигаÑии и вкладÑ', hook: 'Ðде ÑеалÑÐ½Ð°Ñ Ð´Ð¾Ñ Ð¾Ð´Ð½Ð¾ÑÑÑ Ð¿Ð¾Ñле налогов и инÑлÑÑии' }, 367 { icon: 'ð§±', title: 'СбоÑка поÑÑÑелÑ', hook: 'ÐÑопоÑÑии, ÑебаланÑиÑовка и поведение в ÑÑоÑм на ÑÑнке' } 368 ] 369 } 370 }; 371 372 function courseUrl(slug) { 373 return 'https://edu.coinkeeper.me/' + slug + 374 '?utm_source=viral&utm_medium=calc_course&utm_campaign=' + cfg.id; 375 } 376 377 function courseBlock() { 378 var c = COURSES[cfg.course]; 379 if (!c) { return ''; } 380 var rows = c.features.map(function (f) { 381 return '<div class="cta-plat-feat">' + 382 '<span class="cpf-icon">' + f.icon + '</span>' + 383 '<span class="cpf-body"><span class="cpf-title">' + f.title + '</span>' + 384 '<span class="cpf-hook">' + f.hook + '</span></span></div>'; 385 }).join(''); 386 return '<div class="cta-platinum cta-course">' + 387 '<div class="cta-plat-eyebrow">â ÐÑÑÑ CoinKeeper</div>' + 388 '<div class="cta-plat-title">' + c.title + '</div>' + 389 '<div class="cta-plat-sub">' + c.sub + '</div>' + 390 '<div class="cta-plat-feats">' + rows + '</div>' + 391 '<a class="btn cta-plat-cta" id="cta-course-btn" href="' + courseUrl(cfg.course) + '" target="_blank" rel="noopener">УзнаÑÑ Ð¾ кÑÑÑе â</a>' + 392 '</div>'; 393 } 394 395 // апÑÐµÐ»Ð»Ñ Ð¸Ð´ÑÑ ÑÑопкой: ÑнаÑала ÑемаÑиÑеÑкий кÑÑÑ, поÑом Platinum Plus 396 function upsellBlock() { 397 return courseBlock() + platinumBlock(); 398 } 399 400 /* ============================================================ 401 Ðини-ÑеÑка дÑÑÐ³Ð¸Ñ ÐºÐ°Ð»ÑкÑлÑÑоÑов 402 ============================================================ */ 403 function crossGrid() { 404 var rel = cfg.related || []; 405 if (!rel.length) { return ''; } 406 var cards = rel.map(function (r) { 407 var sep = r.href.indexOf('?') > -1 ? '&' : '?'; 408 var href = r.href + sep + 'utm_source=viral&utm_medium=cross&from=' + cfg.id; 409 return '<a class="cta-cross-card" data-to="' + (r.id || r.href) + '" href="' + href + '">' + 410 '<span class="cc-icon">' + (r.icon || 'ð§®') + '</span>' + 411 '<span class="cc-body"><span class="cc-title">' + r.title + '</span>' + 412 (r.hook ? '<span class="cc-hook">' + r.hook + '</span>' : '') + '</span>' + 413 '<span class="cc-arrow">â</span></a>'; 414 }).join(''); 415 return '<div class="cta-cross"><div class="cta-cross-title">ÐÑÑ ÐºÐ°Ð»ÑкÑлÑÑоÑÑ</div><div class="cta-cross-grid">' + cards + '</div></div>'; 416 } 417 418 /* ============================================================ 419 Ð ÐµÐ½Ð´ÐµÑ CTA-блока в #cta-root 420 ============================================================ */ 421 function render() { 422 var reason = cfg.getInstallReason ? cfg.getInstallReason() : ''; 423 root.innerHTML = 424 '<div class="cta-actions">' + 425 '<button class="btn btn-primary" id="cta-share-btn">ÐоделиÑÑÑÑ ÑезÑлÑÑаÑом</button>' + 426 '<div class="cta-reason" id="cta-reason">' + reason + '</div>' + 427 '<a class="btn btn-secondary" id="cta-install-btn" href="' + INSTALL + '" target="_blank" rel="noopener">
427УÑÑановиÑÑ CoinKeeper</a>' + 428 '</div>' + 429 upsellBlock() + 430 crossGrid(); 431 432 document.getElementById('cta-share-btn').addEventListener('click', openPreview); 433 document.getElementById('cta-install-btn').addEventListener('click', function () { track('install_click', { platform: INSTALL_PLATFORM }); }); 434 var platBtn = document.getElementById('cta-plat-btn'); 435 if (platBtn) { platBtn.addEventListener('click', function () { track('platinum_click', {}); }); } 436 var courseBtn = document.getElementById('cta-course-btn'); 437 if (courseBtn) { courseBtn.addEventListener('click', function () { track('course_click', { course: cfg.course }); }); } 438 root.querySelectorAll('.cta-cross-card').forEach(function (a) { 439 a.addEventListener('click', function () { track('cross_calc_click', { to: a.dataset.to }); }); 440 }); 441 } 442 443 // пеÑеÑиÑоваÑÑ ÑолÑко динамиÑеÑкий ÑекÑÑ-пÑиÑÐ¸Ð½Ñ 444 function refresh() { 445 var el = document.getElementById('cta-reason'); 446 if (el && cfg.getInstallReason) { el.innerHTML = cfg.getInstallReason(); } 447 } 448 449 window.CTA = { refresh: refresh, open: openPreview }; 450 render(); 451 /* page_view ÑлÑÑ Ñам gtag (send_page_view:true в analytics.js) â здеÑÑ Ð½Ðµ дÑблиÑÑем */ 452})();
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.