
THINKPAD P15S GEN 2 – WORKSTATION MỎNG NHẸ, ĐỒ HỌA MƯỢT MÀ
CPU: Intel Core i7-1165G7
RAM: 16GB
SSD: 256GB NVMe
VGA: NVIDIA Quadro T500 (chuyên đồ họa, 3D, CAD)
Màn hình: 15.6" Full HD sắc nét

Giá siêu tốt: 11.900.000 VNĐ

Chính sách bảo hành:
Bao test 7 ngày (1 đổi 1 lỗi nhà sản xuất)
Hỗ trợ gói bảo hành mở rộng: +300k/tháng
-
IMG_4732.webp
292,3 KB
· Lượt xem: 1
-
IMG_4733.webp
280,5 KB
· Lượt xem: 1
-
IMG_4736.webp
57,2 KB
· Lượt xem: 1
-
IMG_4735.webp
154,5 KB
· Lượt xem: 1
-
IMG_4734.webp
121,9 KB
· Lượt xem: 1
/* VOZ — in-thread ad spread, client-side.
*
* Lives in the Siropu Ads Manager unit on `thread_view_below_messages` (the ad below the
* last post), pasted after that unit's own
and display() call. This file is the
* source of truth; the ACP copy is a paste of it. Ads Manager's user/device/geo criteria
* on that unit gate the whole thing.
*
* What it does: inserts up to `max` ad slots between posts, spaced by screen height rather
* than post count. Anchors are post 1 and the first unread post (XF marks it `is-unread`;
* it wins over post 1 when the two are closer than a gap) — each only if at least one gap
* above the ad below the last post. Every stretch between anchors, and down to that bottom
* ad, then gets as many slots as fit at the gap, spread evenly, each on the nearest post
* boundary that keeps the gap, up to the cap — stretches below the unread post take their
* share first (the reader scrolls down from there). Slot ids, gap, reserved size and backup
* are per breakpoint (desktop / mobile). Slots are inserted as empty reserved boxes
* (`voz-adSlot`, see extra.less) before PubPower fills them, so nothing shifts when an ad
* arrives; the same markup gives them the backup, the hide-on-load and the fit rules.
*
* Re-runnable: window.vozThreadSpread() removes what it inserted and recomputes.
*/
(function () {
var CONFIG = {
max: 3,
mobileMaxWidth: 650,
label: 'Quảng cáo',
// Per breakpoint: PubPower slot ids, one per placement in page order (kept per
// device so each device's placements report under their own ids); the minimum
// distance between ads (and from the last one to the bottom ad) as viewport heights,
// or a string like '1200px'; the reserved size (voz-adSlot--… in extra.less); and the
// backup shown until an ad has loaded. The backup keeps class voz-adSlot-backup
// (hide-on-load, click-through when unfilled); --raw drops the style's own box so the
// markup inside shows as written.
desktop: {
slots: ['pw_44325', 'pw_44329', 'pw_44331'],
gap: 2,
sizeClass: 'voz-adSlot--leaderboard',
backupHtml: ''
+ 'AD · 970×90
'
+ ''
},
mobile: {
slots: ['pw_44325', 'pw_44329', 'pw_44331'],
gap: 3,
sizeClass: 'voz-adSlot--rectangleLarge',
backupHtml: ''
+ 'AD · 336×280
'
+ ''
}
};
var MARK = 'data-voz-spread';
var script = document.currentScript;
function pageTop(el) { return el.getBoundingClientRect().top + window.pageYOffset; }
function pageBottom(el) { return el.getBoundingClientRect().bottom + window.pageYOffset; }
function breakpoint() {
return window.matchMedia('(max-width: ' + CONFIG.mobileMaxWidth + 'px)').matches ? CONFIG.mobile : CONFIG.desktop;
}
function gapPx() {
var g = breakpoint().gap;
return typeof g === 'string' ? parseFloat(g) : g * window.innerHeight;
}
function bottomAd() {
var el = script;
while (el && !(el.classList && el.classList.contains('voz-adSlot'))) el = el.previousElementSibling;
return el;
}
function unit(slotId) {
var bp = breakpoint();
var wrap = document.createElement('div');
wrap.className = 'voz-adUnit';
wrap.setAttribute(MARK, '');
wrap.innerHTML =
(CONFIG.label ? '' + CONFIG.label + '
' : '')
+ ''
+ bp.backupHtml
+ '
'
+ '
';
return wrap;
}
function run() {
var old = document.querySelectorAll('[' + MARK + ']');
for (var k = 0; k < old.length; k++) old[k].parentNode.removeChild(old[k]);
var posts = Array.prototype.slice.call(document.querySelectorAll('.block--messages article.message--post'));
if (posts.length < 2) return [];
var gap = gapPx();
var bottom = bottomAd();
var bottomY = bottom ? pageTop(bottom) : pageBottom(posts[posts.length - 1]);
var chosen = [];
function fits(y) {
if (bottomY - y < gap) return false;
for (var i = 0; i < chosen.length; i++) if (Math.abs(chosen[i].y - y) < gap) return false;
return true;
}
function take(post) {
var y = pageBottom(post);
if (!fits(y)) return false;
chosen.push({ post: post, y: y });
return true;
}
var unread = -1;
for (var i = 0; i < posts.length; i++) if (posts[i].classList.contains('is-unread')) { unread = i; break; }
// Anchors: the unread post (if a gap above the bottom ad), then post 1 (if a gap
// from both). Every stretch between consecutive anchors and the bottom ad then gets
// as many slots as fit at the gap, spread evenly, each on the nearest post boundary.
if (unread >= 0) take(posts[unread]);
take(posts[0]);
chosen.sort(function (a, b) { return a.y - b.y; });
var uy = unread >= 0 ? pageBottom(posts[unread]) : -1;
var bounds = chosen.map(function (c) { return c.y; }).concat([bottomY]);
var segments = [];
for (var s = 0; s + 1 < bounds.length; s++) segments.push({ a: bounds[s], b: bounds[s + 1] });
// Stretches below the unread post take their share of the cap first (the reader
// scrolls down from there), then the ones above it, top-down.
segments.sort(function (x, y) { return (x.a >= uy ? 0 : 1) - (y.a >= uy ? 0 : 1) || x.a - y.a; });
for (var t = 0; t < segments.length; t++) {
var a = segments[t].a, b = segments[t].b;
var n = Math.min(Math.floor((b - a) / gap) - 1, CONFIG.max - chosen.length);
for (var j = 1; j <= n; j++) {
var target = a + j * (b - a) / (n + 1), best = null, bestD = Infinity;
for (var q = 0; q < posts.length; q++) {
var yq = pageBottom(posts[q]);
if (yq <= a || yq >= b || !fits(yq)) continue;
var d = Math.abs(yq - target);
if (d < bestD) { bestD = d; best = posts[q]; }
}
if (best) take(best);
}
}
chosen.sort(function (a, b) { return a.y - b.y; });
window.powerTag = window.powerTag || {};
var placed = [];
var slots = breakpoint().slots;
for (var p = 0; p < chosen.length; p++) {
var id = slots[p % slots.length];
chosen[p].post.insertAdjacentElement('afterend', unit(id));
placed.push({ afterPost: posts.indexOf(chosen[p].post) + 1, slot: id });
(function (slotId) {
(window.powerTag.Init = window.powerTag.Init || []).push(function () {
window.powerAPITag.display(slotId);
});
})(id);
}
return placed;
}
window.vozThreadSpread = run;
run();
})();