1/* ============================================================================ 2 * shorts/short.js â per-short controller. 3 * 4 * Wraps existing window.Investigation class в context'е /shorts feed slot. 5 * Ðдин Short instance = один <article class="pb-short"> = одно ÑаÑÑледование. 6 * 7 * Lifecycle: 8 * ctor(article, data, options) â mount + ready (ÐÐ start play) 9 * .play() â start Investigation 10 * .pause() â pause Investigation + clear progress-bar 11 * .resume() â resume Investigation 12 * .destroy() â close Investigation, remove DOM contents 13 * (stage заполнÑеÑÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾ когда Embla 14 * re-virtualize'Ð½ÐµÑ slot) 15 * 16 * Hooks: 17 * onActivate(article, short) â feed.js вÑÐ·Ð¾Ð²ÐµÑ ÐºÐ¾Ð³Ð´Ð° article попал в ÑокÑÑ 18 * onDeactivate(article, short) â feed.js вÑÐ·Ð¾Ð²ÐµÑ ÐºÐ¾Ð³Ð´Ð° article ÑÐµÑ Ð°Ð» 19 * onComplete(short) â внÑÑÑи Investigation поÑле summary card 20 * 21 * Progress-bar binding (Phase 1 simple): 22 * - feed.js пеÑедаÑÑ ÑÑÑÐ»ÐºÑ Ð½Ð° progressEl (element Ñ 5 .pb-bar children) 23 * - Short.onSceneChange(idx, name) обновлÑÐµÑ classes is-past/is-current/is-future 24 * - CSS transition на .pb-bar.is-current > i scaleX(0)â1 ÑÐ¸Ð½Ñ Ñонен Ñо scene timing 25 * ========================================================================== */ 26(function (global) { 27 "use strict"; 28 29 // Scene durations (must match investigation.js timings â Scene 1: 1500, 30 // Scene 2: 1800, Scene 3: 1500, Scene 4: 1800, Scene 5: 2400+1800 hold). 31 var SCENE_DURATIONS = { 32 intake: 1500, 33 reviews: 1800, 34 seller: 1500, 35 price: 1800, 36 verdict: 4200, // includes hold + summary 37 }; 38 var SCENE_INDEX = { intake: 0, reviews: 1, seller: 2, price: 3, verdict: 4 }; 39 40 function Short(article, data, options) { 41 this.article = article; 42 this.data = data || {}; 43 this.opts = options || {}; 44 this.progressEl = options && options.progressEl || null; 45 this.inv = null; 46 this.state = "idle"; // idle, ready, playing, paused, complete, destroyed 47 this.completed = false; 48 } 49 50 Short.prototype._mount = function () { 51 if (this.inv) return; 52 if (this.state === "destroyed") return; 53 if (typeof global.Investigation !== "function") { 54 console.warn("[short] Investigation class unavailable"); 55 return; 56 } 57 var stage = this.article.querySelector(".pb-short-stage"); 58 if (!stage) return; 59 60 // v0.25.23: selective cleanup â preview-img оÑÑаÑÑÑÑ Ð² DOM (Ð´Ð»Ñ fade-out 61 // когда Investigation ÑмонÑиÑÑеÑÑÑ). РанÑÑе textContent="" ÑдалÑл вÑÑ 62 // вклÑÑÐ°Ñ preview, ÑÑо ломало pre-show image feature. 63 try { 64 var nodes = stage.childNodes; 65 for (var ci = nodes.length - 1; ci >= 0; ci--) { 66 var n = nodes[ci]; 67 if (n.nodeType !== 1 || !n.classList || 68 !n.classList.contains("pb-short-preview-img")) { 69 stage.removeChild(n); 70 } 71 } 72 } catch (_) {} 73 74 var self = this; 75 // v0.25.3 FIX: бÑло embedded:false â Investigation Ñоздавала <dialog> 76 // ÐÐÐÐРХ feed (полÑзоваÑÐµÐ»Ñ Ð²Ð¸Ð´ÐµÐ» dialog, не feed). ТепеÑÑ embedded:true â 77 // Investigation монÑиÑÑеÑÑÑ INLINE в stage без Ñвоего dialog. 78 // СÑÑеÑÑвÑÑÑие embedded-ÑÑили (phone-frame compact look) пеÑеопÑеделÑем 79 // ÑеÑез CSS в feed.css â внÑÑÑи .pb-short-stage ÑÑÐµÐ½Ñ full-bleed. 80 this.inv = new global.Investigation({ 81 data: this.data, 82 mountPoint: stage, 83 embedded: true, 84 mute: this.opts.mute !== false, 85 loop: false, 86 autoLoop: false, 87 onSceneChange: function (idx, name) { self._onSceneChange(idx, name); }, 88 onComplete: function (info) { self._onComplete(info); }, 89 }); 90 try { stage.classList.add("pb-short-stage-mounted"); } catch (_) {} 91 this.state = "ready"; 92 }; 93 94 Short.prototype.play = function () { 95 if (this.state === "destroyed") return; 96 this._mount(); 97 if (!this.inv) return; 98 try { 99 this.inv.play(); 100 this.state = "playing"; 101 this._setProgressForScene(0, "intake"); 102 // v0.25.23: signal CSS to fade-out preview-img (Investigation гоÑов) 103 try { this.article.classList.add("is-investigation-mounted"); } catch (_) {} 104 } catch (e) { 105 console.warn("[short] play failed", e); 106 } 107 }; 108 109 Short.prototype.pause = function () { 110 if (!this.inv || this.state === "destroyed") return; 111 try { this.inv.pause(); this.state = "paused"; } catch (_) {} 112 }; 113 114 Short.prototype.resume = function () { 115 if (!this.inv || this.state === "destroyed") return; 116 try { this.inv.resume(); this.state = "playing"; } catch (_) {} 117 }; 118 119 Short.prototype.destroy = function () { 120 if (this.state === "destroyed") return; 121 if (this.inv) { 122 try { this.inv.close(); } catch (_) {} 123 this.inv = null; 124 } 125 try { 126 var stage = this.article.querySelector(".pb-short-stage"); 127 if (stage) { 128 // v0.25.23: selective cleanup â preview-img оÑÑаÑÑÑÑ Ð´Ð»Ñ re-mount 129 var nodes = stage.childNodes; 130 for (var di = nodes.length - 1; di >= 0; di--) { 131 var nn = nodes[di]; 132 if (nn.nodeType !== 1 || !nn.classList || 133 !nn.classList.contains("pb-short-preview-img")) { 134 stage.removeChild(nn); 135 } 136 } 137 } 138 this.article.classList.remove("is-investigation-mounted"); 139 } catch (_) {} 140 this.state = "destroyed"; 141 }; 142 143 Short.prototype._onSceneChange = function (idx, name) { 144 this._setProgressForScene(idx, name); 145 if (typeof this.opts.onSceneChange === "function") { 146 try { this.opts.onSceneChange(idx, name); } catch (_) {} 147 } 148 }; 149 150 Short.prototype._onComplete = function (info) { 151 this.completed = true; 152 this.state = "complete"; 153 // ÐаполниÑÑ Ð²Ñе progress-bar segments как is-
153past. 154 if (this.progressEl) { 155 var bars = this.progressEl.querySelectorAll(".pb-bar"); 156 for (var i = 0; i < bars.length; i++) { 157 bars[i].classList.remove("is-current", "is-future"); 158 bars[i].classList.add("is-past"); 159 } 160 } 161 if (typeof this.opts.onComplete === "function") { 162 try { this.opts.onComplete(info, this); } catch (_) {} 163 } 164 }; 165 166 Short.prototype._setProgressForScene = function (idx, name) { 167 if (!this.progressEl) return; 168 var bars = this.progressEl.querySelectorAll(".pb-bar"); 169 for (var i = 0; i < bars.length; i++) { 170 var bar = bars[i]; 171 bar.classList.remove("is-past", "is-current", "is-future"); 172 if (i < idx) bar.classList.add("is-past"); 173 else if (i === idx) bar.classList.add("is-current"); 174 else bar.classList.add("is-future"); 175 // ÐÐ»Ñ current bar â set explicit transition-duration matching scene 176 if (i === idx) { 177 var dur = SCENE_DURATIONS[name] || 1500; 178 var fill = bar.querySelector("i"); 179 if (fill) { 180 fill.style.transition = "none"; 181 fill.style.transform = "scaleX(0)"; 182 // force reflow 183 void fill.offsetWidth; 184 fill.style.transition = "transform " + dur + "ms linear"; 185 fill.style.transform = "scaleX(1)"; 186 } 187 } else { 188 var fill2 = bar.querySelector("i"); 189 if (fill2) { 190 fill2.style.transition = ""; 191 fill2.style.transform = i < idx ? "scaleX(1)" : "scaleX(0)"; 192 } 193 } 194 } 195 }; 196 197 /** 198 * setUserPaused(bool) â toggles the .is-user-paused class on the article 199 * element so feed.css can render the floating pause-indicator overlay. 200 * Does NOT affect Investigation playback (that's done via .pause/.resume). 201 * Safe to call repeatedly with the same value. 202 */ 203 Short.prototype.setUserPaused = function (val) { 204 if (!this.article) return; 205 try { this.article.classList.toggle("is-user-paused", !!val); } catch (_) {} 206 }; 207 208 Short.prototype.resetProgress = function () { 209 if (!this.progressEl) return; 210 var bars = this.progressEl.querySelectorAll(".pb-bar"); 211 for (var i = 0; i < bars.length; i++) { 212 bars[i].classList.remove("is-past", "is-current"); 213 bars[i].classList.add("is-future"); 214 var fill = bars[i].querySelector("i"); 215 if (fill) { fill.style.transition = ""; fill.style.transform = "scaleX(0)"; } 216 } 217 }; 218 219 global.PbShort = Short; 220})(typeof window !== "undefined" ? window : this);
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.